ETH Price: $2,467.97 (-6.34%)

Transaction Decoder

Block:
10339320 at Jun-26-2020 04:40:45 AM +UTC
Transaction Fee:
0.004635802 ETH $11.44
Gas Used:
149,542 Gas / 31 Gwei

Emitted Events:

74 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000009e0a387283ad0534d983997fc52720250ffcaad1, 0x0000000000000000000000006c60c2a9abb2ca13d5a65fd5aecd362007458d40, 000000000000000000000000000000000000000000000000000000038ff37900 )
75 oToken.ERC20CollateralAdded( vaultOwner=[Sender] 0x9e0a387283ad0534d983997fc52720250ffcaad1, amount=15300000000, payer=[Sender] 0x9e0a387283ad0534d983997fc52720250ffcaad1 )
76 oToken.Transfer( from=0x0000000000000000000000000000000000000000, to=[Receiver] oToken, value=695454546 )
77 oToken.IssuedOTokens( issuedTo=[Receiver] oToken, oTokensIssued=695454546, vaultOwner=[Sender] 0x9e0a387283ad0534d983997fc52720250ffcaad1 )
78 oToken.Approval( owner=[Receiver] oToken, spender=OptionsExchange, value=695454546 )
79 oToken.Transfer( from=[Receiver] oToken, to=OptionsExchange, value=695454546 )
80 oToken.Approval( owner=[Receiver] oToken, spender=OptionsExchange, value=0 )
81 oToken.Approval( owner=OptionsExchange, spender=Vyper_contract, value=695454546 )
82 oToken.Transfer( from=OptionsExchange, to=Vyper_contract, value=695454546 )
83 oToken.Approval( owner=OptionsExchange, spender=Vyper_contract, value=0 )
84 Vyper_contract.EthPurchase( buyer=OptionsExchange, tokens_sold=695454546, eth_bought=46439061067894016 )
85 OptionsExchange.SellOTokens( seller=[Receiver] oToken, receiver=[Sender] 0x9e0a387283ad0534d983997fc52720250ffcaad1, oTokenAddress=[Receiver] oToken, payoutTokenAddress=0x0000000000000000000000000000000000000000, oTokensToSell=695454546, payoutTokensReceived=46439061067894016 )

Account State Difference:

  Address   Before After State Difference Code
(firepool)
31.061883968797957632 Eth31.066519770797957632 Eth0.004635802
0x643807F6...0B02F146b 0.668376789558785004 Eth0.621937728490890988 Eth0.046439061067894016
0x6c60c2A9...007458d40
0x9E0a3872...50FFCaad1
0.237939901729239201 Eth
Nonce: 18
0.279743160797133217 Eth
Nonce: 19
0.041803259067894016
0xA0b86991...E3606eB48

Execution Trace

oToken.addAndSellERC20CollateralOption( amtToCreate=695454546, amtCollateral=15300000000, receiver=0x9E0a387283Ad0534d983997fC52720250FFCaad1 )
  • FiatTokenProxy.23b872dd( )
    • FiatTokenV1.transferFrom( _from=0x9E0a387283Ad0534d983997fC52720250FFCaad1, _to=0x6c60c2A9ABB2cA13d5A65Fd5AEcD362007458d40, _value=15300000000 ) => ( True )
    • oToken.approve( spender=0x39246c4F3F6592C974EBC44F80bA6dC69b817c71, amount=695454546 ) => ( True )
    • OptionsExchange.sellOTokens( receiver=0x9E0a387283Ad0534d983997fC52720250FFCaad1, oTokenAddress=0x6c60c2A9ABB2cA13d5A65Fd5AEcD362007458d40, payoutTokenAddress=0x0000000000000000000000000000000000000000, oTokensToSell=695454546 )
      • oToken.transferFrom( sender=0x6c60c2A9ABB2cA13d5A65Fd5AEcD362007458d40, recipient=0x39246c4F3F6592C974EBC44F80bA6dC69b817c71, amount=695454546 ) => ( True )
      • Vyper_contract.getExchange( token=0x6c60c2A9ABB2cA13d5A65Fd5AEcD362007458d40 ) => ( out=0x643807F6142f8A6717BE600A34B7d610B02F146b )
      • oToken.approve( spender=0x643807F6142f8A6717BE600A34B7d610B02F146b, amount=695454546 ) => ( True )
      • Vyper_contract.tokenToEthTransferInput( tokens_sold=695454546, min_eth=1, deadline=1651753129000, recipient=0x9E0a387283Ad0534d983997fC52720250FFCaad1 ) => ( out=46439061067894016 )
        • Vyper_contract.tokenToEthTransferInput( tokens_sold=695454546, min_eth=1, deadline=1651753129000, recipient=0x9E0a387283Ad0534d983997fC52720250FFCaad1 ) => ( out=46439061067894016 )
          • oToken.balanceOf( account=0x643807F6142f8A6717BE600A34B7d610B02F146b ) => ( 9285972249 )
          • ETH 0.046439061067894016 0x9e0a387283ad0534d983997fc52720250ffcaad1.CALL( )
          • oToken.transferFrom( sender=0x39246c4F3F6592C974EBC44F80bA6dC69b817c71, recipient=0x643807F6142f8A6717BE600A34B7d610B02F146b, amount=695454546 ) => ( True )
            addAndSellERC20CollateralOption[oToken (ln:2290)]
            File 1 of 7: oToken
            // File: @openzeppelin/contracts/GSN/Context.sol
            
            pragma solidity ^0.5.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.
             */
            contract Context {
                // Empty internal constructor, to prevent people from mistakenly deploying
                // an instance of this contract, which should be used via inheritance.
                constructor () internal { }
                // solhint-disable-previous-line no-empty-blocks
            
                function _msgSender() internal view returns (address payable) {
                    return msg.sender;
                }
            
                function _msgData() internal view 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/token/ERC20/IERC20.sol
            
            pragma solidity ^0.5.0;
            
            /**
             * @dev Interface of the ERC20 standard as defined in the EIP. Does not include
             * the optional functions; to access them see {ERC20Detailed}.
             */
            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.5.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.
                 *
                 * _Available since v2.4.0._
                 */
                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.
                 *
                 * _Available since v2.4.0._
                 */
                function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                    // Solidity only automatically asserts when dividing by 0
                    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.
                 *
                 * _Available since v2.4.0._
                 */
                function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                    require(b != 0, errorMessage);
                    return a % b;
                }
            }
            
            // File: @openzeppelin/contracts/token/ERC20/ERC20.sol
            
            pragma solidity ^0.5.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 {ERC20Mintable}.
             *
             * TIP: For a detailed writeup see our guide
             * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
             * to implement supply mechanisms].
             *
             * We have followed general OpenZeppelin guidelines: functions revert instead
             * of returning `false` on failure. This behavior is nonetheless conventional
             * and does not conflict with the expectations of ERC20 applications.
             *
             * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
             * This allows applications to reconstruct the allowance for all accounts just
             * by listening to said events. Other implementations of the EIP may not emit
             * these events, as it isn't required by the specification.
             *
             * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
             * functions have been added to mitigate the well-known issues around setting
             * allowances. See {IERC20-approve}.
             */
            contract ERC20 is Context, IERC20 {
                using SafeMath for uint256;
            
                mapping (address => uint256) private _balances;
            
                mapping (address => mapping (address => uint256)) private _allowances;
            
                uint256 private _totalSupply;
            
                /**
                 * @dev See {IERC20-totalSupply}.
                 */
                function totalSupply() public view returns (uint256) {
                    return _totalSupply;
                }
            
                /**
                 * @dev See {IERC20-balanceOf}.
                 */
                function balanceOf(address account) public view 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 returns (bool) {
                    _transfer(_msgSender(), recipient, amount);
                    return true;
                }
            
                /**
                 * @dev See {IERC20-allowance}.
                 */
                function allowance(address owner, address spender) public view returns (uint256) {
                    return _allowances[owner][spender];
                }
            
                /**
                 * @dev See {IERC20-approve}.
                 *
                 * Requirements:
                 *
                 * - `spender` cannot be the zero address.
                 */
                function approve(address spender, uint256 amount) public 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 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 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 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 {
                    require(sender != address(0), "ERC20: transfer from the zero address");
                    require(recipient != address(0), "ERC20: transfer to the zero address");
            
                    _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 {
                    require(account != address(0), "ERC20: mint to the zero address");
            
                    _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 {
                    require(account != address(0), "ERC20: burn from the zero address");
            
                    _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 is 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 {
                    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 Destroys `amount` tokens from `account`.`amount` is then deducted
                 * from the caller's allowance.
                 *
                 * See {_burn} and {_approve}.
                 */
                function _burnFrom(address account, uint256 amount) internal {
                    _burn(account, amount);
                    _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance"));
                }
            }
            
            // File: contracts/lib/CompoundOracleInterface.sol
            
            pragma solidity ^0.5.0;
            // AT MAINNET ADDRESS: 0x02557a5E05DeFeFFD4cAe6D83eA3d173B272c904
            
            contract CompoundOracleInterface {
                // returns asset:eth -- to get USDC:eth, have to do 10**24/result,
            
            
                constructor() public {
                }
            
                /**
              * @notice retrieves price of an asset
              * @dev function to get price for an asset
              * @param asset Asset for which to get the price
              * @return uint mantissa of asset price (scaled by 1e18) or zero if unset or contract paused
              */
                function getPrice(address asset) public view returns (uint);
                function getUnderlyingPrice(ERC20 cToken) public view returns (uint);
                // function getPrice(address asset) public view returns (uint) {
                //     return 527557000000000;
                // }
            
            }
            
            // File: contracts/lib/UniswapExchangeInterface.sol
            
            pragma solidity 0.5.10;
            
            
            // Solidity Interface
            contract UniswapExchangeInterface {
                // Address of ERC20 token sold on this exchange
                function tokenAddress() external view returns (address token);
                // Address of Uniswap Factory
                function factoryAddress() external view returns (address factory);
                // Provide Liquidity
                function addLiquidity(uint256 min_liquidity, uint256 max_tokens, uint256 deadline) external payable returns (uint256);
                function removeLiquidity(uint256 amount, uint256 min_eth, uint256 min_tokens, uint256 deadline) external returns (uint256, uint256);
                // Get Prices
                function getEthToTokenInputPrice(uint256 eth_sold) external view returns (uint256 tokens_bought);
                function getEthToTokenOutputPrice(uint256 tokens_bought) external view returns (uint256 eth_sold);
                function getTokenToEthInputPrice(uint256 tokens_sold) external view returns (uint256 eth_bought);
                function getTokenToEthOutputPrice(uint256 eth_bought) external view returns (uint256 tokens_sold);
                // Trade ETH to ERC20
                function ethToTokenSwapInput(uint256 min_tokens, uint256 deadline) external payable returns (uint256  tokens_bought);
                function ethToTokenTransferInput(uint256 min_tokens, uint256 deadline, address recipient) external payable returns (uint256  tokens_bought);
                function ethToTokenSwapOutput(uint256 tokens_bought, uint256 deadline) external payable returns (uint256  eth_sold);
                function ethToTokenTransferOutput(uint256 tokens_bought, uint256 deadline, address recipient) external payable returns (uint256  eth_sold);
                // Trade ERC20 to ETH
                function tokenToEthSwapInput(uint256 tokens_sold, uint256 min_eth, uint256 deadline) external returns (uint256  eth_bought);
                function tokenToEthTransferInput(uint256 tokens_sold, uint256 min_eth, uint256 deadline, address recipient) external returns (uint256  eth_bought);
                function tokenToEthSwapOutput(uint256 eth_bought, uint256 max_tokens, uint256 deadline) external returns (uint256  tokens_sold);
                function tokenToEthTransferOutput(uint256 eth_bought, uint256 max_tokens, uint256 deadline, address recipient) external returns (uint256  tokens_sold);
                // Trade ERC20 to ERC20
                function tokenToTokenSwapInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address token_addr) external returns (uint256  tokens_bought);
                function tokenToTokenTransferInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address recipient, address token_addr) external returns (uint256  tokens_bought);
                function tokenToTokenSwapOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address token_addr) external returns (uint256  tokens_sold);
                function tokenToTokenTransferOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address recipient, address token_addr) external returns (uint256  tokens_sold);
                // Trade ERC20 to Custom Pool
                function tokenToExchangeSwapInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address exchange_addr) external returns (uint256  tokens_bought);
                function tokenToExchangeTransferInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address recipient, address exchange_addr) external returns (uint256  tokens_bought);
                function tokenToExchangeSwapOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address exchange_addr) external returns (uint256  tokens_sold);
                function tokenToExchangeTransferOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address recipient, address exchange_addr) external returns (uint256  tokens_sold);
                // ERC20 comaptibility for liquidity tokens
                bytes32 public name;
                bytes32 public symbol;
                uint256 public decimals;
                function transfer(address _to, uint256 _value) external returns (bool);
                function transferFrom(address _from, address _to, uint256 value) external returns (bool);
                function approve(address _spender, uint256 _value) external returns (bool);
                function allowance(address _owner, address _spender) external view returns (uint256);
                function balanceOf(address _owner) external view returns (uint256);
                function totalSupply() external view returns (uint256);
                // Never use
                function setup(address token_addr) external;
            }
            
            // File: contracts/lib/UniswapFactoryInterface.sol
            
            pragma solidity 0.5.10;
            
            
            // Solidity Interface
            contract UniswapFactoryInterface {
                // Public Variables
                address public exchangeTemplate;
                uint256 public tokenCount;
                // // Create Exchange
                function createExchange(address token) external returns (address exchange);
                // Get Exchange and Token Info
                function getExchange(address token) external view returns (address exchange);
                function getToken(address exchange) external view returns (address token);
                function getTokenWithId(uint256 tokenId) external view returns (address token);
                // Never use
                function initializeFactory(address template) external;
                // function createExchange(address token) external returns (address exchange) {
                //     return 0x06D014475F84Bb45b9cdeD1Cf3A1b8FE3FbAf128;
                // }
                // // Get Exchange and Token Info
                // function getExchange(address token) external view returns (address exchange){
                //     return 0x06D014475F84Bb45b9cdeD1Cf3A1b8FE3FbAf128;
                // }
                // function getToken(address exchange) external view returns (address token) {
                //     return 0x06D014475F84Bb45b9cdeD1Cf3A1b8FE3FbAf128;
                // }
                // function getTokenWithId(uint256 tokenId) external view returns (address token) {
                //     return 0x06D014475F84Bb45b9cdeD1Cf3A1b8FE3FbAf128;
                // }
            }
            
            // File: contracts/OptionsUtils.sol
            
            pragma solidity 0.5.10;
            
            
            
            
            
            contract OptionsUtils {
                // defauls are for mainnet
                UniswapFactoryInterface public UNISWAP_FACTORY;
            
                CompoundOracleInterface public COMPOUND_ORACLE;
            
                constructor(address _uniswapFactory, address _compoundOracle) public {
                    UNISWAP_FACTORY = UniswapFactoryInterface(_uniswapFactory);
                    COMPOUND_ORACLE = CompoundOracleInterface(_compoundOracle);
                }
            
                // TODO: for now gets Uniswap, later update to get other exchanges
                function getExchange(address _token)
                    public
                    view
                    returns (UniswapExchangeInterface)
                {
                    if (address(UNISWAP_FACTORY.getExchange(_token)) == address(0)) {
                        revert("No payout exchange");
                    }
            
                    UniswapExchangeInterface exchange = UniswapExchangeInterface(
                        UNISWAP_FACTORY.getExchange(_token)
                    );
            
                    return exchange;
                }
            
                function isETH(IERC20 _ierc20) public pure returns (bool) {
                    return _ierc20 == IERC20(0);
                }
            }
            
            // File: contracts/OptionsExchange.sol
            
            pragma solidity 0.5.10;
            
            
            
            
            
            
            contract OptionsExchange {
                uint256 constant LARGE_BLOCK_SIZE = 1651753129000;
                uint256 constant LARGE_APPROVAL_NUMBER = 10**30;
            
                UniswapFactoryInterface public UNISWAP_FACTORY;
            
                constructor(address _uniswapFactory) public {
                    UNISWAP_FACTORY = UniswapFactoryInterface(_uniswapFactory);
                }
            
                /*** Events ***/
                event SellOTokens(
                    address seller,
                    address payable receiver,
                    address oTokenAddress,
                    address payoutTokenAddress,
                    uint256 oTokensToSell,
                    uint256 payoutTokensReceived
                );
                event BuyOTokens(
                    address buyer,
                    address payable receiver,
                    address oTokenAddress,
                    address paymentTokenAddress,
                    uint256 oTokensToBuy,
                    uint256 premiumPaid
                );
            
                /**
                * @notice This function sells oTokens on Uniswap and sends back payoutTokens to the receiver
                * @param receiver The address to send the payout tokens back to
                * @param oTokenAddress The address of the oToken to sell
                * @param payoutTokenAddress The address of the token to receive the premiums in
                * @param oTokensToSell The number of oTokens to sell
                */
                function sellOTokens(
                    address payable receiver,
                    address oTokenAddress,
                    address payoutTokenAddress,
                    uint256 oTokensToSell
                ) public {
                    // @note: first need to bootstrap the uniswap exchange to get the address.
                    IERC20 oToken = IERC20(oTokenAddress);
                    IERC20 payoutToken = IERC20(payoutTokenAddress);
                    oToken.transferFrom(msg.sender, address(this), oTokensToSell);
                    uint256 payoutTokensReceived = uniswapSellOToken(
                        oToken,
                        payoutToken,
                        oTokensToSell,
                        receiver
                    );
            
                    emit SellOTokens(
                        msg.sender,
                        receiver,
                        oTokenAddress,
                        payoutTokenAddress,
                        oTokensToSell,
                        payoutTokensReceived
                    );
                }
            
                /**
                * @notice This function buys oTokens on Uniswap and using paymentTokens from the receiver
                * @param receiver The address to send the oTokens back to
                * @param oTokenAddress The address of the oToken to buy
                * @param paymentTokenAddress The address of the token to pay the premiums in
                * @param oTokensToBuy The number of oTokens to buy
                */
                function buyOTokens(
                    address payable receiver,
                    address oTokenAddress,
                    address paymentTokenAddress,
                    uint256 oTokensToBuy
                ) public payable {
                    IERC20 oToken = IERC20(oTokenAddress);
                    IERC20 paymentToken = IERC20(paymentTokenAddress);
                    uniswapBuyOToken(paymentToken, oToken, oTokensToBuy, receiver);
                }
            
                /**
                * @notice This function calculates the amount of premiums that the seller
                * will receive if they sold oTokens on Uniswap
                * @param oTokenAddress The address of the oToken to sell
                * @param payoutTokenAddress The address of the token to receive the premiums in
                * @param oTokensToSell The number of oTokens to sell
                */
                function premiumReceived(
                    address oTokenAddress,
                    address payoutTokenAddress,
                    uint256 oTokensToSell
                ) public view returns (uint256) {
                    // get the amount of ETH that will be paid out if oTokensToSell is sold.
                    UniswapExchangeInterface oTokenExchange = getExchange(oTokenAddress);
                    uint256 ethReceived = oTokenExchange.getTokenToEthInputPrice(
                        oTokensToSell
                    );
            
                    if (!isETH(IERC20(payoutTokenAddress))) {
                        // get the amount of payout tokens that will be received if the ethRecieved is sold.
                        UniswapExchangeInterface payoutExchange = getExchange(
                            payoutTokenAddress
                        );
                        return payoutExchange.getEthToTokenInputPrice(ethReceived);
                    }
                    return ethReceived;
            
                }
            
                /**
                * @notice This function calculates the premiums to be paid if a buyer wants to
                * buy oTokens on Uniswap
                * @param oTokenAddress The address of the oToken to buy
                * @param paymentTokenAddress The address of the token to pay the premiums in
                * @param oTokensToBuy The number of oTokens to buy
                */
                function premiumToPay(
                    address oTokenAddress,
                    address paymentTokenAddress,
                    uint256 oTokensToBuy
                ) public view returns (uint256) {
                    // get the amount of ETH that needs to be paid for oTokensToBuy.
                    UniswapExchangeInterface oTokenExchange = getExchange(oTokenAddress);
                    uint256 ethToPay = oTokenExchange.getEthToTokenOutputPrice(
                        oTokensToBuy
                    );
            
                    if (!isETH(IERC20(paymentTokenAddress))) {
                        // get the amount of paymentTokens that needs to be paid to get the desired ethToPay.
                        UniswapExchangeInterface paymentTokenExchange = getExchange(
                            paymentTokenAddress
                        );
                        return paymentTokenExchange.getTokenToEthOutputPrice(ethToPay);
                    }
            
                    return ethToPay;
                }
            
                function uniswapSellOToken(
                    IERC20 oToken,
                    IERC20 payoutToken,
                    uint256 _amt,
                    address payable _transferTo
                ) internal returns (uint256) {
                    require(!isETH(oToken), "Can only sell oTokens");
                    UniswapExchangeInterface exchange = getExchange(address(oToken));
            
                    if (isETH(payoutToken)) {
                        //Token to ETH
                        oToken.approve(address(exchange), _amt);
                        return
                            exchange.tokenToEthTransferInput(
                                _amt,
                                1,
                                LARGE_BLOCK_SIZE,
                                _transferTo
                            );
                    } else {
                        //Token to Token
                        oToken.approve(address(exchange), _amt);
                        return
                            exchange.tokenToTokenTransferInput(
                                _amt,
                                1,
                                1,
                                LARGE_BLOCK_SIZE,
                                _transferTo,
                                address(payoutToken)
                            );
                    }
                }
            
                function uniswapBuyOToken(
                    IERC20 paymentToken,
                    IERC20 oToken,
                    uint256 _amt,
                    address payable _transferTo
                ) public returns (uint256) {
                    require(!isETH(oToken), "Can only buy oTokens");
            
                    if (!isETH(paymentToken)) {
                        UniswapExchangeInterface exchange = getExchange(
                            address(paymentToken)
                        );
            
                        uint256 paymentTokensToTransfer = premiumToPay(
                            address(oToken),
                            address(paymentToken),
                            _amt
                        );
                        paymentToken.transferFrom(
                            msg.sender,
                            address(this),
                            paymentTokensToTransfer
                        );
            
                        // Token to Token
                        paymentToken.approve(address(exchange), LARGE_APPROVAL_NUMBER);
            
                        emit BuyOTokens(
                            msg.sender,
                            _transferTo,
                            address(oToken),
                            address(paymentToken),
                            _amt,
                            paymentTokensToTransfer
                        );
            
                        return
                            exchange.tokenToTokenTransferInput(
                                paymentTokensToTransfer,
                                1,
                                1,
                                LARGE_BLOCK_SIZE,
                                _transferTo,
                                address(oToken)
                            );
                    } else {
                        // ETH to Token
                        UniswapExchangeInterface exchange = UniswapExchangeInterface(
                            UNISWAP_FACTORY.getExchange(address(oToken))
                        );
            
                        uint256 ethToTransfer = exchange.getEthToTokenOutputPrice(_amt);
            
                        emit BuyOTokens(
                            msg.sender,
                            _transferTo,
                            address(oToken),
                            address(paymentToken),
                            _amt,
                            ethToTransfer
                        );
            
                        return
                            exchange.ethToTokenTransferOutput.value(ethToTransfer)(
                                _amt,
                                LARGE_BLOCK_SIZE,
                                _transferTo
                            );
                    }
                }
            
                function getExchange(address _token)
                    internal
                    view
                    returns (UniswapExchangeInterface)
                {
                    UniswapExchangeInterface exchange = UniswapExchangeInterface(
                        UNISWAP_FACTORY.getExchange(_token)
                    );
            
                    if (address(exchange) == address(0)) {
                        revert("No payout exchange");
                    }
            
                    return exchange;
                }
            
                function isETH(IERC20 _ierc20) internal pure returns (bool) {
                    return _ierc20 == IERC20(0);
                }
            
                function() external payable {
                    // to get ether from uniswap exchanges
                }
            
            }
            
            // File: @openzeppelin/contracts/token/ERC20/ERC20Detailed.sol
            
            pragma solidity ^0.5.0;
            
            
            /**
             * @dev Optional functions from the ERC20 standard.
             */
            contract ERC20Detailed is IERC20 {
                string private _name;
                string private _symbol;
                uint8 private _decimals;
            
                /**
                 * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of
                 * these values are immutable: they can only be set once during
                 * construction.
                 */
                constructor (string memory name, string memory symbol, uint8 decimals) public {
                    _name = name;
                    _symbol = symbol;
                    _decimals = decimals;
                }
            
                /**
                 * @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.
                 *
                 * 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;
                }
            }
            
            // File: @openzeppelin/contracts/ownership/Ownable.sol
            
            pragma solidity ^0.5.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.
             *
             * 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 {
                    _owner = _msgSender();
                    emit OwnershipTransferred(address(0), _owner);
                }
            
                /**
                 * @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(isOwner(), "Ownable: caller is not the owner");
                    _;
                }
            
                /**
                 * @dev Returns true if the caller is the current owner.
                 */
                function isOwner() public view returns (bool) {
                    return _msgSender() == _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 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 onlyOwner {
                    _transferOwnership(newOwner);
                }
            
                /**
                 * @dev Transfers ownership of the contract to a new account (`newOwner`).
                 */
                function _transferOwnership(address newOwner) internal {
                    require(newOwner != address(0), "Ownable: new owner is the zero address");
                    emit OwnershipTransferred(_owner, newOwner);
                    _owner = newOwner;
                }
            }
            
            // File: contracts/OptionsContract.sol
            
            pragma solidity 0.5.10;
            
            
            
            
            
            
            
            
            
            
            
            /**
             * @title Opyn's Options Contract
             * @author Opyn
             */
            contract OptionsContract is Ownable, ERC20 {
                using SafeMath for uint256;
            
                /* represents floting point numbers, where number = value * 10 ** exponent
                i.e 0.1 = 10 * 10 ** -3 */
                struct Number {
                    uint256 value;
                    int32 exponent;
                }
            
                // Keeps track of the weighted collateral and weighted debt for each vault.
                struct Vault {
                    uint256 collateral;
                    uint256 oTokensIssued;
                    uint256 underlying;
                    bool owned;
                }
            
                OptionsExchange public optionsExchange;
            
                mapping(address => Vault) internal vaults;
            
                address payable[] internal vaultOwners;
            
                // 10 is 0.01 i.e. 1% incentive.
                Number public liquidationIncentive = Number(10, -3);
            
                // 100 is egs. 0.1 i.e. 10%.
                Number public transactionFee = Number(0, -3);
            
                /* 500 is 0.5. Max amount that a Vault can be liquidated by i.e.
                max collateral that can be taken in one function call */
                Number public liquidationFactor = Number(500, -3);
            
                /* 16 means 1.6. The minimum ratio of a Vault's collateral to insurance promised.
                The ratio is calculated as below:
                vault.collateral / (Vault.oTokensIssued * strikePrice) */
                Number public minCollateralizationRatio = Number(16, -1);
            
                // The amount of insurance promised per oToken
                Number public strikePrice;
            
                // The amount of underlying that 1 oToken protects.
                Number public oTokenExchangeRate;
            
                /* UNIX time.
                Exercise period starts at `(expiry - windowSize)` and ends at `expiry` */
                uint256 internal windowSize;
            
                /* The total fees accumulated in the contract any time liquidate or exercise is called */
                uint256 internal totalFee;
            
                // The time of expiry of the options contract
                uint256 public expiry;
            
                // The precision of the collateral
                int32 public collateralExp = -18;
            
                // The precision of the underlying
                int32 public underlyingExp = -18;
            
                // The collateral asset
                IERC20 public collateral;
            
                // The asset being protected by the insurance
                IERC20 public underlying;
            
                // The asset in which insurance is denominated in.
                IERC20 public strike;
            
                // The Oracle used for the contract
                CompoundOracleInterface public COMPOUND_ORACLE;
            
                // The name of  the contract
                string public name;
            
                // The symbol of  the contract
                string public symbol;
            
                // The number of decimals of the contract
                uint8 public decimals;
            
                /**
                * @param _collateral The collateral asset
                * @param _collExp The precision of the collateral (-18 if ETH)
                * @param _underlying The asset that is being protected
                * @param _underlyingExp The precision of the underlying asset
                * @param _oTokenExchangeExp The precision of the `amount of underlying` that 1 oToken protects
                * @param _strikePrice The amount of strike asset that will be paid out per oToken
                * @param _strikeExp The precision of the strike price.
                * @param _strike The asset in which the insurance is calculated
                * @param _expiry The time at which the insurance expires
                * @param _optionsExchange The contract which interfaces with the exchange + oracle
                * @param _oracleAddress The address of the oracle
                * @param _windowSize UNIX time. Exercise window is from `expiry - _windowSize` to `expiry`.
                */
                constructor(
                    IERC20 _collateral,
                    int32 _collExp,
                    IERC20 _underlying,
                    int32 _underlyingExp,
                    int32 _oTokenExchangeExp,
                    uint256 _strikePrice,
                    int32 _strikeExp,
                    IERC20 _strike,
                    uint256 _expiry,
                    OptionsExchange _optionsExchange,
                    address _oracleAddress,
                    uint256 _windowSize
                ) public {
                    require(block.timestamp < _expiry, "Can't deploy an expired contract");
                    require(
                        _windowSize <= _expiry,
                        "Exercise window can't be longer than the contract's lifespan"
                    );
                    require(
                        isWithinExponentRange(_collExp),
                        "collateral exponent not within expected range"
                    );
                    require(
                        isWithinExponentRange(_underlyingExp),
                        "underlying exponent not within expected range"
                    );
                    require(
                        isWithinExponentRange(_strikeExp),
                        "strike price exponent not within expected range"
                    );
                    require(
                        isWithinExponentRange(_oTokenExchangeExp),
                        "oToken exchange rate exponent not within expected range"
                    );
            
                    collateral = _collateral;
                    collateralExp = _collExp;
            
                    underlying = _underlying;
                    underlyingExp = _underlyingExp;
                    oTokenExchangeRate = Number(1, _oTokenExchangeExp);
            
                    strikePrice = Number(_strikePrice, _strikeExp);
                    strike = _strike;
            
                    expiry = _expiry;
                    COMPOUND_ORACLE = CompoundOracleInterface(_oracleAddress);
                    optionsExchange = _optionsExchange;
                    windowSize = _windowSize;
                }
            
                /*** Events ***/
                event VaultOpened(address payable vaultOwner);
                event ETHCollateralAdded(
                    address payable vaultOwner,
                    uint256 amount,
                    address payer
                );
                event ERC20CollateralAdded(
                    address payable vaultOwner,
                    uint256 amount,
                    address payer
                );
                event IssuedOTokens(
                    address issuedTo,
                    uint256 oTokensIssued,
                    address payable vaultOwner
                );
                event Liquidate(
                    uint256 amtCollateralToPay,
                    address payable vaultOwner,
                    address payable liquidator
                );
                event Exercise(
                    uint256 amtUnderlyingToPay,
                    uint256 amtCollateralToPay,
                    address payable exerciser,
                    address payable vaultExercisedFrom
                );
                event RedeemVaultBalance(
                    uint256 amtCollateralRedeemed,
                    uint256 amtUnderlyingRedeemed,
                    address payable vaultOwner
                );
                event BurnOTokens(address payable vaultOwner, uint256 oTokensBurned);
                event RemoveCollateral(uint256 amtRemoved, address payable vaultOwner);
                event UpdateParameters(
                    uint256 liquidationIncentive,
                    uint256 liquidationFactor,
                    uint256 transactionFee,
                    uint256 minCollateralizationRatio,
                    address owner
                );
                event TransferFee(address payable to, uint256 fees);
                event RemoveUnderlying(
                    uint256 amountUnderlying,
                    address payable vaultOwner
                );
            
                /**
                 * @dev Throws if called Options contract is expired.
                 */
                modifier notExpired() {
                    require(!hasExpired(), "Options contract expired");
                    _;
                }
            
                /**
                 * @notice This function gets the array of vaultOwners
                 */
                function getVaultOwners() public view returns (address payable[] memory) {
                    address payable[] memory owners;
                    uint256 index = 0;
                    for (uint256 i = 0; i < vaultOwners.length; i++) {
                        if (hasVault(vaultOwners[i])) {
                            owners[index] = vaultOwners[i];
                            index++;
                        }
                    }
            
                    return owners;
                }
            
                /**
                 * @notice Can only be called by owner. Used to update the fees, minminCollateralizationRatio, etc
                 * @param _liquidationIncentive The incentive paid to liquidator. 10 is 0.01 i.e. 1% incentive.
                 * @param _liquidationFactor Max amount that a Vault can be liquidated by. 500 is 0.5.
                 * @param _transactionFee The fees paid to our protocol every time a execution happens. 100 is egs. 0.1 i.e. 10%.
                 * @param _minCollateralizationRatio The minimum ratio of a Vault's collateral to insurance promised. 16 means 1.6.
                 */
                function updateParameters(
                    uint256 _liquidationIncentive,
                    uint256 _liquidationFactor,
                    uint256 _transactionFee,
                    uint256 _minCollateralizationRatio
                ) public onlyOwner {
                    require(
                        _liquidationIncentive <= 200,
                        "Can't have >20% liquidation incentive"
                    );
                    require(
                        _liquidationFactor <= 1000,
                        "Can't liquidate more than 100% of the vault"
                    );
                    require(_transactionFee <= 100, "Can't have transaction fee > 10%");
                    require(
                        _minCollateralizationRatio >= 10,
                        "Can't have minCollateralizationRatio < 1"
                    );
            
                    liquidationIncentive.value = _liquidationIncentive;
                    liquidationFactor.value = _liquidationFactor;
                    transactionFee.value = _transactionFee;
                    minCollateralizationRatio.value = _minCollateralizationRatio;
            
                    emit UpdateParameters(
                        _liquidationIncentive,
                        _liquidationFactor,
                        _transactionFee,
                        _minCollateralizationRatio,
                        owner()
                    );
                }
            
                /**
                 * @notice Can only be called by owner. Used to set the name, symbol and decimals of the contract
                 * @param _name The name of the contract
                 * @param _symbol The symbol of the contract
                 */
                function setDetails(string memory _name, string memory _symbol)
                    public
                    onlyOwner
                {
                    name = _name;
                    symbol = _symbol;
                    decimals = uint8(-1 * oTokenExchangeRate.exponent);
                    require(
                        decimals >= 0,
                        "1 oToken cannot protect less than the smallest unit of the asset"
                    );
                }
            
                /**
                 * @notice Can only be called by owner. Used to take out the protocol fees from the contract.
                 * @param _address The address to send the fee to.
                 */
                function transferFee(address payable _address) public onlyOwner {
                    uint256 fees = totalFee;
                    totalFee = 0;
                    transferCollateral(_address, fees);
            
                    emit TransferFee(_address, fees);
                }
            
                /**
                 * @notice Checks if a `owner` has already created a Vault
                 * @param owner The address of the supposed owner
                 * @return true or false
                 */
                function hasVault(address payable owner) public view returns (bool) {
                    return vaults[owner].owned;
                }
            
                /**
                 * @notice Creates a new empty Vault and sets the owner of the vault to be the msg.sender.
                 */
                function openVault() public notExpired returns (bool) {
                    require(!hasVault(msg.sender), "Vault already created");
            
                    vaults[msg.sender] = Vault(0, 0, 0, true);
                    vaultOwners.push(msg.sender);
            
                    emit VaultOpened(msg.sender);
                    return true;
                }
            
                /**
                 * @notice If the collateral type is ETH, anyone can call this function any time before
                 * expiry to increase the amount of collateral in a Vault. Will fail if ETH is not the
                 * collateral asset.
                 * Remember that adding ETH collateral even if no oTokens have been created can put the owner at a
                 * risk of losing the collateral if an exercise event happens.
                 * Ensure that you issue and immediately sell oTokens to allow the owner to earn premiums.
                 * (Either call the createAndSell function in the oToken contract or batch the
                 * addERC20Collateral, issueOTokens and sell transactions and ensure they happen atomically to protect
                 * the end user).
                 * @param vaultOwner the index of the Vault to which collateral will be added.
                 */
                function addETHCollateral(address payable vaultOwner)
                    public
                    payable
                    notExpired
                    returns (uint256)
                {
                    require(isETH(collateral), "ETH is not the specified collateral type");
                    require(hasVault(vaultOwner), "Vault does not exist");
            
                    emit ETHCollateralAdded(vaultOwner, msg.value, msg.sender);
                    return _addCollateral(vaultOwner, msg.value);
                }
            
                /**
                 * @notice If the collateral type is any ERC20, anyone can call this function any time before
                 * expiry to increase the amount of collateral in a Vault. Can only transfer in the collateral asset.
                 * Will fail if ETH is the collateral asset.
                 * The user has to allow the contract to handle their ERC20 tokens on his behalf before these
                 * functions are called.
                 * Remember that adding ERC20 collateral even if no oTokens have been created can put the owner at a
                 * risk of losing the collateral. Ensure that you issue and immediately sell the oTokens!
                 * (Either call the createAndSell function in the oToken contract or batch the
                 * addERC20Collateral, issueOTokens and sell transactions and ensure they happen atomically to protect
                 * the end user).
                 * @param vaultOwner the index of the Vault to which collateral will be added.
                 * @param amt the amount of collateral to be transferred in.
                 */
                function addERC20Collateral(address payable vaultOwner, uint256 amt)
                    public
                    notExpired
                    returns (uint256)
                {
                    require(
                        collateral.transferFrom(msg.sender, address(this), amt),
                        "Could not transfer in collateral tokens"
                    );
                    require(hasVault(vaultOwner), "Vault does not exist");
            
                    emit ERC20CollateralAdded(vaultOwner, amt, msg.sender);
                    return _addCollateral(vaultOwner, amt);
                }
            
                /**
                 * @notice Returns the amount of underlying to be transferred during an exercise call
                 */
                function underlyingRequiredToExercise(uint256 oTokensToExercise)
                    public
                    view
                    returns (uint256)
                {
                    uint64 underlyingPerOTokenExp = uint64(
                        oTokenExchangeRate.exponent - underlyingExp
                    );
                    return oTokensToExercise.mul(10**underlyingPerOTokenExp);
                }
            
                /**
                 * @notice Returns true if exercise can be called
                 */
                function isExerciseWindow() public view returns (bool) {
                    return ((block.timestamp >= expiry.sub(windowSize)) &&
                        (block.timestamp < expiry));
                }
            
                /**
                 * @notice Returns true if the oToken contract has expired
                 */
                function hasExpired() public view returns (bool) {
                    return (block.timestamp >= expiry);
                }
            
                /**
                 * @notice Called by anyone holding the oTokens and underlying during the
                 * exercise window i.e. from `expiry - windowSize` time to `expiry` time. The caller
                 * transfers in their oTokens and corresponding amount of underlying and gets
                 * `strikePrice * oTokens` amount of collateral out. The collateral paid out is taken from
                 * the each vault owner starting with the first and iterating until the oTokens to exercise
                 * are found.
                 * NOTE: This uses a for loop and hence could run out of gas if the array passed in is too big!
                 * @param oTokensToExercise the number of oTokens being exercised.
                 * @param vaultsToExerciseFrom the array of vaults to exercise from.
                 */
                function exercise(
                    uint256 oTokensToExercise,
                    address payable[] memory vaultsToExerciseFrom
                ) public payable {
                    for (uint256 i = 0; i < vaultsToExerciseFrom.length; i++) {
                        address payable vaultOwner = vaultsToExerciseFrom[i];
                        require(
                            hasVault(vaultOwner),
                            "Cannot exercise from a vault that doesn't exist"
                        );
                        Vault storage vault = vaults[vaultOwner];
                        if (oTokensToExercise == 0) {
                            return;
                        } else if (vault.oTokensIssued >= oTokensToExercise) {
                            _exercise(oTokensToExercise, vaultOwner);
                            return;
                        } else {
                            oTokensToExercise = oTokensToExercise.sub(vault.oTokensIssued);
                            _exercise(vault.oTokensIssued, vaultOwner);
                        }
                    }
                    require(
                        oTokensToExercise == 0,
                        "Specified vaults have insufficient collateral"
                    );
                }
            
                /**
                 * @notice This function allows the vault owner to remove their share of underlying after an exercise
                 */
                function removeUnderlying() public {
                    require(hasVault(msg.sender), "Vault does not exist");
                    Vault storage vault = vaults[msg.sender];
            
                    require(vault.underlying > 0, "No underlying balance");
            
                    uint256 underlyingToTransfer = vault.underlying;
                    vault.underlying = 0;
            
                    transferUnderlying(msg.sender, underlyingToTransfer);
                    emit RemoveUnderlying(underlyingToTransfer, msg.sender);
            
                }
            
                /**
                 * @notice This function is called to issue the option tokens. Remember that issuing oTokens even if they
                 * haven't been sold can put the owner at a risk of not making premiums on the oTokens. Ensure that you
                 * issue and immidiately sell the oTokens! (Either call the createAndSell function in the oToken contract
                 * of batch the issueOTokens transaction with a sell transaction and ensure it happens atomically).
                 * @dev The owner of a Vault should only be able to have a max of
                 * repo.collateral * collateralToStrike / (minminCollateralizationRatio * strikePrice) tokens issued.
                 * @param oTokensToIssue The number of o tokens to issue
                 * @param receiver The address to send the oTokens to
                 */
                function issueOTokens(uint256 oTokensToIssue, address receiver)
                    public
                    notExpired
                {
                    //check that we're properly collateralized to mint this number, then call _mint(address account, uint256 amount)
                    require(hasVault(msg.sender), "Vault does not exist");
            
                    Vault storage vault = vaults[msg.sender];
            
                    // checks that the vault is sufficiently collateralized
                    uint256 newOTokensBalance = vault.oTokensIssued.add(oTokensToIssue);
                    require(isSafe(vault.collateral, newOTokensBalance), "unsafe to mint");
            
                    // issue the oTokens
                    vault.oTokensIssued = newOTokensBalance;
                    _mint(receiver, oTokensToIssue);
            
                    emit IssuedOTokens(receiver, oTokensToIssue, msg.sender);
                    return;
                }
            
                /**
                 * @notice Returns the vault for a given address
                 * @param vaultOwner the owner of the Vault to return
                 */
                function getVault(address payable vaultOwner)
                    public
                    view
                    returns (uint256, uint256, uint256, bool)
                {
                    Vault storage vault = vaults[vaultOwner];
                    return (
                        vault.collateral,
                        vault.oTokensIssued,
                        vault.underlying,
                        vault.owned
                    );
                }
            
                /**
                 * @notice Returns true if the given ERC20 is ETH.
                 * @param _ierc20 the ERC20 asset.
                 */
                function isETH(IERC20 _ierc20) public pure returns (bool) {
                    return _ierc20 == IERC20(0);
                }
            
                /**
                 * @notice allows the owner to burn their oTokens to increase the collateralization ratio of
                 * their vault.
                 * @param amtToBurn number of oTokens to burn
                 * @dev only want to call this function before expiry. After expiry, no benefit to calling it.
                 */
                function burnOTokens(uint256 amtToBurn) public notExpired {
                    require(hasVault(msg.sender), "Vault does not exist");
            
                    Vault storage vault = vaults[msg.sender];
            
                    vault.oTokensIssued = vault.oTokensIssued.sub(amtToBurn);
                    _burn(msg.sender, amtToBurn);
            
                    emit BurnOTokens(msg.sender, amtToBurn);
                }
            
                /**
                 * @notice allows the owner to remove excess collateral from the vault before expiry. Removing collateral lowers
                 * the collateralization ratio of the vault.
                 * @param amtToRemove Amount of collateral to remove in 10^-18.
                 */
                function removeCollateral(uint256 amtToRemove) public notExpired {
                    require(amtToRemove > 0, "Cannot remove 0 collateral");
                    require(hasVault(msg.sender), "Vault does not exist");
            
                    Vault storage vault = vaults[msg.sender];
                    require(
                        amtToRemove <= getCollateral(msg.sender),
                        "Can't remove more collateral than owned"
                    );
            
                    // check that vault will remain safe after removing collateral
                    uint256 newCollateralBalance = vault.collateral.sub(amtToRemove);
            
                    require(
                        isSafe(newCollateralBalance, vault.oTokensIssued),
                        "Vault is unsafe"
                    );
            
                    // remove the collateral
                    vault.collateral = newCollateralBalance;
                    transferCollateral(msg.sender, amtToRemove);
            
                    emit RemoveCollateral(amtToRemove, msg.sender);
                }
            
                /**
                 * @notice after expiry, each vault holder can get back their proportional share of collateral
                 * from vaults that they own.
                 * @dev The owner gets all of their collateral back if no exercise event took their collateral.
                 */
                function redeemVaultBalance() public {
                    require(hasExpired(), "Can't collect collateral until expiry");
                    require(hasVault(msg.sender), "Vault does not exist");
            
                    // pay out owner their share
                    Vault storage vault = vaults[msg.sender];
            
                    // To deal with lower precision
                    uint256 collateralToTransfer = vault.collateral;
                    uint256 underlyingToTransfer = vault.underlying;
            
                    vault.collateral = 0;
                    vault.oTokensIssued = 0;
                    vault.underlying = 0;
            
                    transferCollateral(msg.sender, collateralToTransfer);
                    transferUnderlying(msg.sender, underlyingToTransfer);
            
                    emit RedeemVaultBalance(
                        collateralToTransfer,
                        underlyingToTransfer,
                        msg.sender
                    );
                }
            
                /**
                 * This function returns the maximum amount of collateral liquidatable if the given vault is unsafe
                 * @param vaultOwner The index of the vault to be liquidated
                 */
                function maxOTokensLiquidatable(address payable vaultOwner)
                    public
                    view
                    returns (uint256)
                {
                    if (isUnsafe(vaultOwner)) {
                        Vault storage vault = vaults[vaultOwner];
                        uint256 maxCollateralLiquidatable = vault
                            .collateral
                            .mul(liquidationFactor.value)
                            .div(10**uint256(-liquidationFactor.exponent));
            
                        uint256 one = 10**uint256(-liquidationIncentive.exponent);
                        Number memory liqIncentive = Number(
                            liquidationIncentive.value.add(one),
                            liquidationIncentive.exponent
                        );
                        return calculateOTokens(maxCollateralLiquidatable, liqIncentive);
                    } else {
                        return 0;
                    }
                }
            
                /**
                 * @notice This function can be called by anyone who notices a vault is undercollateralized.
                 * The caller gets a reward for reducing the amount of oTokens in circulation.
                 * @dev Liquidator comes with _oTokens. They get _oTokens * strikePrice * (incentive + fee)
                 * amount of collateral out. They can liquidate a max of liquidationFactor * vault.collateral out
                 * in one function call i.e. partial liquidations.
                 * @param vaultOwner The index of the vault to be liquidated
                 * @param oTokensToLiquidate The number of oTokens being taken out of circulation
                 */
                function liquidate(address payable vaultOwner, uint256 oTokensToLiquidate)
                    public
                    notExpired
                {
                    require(hasVault(vaultOwner), "Vault does not exist");
            
                    Vault storage vault = vaults[vaultOwner];
            
                    // cannot liquidate a safe vault.
                    require(isUnsafe(vaultOwner), "Vault is safe");
            
                    // Owner can't liquidate themselves
                    require(msg.sender != vaultOwner, "Owner can't liquidate themselves");
            
                    uint256 amtCollateral = calculateCollateralToPay(
                        oTokensToLiquidate,
                        Number(1, 0)
                    );
                    uint256 amtIncentive = calculateCollateralToPay(
                        oTokensToLiquidate,
                        liquidationIncentive
                    );
                    uint256 amtCollateralToPay = amtCollateral.add(amtIncentive);
            
                    // calculate the maximum amount of collateral that can be liquidated
                    uint256 maxCollateralLiquidatable = vault.collateral.mul(
                        liquidationFactor.value
                    );
            
                    if (liquidationFactor.exponent > 0) {
                        maxCollateralLiquidatable = maxCollateralLiquidatable.mul(
                            10**uint256(liquidationFactor.exponent)
                        );
                    } else {
                        maxCollateralLiquidatable = maxCollateralLiquidatable.div(
                            10**uint256(-1 * liquidationFactor.exponent)
                        );
                    }
            
                    require(
                        amtCollateralToPay <= maxCollateralLiquidatable,
                        "Can only liquidate liquidation factor at any given time"
                    );
            
                    // deduct the collateral and oTokensIssued
                    vault.collateral = vault.collateral.sub(amtCollateralToPay);
                    vault.oTokensIssued = vault.oTokensIssued.sub(oTokensToLiquidate);
            
                    // transfer the collateral and burn the _oTokens
                    _burn(msg.sender, oTokensToLiquidate);
                    transferCollateral(msg.sender, amtCollateralToPay);
            
                    emit Liquidate(amtCollateralToPay, vaultOwner, msg.sender);
                }
            
                /**
                 * @notice checks if a vault is unsafe. If so, it can be liquidated
                 * @param vaultOwner The number of the vault to check
                 * @return true or false
                 */
                function isUnsafe(address payable vaultOwner) public view returns (bool) {
                    bool stillUnsafe = !isSafe(
                        getCollateral(vaultOwner),
                        getOTokensIssued(vaultOwner)
                    );
                    return stillUnsafe;
                }
            
                /**
                 * @notice This function returns if an -30 <= exponent <= 30
                 */
                function isWithinExponentRange(int32 val) internal pure returns (bool) {
                    return ((val <= 30) && (val >= -30));
                }
            
                /**
                 * @notice This function calculates and returns the amount of collateral in the vault
                */
                function getCollateral(address payable vaultOwner)
                    internal
                    view
                    returns (uint256)
                {
                    Vault storage vault = vaults[vaultOwner];
                    return vault.collateral;
                }
            
                /**
                 * @notice This function calculates and returns the amount of puts issued by the Vault
                */
                function getOTokensIssued(address payable vaultOwner)
                    internal
                    view
                    returns (uint256)
                {
                    Vault storage vault = vaults[vaultOwner];
                    return vault.oTokensIssued;
                }
            
                /**
                 * @notice Called by anyone holding the oTokens and underlying during the
                 * exercise window i.e. from `expiry - windowSize` time to `expiry` time. The caller
                 * transfers in their oTokens and corresponding amount of underlying and gets
                 * `strikePrice * oTokens` amount of collateral out. The collateral paid out is taken from
                 * the specified vault holder. At the end of the expiry window, the vault holder can redeem their balance
                 * of collateral. The vault owner can withdraw their underlying at any time.
                 * The user has to allow the contract to handle their oTokens and underlying on his behalf before these functions are called.
                 * @param oTokensToExercise the number of oTokens being exercised.
                 * @param vaultToExerciseFrom the address of the vaultOwner to take collateral from.
                 * @dev oTokenExchangeRate is the number of underlying tokens that 1 oToken protects.
                 */
                function _exercise(
                    uint256 oTokensToExercise,
                    address payable vaultToExerciseFrom
                ) internal {
                    // 1. before exercise window: revert
                    require(
                        isExerciseWindow(),
                        "Can't exercise outside of the exercise window"
                    );
            
                    require(hasVault(vaultToExerciseFrom), "Vault does not exist");
            
                    Vault storage vault = vaults[vaultToExerciseFrom];
                    require(oTokensToExercise > 0, "Can't exercise 0 oTokens");
                    // Check correct amount of oTokens passed in)
                    require(
                        oTokensToExercise <= vault.oTokensIssued,
                        "Can't exercise more oTokens than the owner has"
                    );
                    // Ensure person calling has enough oTokens
                    require(
                        balanceOf(msg.sender) >= oTokensToExercise,
                        "Not enough oTokens"
                    );
            
                    // 1. Check sufficient underlying
                    // 1.1 update underlying balances
                    uint256 amtUnderlyingToPay = underlyingRequiredToExercise(
                        oTokensToExercise
                    );
                    vault.underlying = vault.underlying.add(amtUnderlyingToPay);
            
                    // 2. Calculate Collateral to pay
                    // 2.1 Payout enough collateral to get (strikePrice * oTokens) amount of collateral
                    uint256 amtCollateralToPay = calculateCollateralToPay(
                        oTokensToExercise,
                        Number(1, 0)
                    );
            
                    // 2.2 Take a small fee on every exercise
                    uint256 amtFee = calculateCollateralToPay(
                        oTokensToExercise,
                        transactionFee
                    );
                    totalFee = totalFee.add(amtFee);
            
                    uint256 totalCollateralToPay = amtCollateralToPay.add(amtFee);
                    require(
                        totalCollateralToPay <= vault.collateral,
                        "Vault underwater, can't exercise"
                    );
            
                    // 3. Update collateral + oToken balances
                    vault.collateral = vault.collateral.sub(totalCollateralToPay);
                    vault.oTokensIssued = vault.oTokensIssued.sub(oTokensToExercise);
            
                    // 4. Transfer in underlying, burn oTokens + pay out collateral
                    // 4.1 Transfer in underlying
                    if (isETH(underlying)) {
                        require(msg.value == amtUnderlyingToPay, "Incorrect msg.value");
                    } else {
                        require(
                            underlying.transferFrom(
                                msg.sender,
                                address(this),
                                amtUnderlyingToPay
                            ),
                            "Could not transfer in tokens"
                        );
                    }
                    // 4.2 burn oTokens
                    _burn(msg.sender, oTokensToExercise);
            
                    // 4.3 Pay out collateral
                    transferCollateral(msg.sender, amtCollateralToPay);
            
                    emit Exercise(
                        amtUnderlyingToPay,
                        amtCollateralToPay,
                        msg.sender,
                        vaultToExerciseFrom
                    );
            
                }
            
                /**
                 * @notice adds `_amt` collateral to `vaultOwner` and returns the new balance of the vault
                 * @param vaultOwner the index of the vault
                 * @param amt the amount of collateral to add
                 */
                function _addCollateral(address payable vaultOwner, uint256 amt)
                    internal
                    notExpired
                    returns (uint256)
                {
                    Vault storage vault = vaults[vaultOwner];
                    vault.collateral = vault.collateral.add(amt);
            
                    return vault.collateral;
                }
            
                /**
                 * @notice checks if a hypothetical vault is safe with the given collateralAmt and oTokensIssued
                 * @param collateralAmt The amount of collateral the hypothetical vault has
                 * @param oTokensIssued The amount of oTokens generated by the hypothetical vault
                 * @return true or false
                 */
                function isSafe(uint256 collateralAmt, uint256 oTokensIssued)
                    internal
                    view
                    returns (bool)
                {
                    // get price from Oracle
                    uint256 collateralToEthPrice = getPrice(address(collateral));
                    uint256 strikeToEthPrice = getPrice(address(strike));
            
                    // check `oTokensIssued * minCollateralizationRatio * strikePrice <= collAmt * collateralToStrikePrice`
                    uint256 leftSideVal = oTokensIssued
                        .mul(minCollateralizationRatio.value)
                        .mul(strikePrice.value);
                    int32 leftSideExp = minCollateralizationRatio.exponent +
                        strikePrice.exponent;
            
                    uint256 rightSideVal = (collateralAmt.mul(collateralToEthPrice)).div(
                        strikeToEthPrice
                    );
                    int32 rightSideExp = collateralExp;
            
                    uint256 exp = 0;
                    bool stillSafe = false;
            
                    if (rightSideExp < leftSideExp) {
                        exp = uint256(leftSideExp - rightSideExp);
                        stillSafe = leftSideVal.mul(10**exp) <= rightSideVal;
                    } else {
                        exp = uint256(rightSideExp - leftSideExp);
                        stillSafe = leftSideVal <= rightSideVal.mul(10**exp);
                    }
            
                    return stillSafe;
                }
            
                /**
                 * This function returns the maximum amount of oTokens that can safely be issued against the specified amount of collateral.
                 * @param collateralAmt The amount of collateral against which oTokens will be issued.
                 */
                function maxOTokensIssuable(uint256 collateralAmt)
                    public
                    view
                    returns (uint256)
                {
                    return calculateOTokens(collateralAmt, minCollateralizationRatio);
            
                }
            
                /**
                 * @notice This function is used to calculate the amount of tokens that can be issued.
                 * @dev The amount of oTokens is determined by:
                 * oTokensIssued  <= collateralAmt * collateralToStrikePrice / (proportion * strikePrice)
                 * @param collateralAmt The amount of collateral
                 * @param proportion The proportion of the collateral to pay out. If 100% of collateral
                 * should be paid out, pass in Number(1, 0). The proportion might be less than 100% if
                 * you are calculating fees.
                 */
                function calculateOTokens(uint256 collateralAmt, Number memory proportion)
                    internal
                    view
                    returns (uint256)
                {
                    uint256 collateralToEthPrice = getPrice(address(collateral));
                    uint256 strikeToEthPrice = getPrice(address(strike));
            
                    // oTokensIssued  <= collAmt * collateralToStrikePrice / (proportion * strikePrice)
                    uint256 denomVal = proportion.value.mul(strikePrice.value);
                    int32 denomExp = proportion.exponent + strikePrice.exponent;
            
                    uint256 numeratorVal = (collateralAmt.mul(collateralToEthPrice)).div(
                        strikeToEthPrice
                    );
                    int32 numeratorExp = collateralExp;
            
                    uint256 exp = 0;
                    uint256 numOptions = 0;
            
                    if (numeratorExp < denomExp) {
                        exp = uint256(denomExp - numeratorExp);
                        numOptions = numeratorVal.div(denomVal.mul(10**exp));
                    } else {
                        exp = uint256(numeratorExp - denomExp);
                        numOptions = numeratorVal.mul(10**exp).div(denomVal);
                    }
            
                    return numOptions;
                }
            
                /**
                 * @notice This function calculates the amount of collateral to be paid out.
                 * @dev The amount of collateral to paid out is determined by:
                 * (proportion * strikePrice * strikeToCollateralPrice * oTokens) amount of collateral.
                 * @param _oTokens The number of oTokens.
                 * @param proportion The proportion of the collateral to pay out. If 100% of collateral
                 * should be paid out, pass in Number(1, 0). The proportion might be less than 100% if
                 * you are calculating fees.
                 */
                function calculateCollateralToPay(
                    uint256 _oTokens,
                    Number memory proportion
                ) internal view returns (uint256) {
                    // Get price from oracle
                    uint256 collateralToEthPrice = getPrice(address(collateral));
                    uint256 strikeToEthPrice = getPrice(address(strike));
            
                    // calculate how much should be paid out
                    uint256 amtCollateralToPayInEthNum = _oTokens
                        .mul(strikePrice.value)
                        .mul(proportion.value)
                        .mul(strikeToEthPrice);
                    int32 amtCollateralToPayExp = strikePrice.exponent +
                        proportion.exponent -
                        collateralExp;
                    uint256 amtCollateralToPay = 0;
                    if (amtCollateralToPayExp > 0) {
                        uint32 exp = uint32(amtCollateralToPayExp);
                        amtCollateralToPay = amtCollateralToPayInEthNum.mul(10**exp).div(
                            collateralToEthPrice
                        );
                    } else {
                        uint32 exp = uint32(-1 * amtCollateralToPayExp);
                        amtCollateralToPay = (amtCollateralToPayInEthNum.div(10**exp)).div(
                            collateralToEthPrice
                        );
                    }
            
                    return amtCollateralToPay;
            
                }
            
                /**
                 * @notice This function transfers `amt` collateral to `_addr`
                 * @param _addr The address to send the collateral to
                 * @param _amt The amount of the collateral to pay out.
                 */
                function transferCollateral(address payable _addr, uint256 _amt) internal {
                    if (isETH(collateral)) {
                        _addr.transfer(_amt);
                    } else {
                        collateral.transfer(_addr, _amt);
                    }
                }
            
                /**
                 * @notice This function transfers `amt` underlying to `_addr`
                 * @param _addr The address to send the underlying to
                 * @param _amt The amount of the underlying to pay out.
                 */
                function transferUnderlying(address payable _addr, uint256 _amt) internal {
                    if (isETH(underlying)) {
                        _addr.transfer(_amt);
                    } else {
                        underlying.transfer(_addr, _amt);
                    }
                }
            
                /**
                 * @notice This function gets the price ETH (wei) to asset price.
                 * @param asset The address of the asset to get the price of
                 */
                function getPrice(address asset) internal view returns (uint256) {
                    if (address(collateral) == address(strike)) {
                        return 1;
                    } else if (asset == address(0)) {
                        return (10**18);
                    } else {
                        return COMPOUND_ORACLE.getPrice(asset);
                    }
                }
            }
            
            // File: contracts/oToken.sol
            
            pragma solidity 0.5.10;
            
            
            /**
             * @title Opyn's Options Contract
             * @author Opyn
             */
            
            contract oToken is OptionsContract {
                /**
                * @param _collateral The collateral asset
                * @param _collExp The precision of the collateral (-18 if ETH)
                * @param _underlying The asset that is being protected
                * @param _underlyingExp The precision of the underlying asset
                * @param _oTokenExchangeExp The precision of the `amount of underlying` that 1 oToken protects
                * @param _strikePrice The amount of strike asset that will be paid out
                * @param _strikeExp The precision of the strike asset (-18 if ETH)
                * @param _strike The asset in which the insurance is calculated
                * @param _expiry The time at which the insurance expires
                * @param _optionsExchange The contract which interfaces with the exchange + oracle
                * @param _oracleAddress The address of the oracle
                * @param _windowSize UNIX time. Exercise window is from `expiry - _windowSize` to `expiry`.
                */
                constructor(
                    IERC20 _collateral,
                    int32 _collExp,
                    IERC20 _underlying,
                    int32 _underlyingExp,
                    int32 _oTokenExchangeExp,
                    uint256 _strikePrice,
                    int32 _strikeExp,
                    IERC20 _strike,
                    uint256 _expiry,
                    OptionsExchange _optionsExchange,
                    address _oracleAddress,
                    uint256 _windowSize
                )
                    public
                    OptionsContract(
                        _collateral,
                        _collExp,
                        _underlying,
                        _underlyingExp,
                        _oTokenExchangeExp,
                        _strikePrice,
                        _strikeExp,
                        _strike,
                        _expiry,
                        _optionsExchange,
                        _oracleAddress,
                        _windowSize
                    )
                {}
            
                /**
                 * @notice opens a Vault, adds ETH collateral, and mints new oTokens in one step
                 * Remember that creating oTokens can put the owner at a risk of losing the collateral
                 * if an exercise event happens.
                 * The sell function provides the owner a chance to earn premiums.
                 * Ensure that you create and immediately sell oTokens atmoically.
                 * @param amtToCreate number of oTokens to create
                 * @param receiver address to send the Options to
                 */
                function createETHCollateralOption(uint256 amtToCreate, address receiver)
                    external
                    payable
                {
                    openVault();
                    addETHCollateralOption(amtToCreate, receiver);
                }
            
                /**
                 * @notice adds ETH collateral, and mints new oTokens in one step to an existing Vault
                 * Remember that creating oTokens can put the owner at a risk of losing the collateral
                 * if an exercise event happens.
                 * The sell function provides the owner a chance to earn premiums.
                 * Ensure that you create and immediately sell oTokens atmoically.
                 * @param amtToCreate number of oTokens to create
                 * @param receiver address to send the Options to
                 */
                function addETHCollateralOption(uint256 amtToCreate, address receiver)
                    public
                    payable
                {
                    addETHCollateral(msg.sender);
                    issueOTokens(amtToCreate, receiver);
                }
            
                /**
                 * @notice opens a Vault, adds ETH collateral, mints new oTokens and sell in one step
                 * @param amtToCreate number of oTokens to create
                 * @param receiver address to receive the premiums
                 */
                function createAndSellETHCollateralOption(
                    uint256 amtToCreate,
                    address payable receiver
                ) external payable {
                    openVault();
                    addETHCollateralOption(amtToCreate, address(this));
                    this.approve(address(optionsExchange), amtToCreate);
                    optionsExchange.sellOTokens(
                        receiver,
                        address(this),
                        address(0),
                        amtToCreate
                    );
                }
            
                /**
                 * @notice adds ETH collateral to an existing Vault, and mints new oTokens and sells the oTokens in one step
                 * @param amtToCreate number of oTokens to create
                 * @param receiver address to send the Options to
                 */
                function addAndSellETHCollateralOption(
                    uint256 amtToCreate,
                    address payable receiver
                ) public payable {
                    addETHCollateral(msg.sender);
                    issueOTokens(amtToCreate, address(this));
                    this.approve(address(optionsExchange), amtToCreate);
                    optionsExchange.sellOTokens(
                        receiver,
                        address(this),
                        address(0),
                        amtToCreate
                    );
                }
            
                /**
                 * @notice opens a Vault, adds ERC20 collateral, and mints new oTokens in one step
                 * Remember that creating oTokens can put the owner at a risk of losing the collateral
                 * if an exercise event happens.
                 * The sell function provides the owner a chance to earn premiums.
                 * Ensure that you create and immediately sell oTokens atmoically.
                 * @param amtToCreate number of oTokens to create
                 * @param amtCollateral amount of collateral added
                 * @param receiver address to send the Options to
                 */
                function createERC20CollateralOption(
                    uint256 amtToCreate,
                    uint256 amtCollateral,
                    address receiver
                ) external {
                    openVault();
                    addERC20CollateralOption(amtToCreate, amtCollateral, receiver);
                }
            
                /**
                 * @notice adds ERC20 collateral, and mints new oTokens in one step
                 * Remember that creating oTokens can put the owner at a risk of losing the collateral
                 * if an exercise event happens.
                 * The sell function provides the owner a chance to earn premiums.
                 * Ensure that you create and immediately sell oTokens atmoically.
                 * @param amtToCreate number of oTokens to create
                 * @param amtCollateral amount of collateral added
                 * @param receiver address to send the Options to
                 */
                function addERC20CollateralOption(
                    uint256 amtToCreate,
                    uint256 amtCollateral,
                    address receiver
                ) public {
                    addERC20Collateral(msg.sender, amtCollateral);
                    issueOTokens(amtToCreate, receiver);
                }
            
                /**
                 * @notice opens a Vault, adds ERC20 collateral, mints new oTokens and sells the oTokens in one step
                 * @param amtToCreate number of oTokens to create
                 * @param amtCollateral amount of collateral added
                 * @param receiver address to send the Options to
                 */
                function createAndSellERC20CollateralOption(
                    uint256 amtToCreate,
                    uint256 amtCollateral,
                    address payable receiver
                ) external {
                    openVault();
                    addERC20CollateralOption(amtToCreate, amtCollateral, address(this));
                    this.approve(address(optionsExchange), amtToCreate);
                    optionsExchange.sellOTokens(
                        receiver,
                        address(this),
                        address(0),
                        amtToCreate
                    );
                }
            
                /**
                 * @notice adds ERC20 collateral, mints new oTokens and sells the oTokens in one step
                 * @param amtToCreate number of oTokens to create
                 * @param amtCollateral amount of collateral added
                 * @param receiver address to send the Options to
                 */
                function addAndSellERC20CollateralOption(
                    uint256 amtToCreate,
                    uint256 amtCollateral,
                    address payable receiver
                ) public {
                    addERC20Collateral(msg.sender, amtCollateral);
                    issueOTokens(amtToCreate, address(this));
                    this.approve(address(optionsExchange), amtToCreate);
                    optionsExchange.sellOTokens(
                        receiver,
                        address(this),
                        address(0),
                        amtToCreate
                    );
                }
            }

            File 2 of 7: FiatTokenProxy
            pragma solidity ^0.4.24;
            
            // File: zos-lib/contracts/upgradeability/Proxy.sol
            
            /**
             * @title Proxy
             * @dev Implements delegation of calls to other contracts, with proper
             * forwarding of return values and bubbling of failures.
             * It defines a fallback function that delegates all calls to the address
             * returned by the abstract _implementation() internal function.
             */
            contract Proxy {
              /**
               * @dev Fallback function.
               * Implemented entirely in `_fallback`.
               */
              function () payable external {
                _fallback();
              }
            
              /**
               * @return The Address of the implementation.
               */
              function _implementation() internal view returns (address);
            
              /**
               * @dev Delegates execution to an implementation contract.
               * This is a low level function that doesn't return to its internal call site.
               * It will return to the external caller whatever the implementation returns.
               * @param implementation Address to delegate.
               */
              function _delegate(address implementation) internal {
                assembly {
                  // Copy msg.data. We take full control of memory in this inline assembly
                  // block because it will not return to Solidity code. We overwrite the
                  // Solidity scratch pad at memory position 0.
                  calldatacopy(0, 0, calldatasize)
            
                  // Call the implementation.
                  // out and outsize are 0 because we don't know the size yet.
                  let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0)
            
                  // Copy the returned data.
                  returndatacopy(0, 0, returndatasize)
            
                  switch result
                  // delegatecall returns 0 on error.
                  case 0 { revert(0, returndatasize) }
                  default { return(0, returndatasize) }
                }
              }
            
              /**
               * @dev Function that is run as the first thing in the fallback function.
               * Can be redefined in derived contracts to add functionality.
               * Redefinitions must call super._willFallback().
               */
              function _willFallback() internal {
              }
            
              /**
               * @dev fallback implementation.
               * Extracted to enable manual triggering.
               */
              function _fallback() internal {
                _willFallback();
                _delegate(_implementation());
              }
            }
            
            // File: openzeppelin-solidity/contracts/AddressUtils.sol
            
            /**
             * Utility library of inline functions on addresses
             */
            library AddressUtils {
            
              /**
               * Returns whether the target address is a contract
               * @dev This function will return false if invoked during the constructor of a contract,
               * as the code is not actually created until after the constructor finishes.
               * @param addr address to check
               * @return whether the target address is a contract
               */
              function isContract(address addr) internal view returns (bool) {
                uint256 size;
                // XXX Currently there is no better way to check if there is a contract in an address
                // than to check the size of the code at that address.
                // See https://ethereum.stackexchange.com/a/14016/36603
                // for more details about how this works.
                // TODO Check this again before the Serenity release, because all addresses will be
                // contracts then.
                // solium-disable-next-line security/no-inline-assembly
                assembly { size := extcodesize(addr) }
                return size > 0;
              }
            
            }
            
            // File: zos-lib/contracts/upgradeability/UpgradeabilityProxy.sol
            
            /**
             * @title UpgradeabilityProxy
             * @dev This contract implements a proxy that allows to change the
             * implementation address to which it will delegate.
             * Such a change is called an implementation upgrade.
             */
            contract UpgradeabilityProxy is Proxy {
              /**
               * @dev Emitted when the implementation is upgraded.
               * @param implementation Address of the new implementation.
               */
              event Upgraded(address implementation);
            
              /**
               * @dev Storage slot with the address of the current implementation.
               * This is the keccak-256 hash of "org.zeppelinos.proxy.implementation", and is
               * validated in the constructor.
               */
              bytes32 private constant IMPLEMENTATION_SLOT = 0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3;
            
              /**
               * @dev Contract constructor.
               * @param _implementation Address of the initial implementation.
               */
              constructor(address _implementation) public {
                assert(IMPLEMENTATION_SLOT == keccak256("org.zeppelinos.proxy.implementation"));
            
                _setImplementation(_implementation);
              }
            
              /**
               * @dev Returns the current implementation.
               * @return Address of the current implementation
               */
              function _implementation() internal view returns (address impl) {
                bytes32 slot = IMPLEMENTATION_SLOT;
                assembly {
                  impl := sload(slot)
                }
              }
            
              /**
               * @dev Upgrades the proxy to a new implementation.
               * @param newImplementation Address of the new implementation.
               */
              function _upgradeTo(address newImplementation) internal {
                _setImplementation(newImplementation);
                emit Upgraded(newImplementation);
              }
            
              /**
               * @dev Sets the implementation address of the proxy.
               * @param newImplementation Address of the new implementation.
               */
              function _setImplementation(address newImplementation) private {
                require(AddressUtils.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address");
            
                bytes32 slot = IMPLEMENTATION_SLOT;
            
                assembly {
                  sstore(slot, newImplementation)
                }
              }
            }
            
            // File: zos-lib/contracts/upgradeability/AdminUpgradeabilityProxy.sol
            
            /**
             * @title AdminUpgradeabilityProxy
             * @dev This contract combines an upgradeability proxy with an authorization
             * mechanism for administrative tasks.
             * All external functions in this contract must be guarded by the
             * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity
             * feature proposal that would enable this to be done automatically.
             */
            contract AdminUpgradeabilityProxy is UpgradeabilityProxy {
              /**
               * @dev Emitted when the administration has been transferred.
               * @param previousAdmin Address of the previous admin.
               * @param newAdmin Address of the new admin.
               */
              event AdminChanged(address previousAdmin, address newAdmin);
            
              /**
               * @dev Storage slot with the admin of the contract.
               * This is the keccak-256 hash of "org.zeppelinos.proxy.admin", and is
               * validated in the constructor.
               */
              bytes32 private constant ADMIN_SLOT = 0x10d6a54a4754c8869d6886b5f5d7fbfa5b4522237ea5c60d11bc4e7a1ff9390b;
            
              /**
               * @dev Modifier to check whether the `msg.sender` is the admin.
               * If it is, it will run the function. Otherwise, it will delegate the call
               * to the implementation.
               */
              modifier ifAdmin() {
                if (msg.sender == _admin()) {
                  _;
                } else {
                  _fallback();
                }
              }
            
              /**
               * Contract constructor.
               * It sets the `msg.sender` as the proxy administrator.
               * @param _implementation address of the initial implementation.
               */
              constructor(address _implementation) UpgradeabilityProxy(_implementation) public {
                assert(ADMIN_SLOT == keccak256("org.zeppelinos.proxy.admin"));
            
                _setAdmin(msg.sender);
              }
            
              /**
               * @return The address of the proxy admin.
               */
              function admin() external view ifAdmin returns (address) {
                return _admin();
              }
            
              /**
               * @return The address of the implementation.
               */
              function implementation() external view ifAdmin returns (address) {
                return _implementation();
              }
            
              /**
               * @dev Changes the admin of the proxy.
               * Only the current admin can call this function.
               * @param newAdmin Address to transfer proxy administration to.
               */
              function changeAdmin(address newAdmin) external ifAdmin {
                require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address");
                emit AdminChanged(_admin(), newAdmin);
                _setAdmin(newAdmin);
              }
            
              /**
               * @dev Upgrade the backing implementation of the proxy.
               * Only the admin can call this function.
               * @param newImplementation Address of the new implementation.
               */
              function upgradeTo(address newImplementation) external ifAdmin {
                _upgradeTo(newImplementation);
              }
            
              /**
               * @dev Upgrade the backing implementation of the proxy and call a function
               * on the new implementation.
               * This is useful to initialize the proxied contract.
               * @param newImplementation Address of the new implementation.
               * @param data Data to send as msg.data in the low level call.
               * It should include the signature and the parameters of the function to be
               * called, as described in
               * https://solidity.readthedocs.io/en/develop/abi-spec.html#function-selector-and-argument-encoding.
               */
              function upgradeToAndCall(address newImplementation, bytes data) payable external ifAdmin {
                _upgradeTo(newImplementation);
                require(address(this).call.value(msg.value)(data));
              }
            
              /**
               * @return The admin slot.
               */
              function _admin() internal view returns (address adm) {
                bytes32 slot = ADMIN_SLOT;
                assembly {
                  adm := sload(slot)
                }
              }
            
              /**
               * @dev Sets the address of the proxy admin.
               * @param newAdmin Address of the new proxy admin.
               */
              function _setAdmin(address newAdmin) internal {
                bytes32 slot = ADMIN_SLOT;
            
                assembly {
                  sstore(slot, newAdmin)
                }
              }
            
              /**
               * @dev Only fall back when the sender is not the admin.
               */
              function _willFallback() internal {
                require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin");
                super._willFallback();
              }
            }
            
            // File: contracts/FiatTokenProxy.sol
            
            /**
            * Copyright CENTRE SECZ 2018
            *
            * Permission is hereby granted, free of charge, to any person obtaining a copy 
            * of this software and associated documentation files (the "Software"), to deal 
            * in the Software without restriction, including without limitation the rights 
            * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
            * copies of the Software, and to permit persons to whom the Software is furnished to 
            * do so, subject to the following conditions:
            *
            * The above copyright notice and this permission notice shall be included in all 
            * copies or substantial portions of the Software.
            *
            * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
            * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
            * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
            * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
            * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
            * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
            */
            
            pragma solidity ^0.4.24;
            
            
            /**
             * @title FiatTokenProxy
             * @dev This contract proxies FiatToken calls and enables FiatToken upgrades
            */ 
            contract FiatTokenProxy is AdminUpgradeabilityProxy {
                constructor(address _implementation) public AdminUpgradeabilityProxy(_implementation) {
                }
            }

            File 3 of 7: OptionsExchange
            // File: @openzeppelin/contracts/GSN/Context.sol
            
            pragma solidity ^0.5.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.
             */
            contract Context {
                // Empty internal constructor, to prevent people from mistakenly deploying
                // an instance of this contract, which should be used via inheritance.
                constructor () internal { }
                // solhint-disable-previous-line no-empty-blocks
            
                function _msgSender() internal view returns (address payable) {
                    return msg.sender;
                }
            
                function _msgData() internal view 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/token/ERC20/IERC20.sol
            
            pragma solidity ^0.5.0;
            
            /**
             * @dev Interface of the ERC20 standard as defined in the EIP. Does not include
             * the optional functions; to access them see {ERC20Detailed}.
             */
            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.5.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.
                 *
                 * _Available since v2.4.0._
                 */
                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.
                 *
                 * _Available since v2.4.0._
                 */
                function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                    // Solidity only automatically asserts when dividing by 0
                    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.
                 *
                 * _Available since v2.4.0._
                 */
                function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                    require(b != 0, errorMessage);
                    return a % b;
                }
            }
            
            // File: @openzeppelin/contracts/token/ERC20/ERC20.sol
            
            pragma solidity ^0.5.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 {ERC20Mintable}.
             *
             * TIP: For a detailed writeup see our guide
             * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
             * to implement supply mechanisms].
             *
             * We have followed general OpenZeppelin guidelines: functions revert instead
             * of returning `false` on failure. This behavior is nonetheless conventional
             * and does not conflict with the expectations of ERC20 applications.
             *
             * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
             * This allows applications to reconstruct the allowance for all accounts just
             * by listening to said events. Other implementations of the EIP may not emit
             * these events, as it isn't required by the specification.
             *
             * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
             * functions have been added to mitigate the well-known issues around setting
             * allowances. See {IERC20-approve}.
             */
            contract ERC20 is Context, IERC20 {
                using SafeMath for uint256;
            
                mapping (address => uint256) private _balances;
            
                mapping (address => mapping (address => uint256)) private _allowances;
            
                uint256 private _totalSupply;
            
                /**
                 * @dev See {IERC20-totalSupply}.
                 */
                function totalSupply() public view returns (uint256) {
                    return _totalSupply;
                }
            
                /**
                 * @dev See {IERC20-balanceOf}.
                 */
                function balanceOf(address account) public view 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 returns (bool) {
                    _transfer(_msgSender(), recipient, amount);
                    return true;
                }
            
                /**
                 * @dev See {IERC20-allowance}.
                 */
                function allowance(address owner, address spender) public view returns (uint256) {
                    return _allowances[owner][spender];
                }
            
                /**
                 * @dev See {IERC20-approve}.
                 *
                 * Requirements:
                 *
                 * - `spender` cannot be the zero address.
                 */
                function approve(address spender, uint256 amount) public 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 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 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 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 {
                    require(sender != address(0), "ERC20: transfer from the zero address");
                    require(recipient != address(0), "ERC20: transfer to the zero address");
            
                    _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 {
                    require(account != address(0), "ERC20: mint to the zero address");
            
                    _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 {
                    require(account != address(0), "ERC20: burn from the zero address");
            
                    _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 is 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 {
                    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 Destroys `amount` tokens from `account`.`amount` is then deducted
                 * from the caller's allowance.
                 *
                 * See {_burn} and {_approve}.
                 */
                function _burnFrom(address account, uint256 amount) internal {
                    _burn(account, amount);
                    _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance"));
                }
            }
            
            // File: contracts/lib/CompoundOracleInterface.sol
            
            pragma solidity ^0.5.0;
            // AT MAINNET ADDRESS: 0x02557a5E05DeFeFFD4cAe6D83eA3d173B272c904
            
            contract CompoundOracleInterface {
                // returns asset:eth -- to get USDC:eth, have to do 10**24/result,
            
            
                constructor() public {
                }
            
                /**
              * @notice retrieves price of an asset
              * @dev function to get price for an asset
              * @param asset Asset for which to get the price
              * @return uint mantissa of asset price (scaled by 1e18) or zero if unset or contract paused
              */
                function getPrice(address asset) public view returns (uint);
                function getUnderlyingPrice(ERC20 cToken) public view returns (uint);
                // function getPrice(address asset) public view returns (uint) {
                //     return 527557000000000;
                // }
            
            }
            
            // File: contracts/lib/UniswapExchangeInterface.sol
            
            pragma solidity 0.5.10;
            
            
            // Solidity Interface
            contract UniswapExchangeInterface {
                // Address of ERC20 token sold on this exchange
                function tokenAddress() external view returns (address token);
                // Address of Uniswap Factory
                function factoryAddress() external view returns (address factory);
                // Provide Liquidity
                function addLiquidity(uint256 min_liquidity, uint256 max_tokens, uint256 deadline) external payable returns (uint256);
                function removeLiquidity(uint256 amount, uint256 min_eth, uint256 min_tokens, uint256 deadline) external returns (uint256, uint256);
                // Get Prices
                function getEthToTokenInputPrice(uint256 eth_sold) external view returns (uint256 tokens_bought);
                function getEthToTokenOutputPrice(uint256 tokens_bought) external view returns (uint256 eth_sold);
                function getTokenToEthInputPrice(uint256 tokens_sold) external view returns (uint256 eth_bought);
                function getTokenToEthOutputPrice(uint256 eth_bought) external view returns (uint256 tokens_sold);
                // Trade ETH to ERC20
                function ethToTokenSwapInput(uint256 min_tokens, uint256 deadline) external payable returns (uint256  tokens_bought);
                function ethToTokenTransferInput(uint256 min_tokens, uint256 deadline, address recipient) external payable returns (uint256  tokens_bought);
                function ethToTokenSwapOutput(uint256 tokens_bought, uint256 deadline) external payable returns (uint256  eth_sold);
                function ethToTokenTransferOutput(uint256 tokens_bought, uint256 deadline, address recipient) external payable returns (uint256  eth_sold);
                // Trade ERC20 to ETH
                function tokenToEthSwapInput(uint256 tokens_sold, uint256 min_eth, uint256 deadline) external returns (uint256  eth_bought);
                function tokenToEthTransferInput(uint256 tokens_sold, uint256 min_eth, uint256 deadline, address recipient) external returns (uint256  eth_bought);
                function tokenToEthSwapOutput(uint256 eth_bought, uint256 max_tokens, uint256 deadline) external returns (uint256  tokens_sold);
                function tokenToEthTransferOutput(uint256 eth_bought, uint256 max_tokens, uint256 deadline, address recipient) external returns (uint256  tokens_sold);
                // Trade ERC20 to ERC20
                function tokenToTokenSwapInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address token_addr) external returns (uint256  tokens_bought);
                function tokenToTokenTransferInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address recipient, address token_addr) external returns (uint256  tokens_bought);
                function tokenToTokenSwapOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address token_addr) external returns (uint256  tokens_sold);
                function tokenToTokenTransferOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address recipient, address token_addr) external returns (uint256  tokens_sold);
                // Trade ERC20 to Custom Pool
                function tokenToExchangeSwapInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address exchange_addr) external returns (uint256  tokens_bought);
                function tokenToExchangeTransferInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address recipient, address exchange_addr) external returns (uint256  tokens_bought);
                function tokenToExchangeSwapOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address exchange_addr) external returns (uint256  tokens_sold);
                function tokenToExchangeTransferOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address recipient, address exchange_addr) external returns (uint256  tokens_sold);
                // ERC20 comaptibility for liquidity tokens
                bytes32 public name;
                bytes32 public symbol;
                uint256 public decimals;
                function transfer(address _to, uint256 _value) external returns (bool);
                function transferFrom(address _from, address _to, uint256 value) external returns (bool);
                function approve(address _spender, uint256 _value) external returns (bool);
                function allowance(address _owner, address _spender) external view returns (uint256);
                function balanceOf(address _owner) external view returns (uint256);
                function totalSupply() external view returns (uint256);
                // Never use
                function setup(address token_addr) external;
            }
            
            // File: contracts/lib/UniswapFactoryInterface.sol
            
            pragma solidity 0.5.10;
            
            
            // Solidity Interface
            contract UniswapFactoryInterface {
                // Public Variables
                address public exchangeTemplate;
                uint256 public tokenCount;
                // // Create Exchange
                function createExchange(address token) external returns (address exchange);
                // Get Exchange and Token Info
                function getExchange(address token) external view returns (address exchange);
                function getToken(address exchange) external view returns (address token);
                function getTokenWithId(uint256 tokenId) external view returns (address token);
                // Never use
                function initializeFactory(address template) external;
                // function createExchange(address token) external returns (address exchange) {
                //     return 0x06D014475F84Bb45b9cdeD1Cf3A1b8FE3FbAf128;
                // }
                // // Get Exchange and Token Info
                // function getExchange(address token) external view returns (address exchange){
                //     return 0x06D014475F84Bb45b9cdeD1Cf3A1b8FE3FbAf128;
                // }
                // function getToken(address exchange) external view returns (address token) {
                //     return 0x06D014475F84Bb45b9cdeD1Cf3A1b8FE3FbAf128;
                // }
                // function getTokenWithId(uint256 tokenId) external view returns (address token) {
                //     return 0x06D014475F84Bb45b9cdeD1Cf3A1b8FE3FbAf128;
                // }
            }
            
            // File: contracts/OptionsUtils.sol
            
            pragma solidity 0.5.10;
            
            
            
            
            
            contract OptionsUtils {
                // defauls are for mainnet
                UniswapFactoryInterface public UNISWAP_FACTORY;
            
                CompoundOracleInterface public COMPOUND_ORACLE;
            
                constructor(address _uniswapFactory, address _compoundOracle) public {
                    UNISWAP_FACTORY = UniswapFactoryInterface(_uniswapFactory);
                    COMPOUND_ORACLE = CompoundOracleInterface(_compoundOracle);
                }
            
                // TODO: for now gets Uniswap, later update to get other exchanges
                function getExchange(address _token)
                    public
                    view
                    returns (UniswapExchangeInterface)
                {
                    if (address(UNISWAP_FACTORY.getExchange(_token)) == address(0)) {
                        revert("No payout exchange");
                    }
            
                    UniswapExchangeInterface exchange = UniswapExchangeInterface(
                        UNISWAP_FACTORY.getExchange(_token)
                    );
            
                    return exchange;
                }
            
                function isETH(IERC20 _ierc20) public pure returns (bool) {
                    return _ierc20 == IERC20(0);
                }
            }
            
            // File: contracts/OptionsExchange.sol
            
            pragma solidity 0.5.10;
            
            
            
            
            
            
            contract OptionsExchange {
                uint256 constant LARGE_BLOCK_SIZE = 1651753129000;
                uint256 constant LARGE_APPROVAL_NUMBER = 10**30;
            
                UniswapFactoryInterface public UNISWAP_FACTORY;
            
                constructor(address _uniswapFactory) public {
                    UNISWAP_FACTORY = UniswapFactoryInterface(_uniswapFactory);
                }
            
                /*** Events ***/
                event SellOTokens(
                    address seller,
                    address payable receiver,
                    address oTokenAddress,
                    address payoutTokenAddress,
                    uint256 oTokensToSell,
                    uint256 payoutTokensReceived
                );
                event BuyOTokens(
                    address buyer,
                    address payable receiver,
                    address oTokenAddress,
                    address paymentTokenAddress,
                    uint256 oTokensToBuy,
                    uint256 premiumPaid
                );
            
                /**
                * @notice This function sells oTokens on Uniswap and sends back payoutTokens to the receiver
                * @param receiver The address to send the payout tokens back to
                * @param oTokenAddress The address of the oToken to sell
                * @param payoutTokenAddress The address of the token to receive the premiums in
                * @param oTokensToSell The number of oTokens to sell
                */
                function sellOTokens(
                    address payable receiver,
                    address oTokenAddress,
                    address payoutTokenAddress,
                    uint256 oTokensToSell
                ) public {
                    // @note: first need to bootstrap the uniswap exchange to get the address.
                    IERC20 oToken = IERC20(oTokenAddress);
                    IERC20 payoutToken = IERC20(payoutTokenAddress);
                    oToken.transferFrom(msg.sender, address(this), oTokensToSell);
                    uint256 payoutTokensReceived = uniswapSellOToken(
                        oToken,
                        payoutToken,
                        oTokensToSell,
                        receiver
                    );
            
                    emit SellOTokens(
                        msg.sender,
                        receiver,
                        oTokenAddress,
                        payoutTokenAddress,
                        oTokensToSell,
                        payoutTokensReceived
                    );
                }
            
                /**
                * @notice This function buys oTokens on Uniswap and using paymentTokens from the receiver
                * @param receiver The address to send the oTokens back to
                * @param oTokenAddress The address of the oToken to buy
                * @param paymentTokenAddress The address of the token to pay the premiums in
                * @param oTokensToBuy The number of oTokens to buy
                */
                function buyOTokens(
                    address payable receiver,
                    address oTokenAddress,
                    address paymentTokenAddress,
                    uint256 oTokensToBuy
                ) public payable {
                    IERC20 oToken = IERC20(oTokenAddress);
                    IERC20 paymentToken = IERC20(paymentTokenAddress);
                    uniswapBuyOToken(paymentToken, oToken, oTokensToBuy, receiver);
                }
            
                /**
                * @notice This function calculates the amount of premiums that the seller
                * will receive if they sold oTokens on Uniswap
                * @param oTokenAddress The address of the oToken to sell
                * @param payoutTokenAddress The address of the token to receive the premiums in
                * @param oTokensToSell The number of oTokens to sell
                */
                function premiumReceived(
                    address oTokenAddress,
                    address payoutTokenAddress,
                    uint256 oTokensToSell
                ) public view returns (uint256) {
                    // get the amount of ETH that will be paid out if oTokensToSell is sold.
                    UniswapExchangeInterface oTokenExchange = getExchange(oTokenAddress);
                    uint256 ethReceived = oTokenExchange.getTokenToEthInputPrice(
                        oTokensToSell
                    );
            
                    if (!isETH(IERC20(payoutTokenAddress))) {
                        // get the amount of payout tokens that will be received if the ethRecieved is sold.
                        UniswapExchangeInterface payoutExchange = getExchange(
                            payoutTokenAddress
                        );
                        return payoutExchange.getEthToTokenInputPrice(ethReceived);
                    }
                    return ethReceived;
            
                }
            
                /**
                * @notice This function calculates the premiums to be paid if a buyer wants to
                * buy oTokens on Uniswap
                * @param oTokenAddress The address of the oToken to buy
                * @param paymentTokenAddress The address of the token to pay the premiums in
                * @param oTokensToBuy The number of oTokens to buy
                */
                function premiumToPay(
                    address oTokenAddress,
                    address paymentTokenAddress,
                    uint256 oTokensToBuy
                ) public view returns (uint256) {
                    // get the amount of ETH that needs to be paid for oTokensToBuy.
                    UniswapExchangeInterface oTokenExchange = getExchange(oTokenAddress);
                    uint256 ethToPay = oTokenExchange.getEthToTokenOutputPrice(
                        oTokensToBuy
                    );
            
                    if (!isETH(IERC20(paymentTokenAddress))) {
                        // get the amount of paymentTokens that needs to be paid to get the desired ethToPay.
                        UniswapExchangeInterface paymentTokenExchange = getExchange(
                            paymentTokenAddress
                        );
                        return paymentTokenExchange.getTokenToEthOutputPrice(ethToPay);
                    }
            
                    return ethToPay;
                }
            
                function uniswapSellOToken(
                    IERC20 oToken,
                    IERC20 payoutToken,
                    uint256 _amt,
                    address payable _transferTo
                ) internal returns (uint256) {
                    require(!isETH(oToken), "Can only sell oTokens");
                    UniswapExchangeInterface exchange = getExchange(address(oToken));
            
                    if (isETH(payoutToken)) {
                        //Token to ETH
                        oToken.approve(address(exchange), _amt);
                        return
                            exchange.tokenToEthTransferInput(
                                _amt,
                                1,
                                LARGE_BLOCK_SIZE,
                                _transferTo
                            );
                    } else {
                        //Token to Token
                        oToken.approve(address(exchange), _amt);
                        return
                            exchange.tokenToTokenTransferInput(
                                _amt,
                                1,
                                1,
                                LARGE_BLOCK_SIZE,
                                _transferTo,
                                address(payoutToken)
                            );
                    }
                }
            
                function uniswapBuyOToken(
                    IERC20 paymentToken,
                    IERC20 oToken,
                    uint256 _amt,
                    address payable _transferTo
                ) public returns (uint256) {
                    require(!isETH(oToken), "Can only buy oTokens");
            
                    if (!isETH(paymentToken)) {
                        UniswapExchangeInterface exchange = getExchange(
                            address(paymentToken)
                        );
            
                        uint256 paymentTokensToTransfer = premiumToPay(
                            address(oToken),
                            address(paymentToken),
                            _amt
                        );
                        paymentToken.transferFrom(
                            msg.sender,
                            address(this),
                            paymentTokensToTransfer
                        );
            
                        // Token to Token
                        paymentToken.approve(address(exchange), LARGE_APPROVAL_NUMBER);
            
                        emit BuyOTokens(
                            msg.sender,
                            _transferTo,
                            address(oToken),
                            address(paymentToken),
                            _amt,
                            paymentTokensToTransfer
                        );
            
                        return
                            exchange.tokenToTokenTransferInput(
                                paymentTokensToTransfer,
                                1,
                                1,
                                LARGE_BLOCK_SIZE,
                                _transferTo,
                                address(oToken)
                            );
                    } else {
                        // ETH to Token
                        UniswapExchangeInterface exchange = UniswapExchangeInterface(
                            UNISWAP_FACTORY.getExchange(address(oToken))
                        );
            
                        uint256 ethToTransfer = exchange.getEthToTokenOutputPrice(_amt);
            
                        emit BuyOTokens(
                            msg.sender,
                            _transferTo,
                            address(oToken),
                            address(paymentToken),
                            _amt,
                            ethToTransfer
                        );
            
                        return
                            exchange.ethToTokenTransferOutput.value(ethToTransfer)(
                                _amt,
                                LARGE_BLOCK_SIZE,
                                _transferTo
                            );
                    }
                }
            
                function getExchange(address _token)
                    internal
                    view
                    returns (UniswapExchangeInterface)
                {
                    UniswapExchangeInterface exchange = UniswapExchangeInterface(
                        UNISWAP_FACTORY.getExchange(_token)
                    );
            
                    if (address(exchange) == address(0)) {
                        revert("No payout exchange");
                    }
            
                    return exchange;
                }
            
                function isETH(IERC20 _ierc20) internal pure returns (bool) {
                    return _ierc20 == IERC20(0);
                }
            
                function() external payable {
                    // to get ether from uniswap exchanges
                }
            
            }

            File 4 of 7: Vyper_contract
            # @title Uniswap Exchange Interface V1
            # @notice Source code found at https://github.com/uniswap
            # @notice Use at your own risk
            
            contract Factory():
                def getExchange(token_addr: address) -> address: constant
            
            contract Exchange():
                def getEthToTokenOutputPrice(tokens_bought: uint256) -> uint256(wei): constant
                def ethToTokenTransferInput(min_tokens: uint256, deadline: timestamp, recipient: address) -> uint256: modifying
                def ethToTokenTransferOutput(tokens_bought: uint256, deadline: timestamp, recipient: address) -> uint256(wei): modifying
            
            TokenPurchase: event({buyer: indexed(address), eth_sold: indexed(uint256(wei)), tokens_bought: indexed(uint256)})
            EthPurchase: event({buyer: indexed(address), tokens_sold: indexed(uint256), eth_bought: indexed(uint256(wei))})
            AddLiquidity: event({provider: indexed(address), eth_amount: indexed(uint256(wei)), token_amount: indexed(uint256)})
            RemoveLiquidity: event({provider: indexed(address), eth_amount: indexed(uint256(wei)), token_amount: indexed(uint256)})
            Transfer: event({_from: indexed(address), _to: indexed(address), _value: uint256})
            Approval: event({_owner: indexed(address), _spender: indexed(address), _value: uint256})
            
            name: public(bytes32)                             # Uniswap V1
            symbol: public(bytes32)                           # UNI-V1
            decimals: public(uint256)                         # 18
            totalSupply: public(uint256)                      # total number of UNI in existence
            balances: uint256[address]                        # UNI balance of an address
            allowances: (uint256[address])[address]           # UNI allowance of one address on another
            token: address(ERC20)                             # address of the ERC20 token traded on this contract
            factory: Factory                                  # interface for the factory that created this contract
            
            # @dev This function acts as a contract constructor which is not currently supported in contracts deployed
            #      using create_with_code_of(). It is called once by the factory during contract creation.
            @public
            def setup(token_addr: address):
                assert (self.factory == ZERO_ADDRESS and self.token == ZERO_ADDRESS) and token_addr != ZERO_ADDRESS
                self.factory = msg.sender
                self.token = token_addr
                self.name = 0x556e697377617020563100000000000000000000000000000000000000000000
                self.symbol = 0x554e492d56310000000000000000000000000000000000000000000000000000
                self.decimals = 18
            
            # @notice Deposit ETH and Tokens (self.token) at current ratio to mint UNI tokens.
            # @dev min_liquidity does nothing when total UNI supply is 0.
            # @param min_liquidity Minimum number of UNI sender will mint if total UNI supply is greater than 0.
            # @param max_tokens Maximum number of tokens deposited. Deposits max amount if total UNI supply is 0.
            # @param deadline Time after which this transaction can no longer be executed.
            # @return The amount of UNI minted.
            @public
            @payable
            def addLiquidity(min_liquidity: uint256, max_tokens: uint256, deadline: timestamp) -> uint256:
                assert deadline > block.timestamp and (max_tokens > 0 and msg.value > 0)
                total_liquidity: uint256 = self.totalSupply
                if total_liquidity > 0:
                    assert min_liquidity > 0
                    eth_reserve: uint256(wei) = self.balance - msg.value
                    token_reserve: uint256 = self.token.balanceOf(self)
                    token_amount: uint256 = msg.value * token_reserve / eth_reserve + 1
                    liquidity_minted: uint256 = msg.value * total_liquidity / eth_reserve
                    assert max_tokens >= token_amount and liquidity_minted >= min_liquidity
                    self.balances[msg.sender] += liquidity_minted
                    self.totalSupply = total_liquidity + liquidity_minted
                    assert self.token.transferFrom(msg.sender, self, token_amount)
                    log.AddLiquidity(msg.sender, msg.value, token_amount)
                    log.Transfer(ZERO_ADDRESS, msg.sender, liquidity_minted)
                    return liquidity_minted
                else:
                    assert (self.factory != ZERO_ADDRESS and self.token != ZERO_ADDRESS) and msg.value >= 1000000000
                    assert self.factory.getExchange(self.token) == self
                    token_amount: uint256 = max_tokens
                    initial_liquidity: uint256 = as_unitless_number(self.balance)
                    self.totalSupply = initial_liquidity
                    self.balances[msg.sender] = initial_liquidity
                    assert self.token.transferFrom(msg.sender, self, token_amount)
                    log.AddLiquidity(msg.sender, msg.value, token_amount)
                    log.Transfer(ZERO_ADDRESS, msg.sender, initial_liquidity)
                    return initial_liquidity
            
            # @dev Burn UNI tokens to withdraw ETH and Tokens at current ratio.
            # @param amount Amount of UNI burned.
            # @param min_eth Minimum ETH withdrawn.
            # @param min_tokens Minimum Tokens withdrawn.
            # @param deadline Time after which this transaction can no longer be executed.
            # @return The amount of ETH and Tokens withdrawn.
            @public
            def removeLiquidity(amount: uint256, min_eth: uint256(wei), min_tokens: uint256, deadline: timestamp) -> (uint256(wei), uint256):
                assert (amount > 0 and deadline > block.timestamp) and (min_eth > 0 and min_tokens > 0)
                total_liquidity: uint256 = self.totalSupply
                assert total_liquidity > 0
                token_reserve: uint256 = self.token.balanceOf(self)
                eth_amount: uint256(wei) = amount * self.balance / total_liquidity
                token_amount: uint256 = amount * token_reserve / total_liquidity
                assert eth_amount >= min_eth and token_amount >= min_tokens
                self.balances[msg.sender] -= amount
                self.totalSupply = total_liquidity - amount
                send(msg.sender, eth_amount)
                assert self.token.transfer(msg.sender, token_amount)
                log.RemoveLiquidity(msg.sender, eth_amount, token_amount)
                log.Transfer(msg.sender, ZERO_ADDRESS, amount)
                return eth_amount, token_amount
            
            # @dev Pricing function for converting between ETH and Tokens.
            # @param input_amount Amount of ETH or Tokens being sold.
            # @param input_reserve Amount of ETH or Tokens (input type) in exchange reserves.
            # @param output_reserve Amount of ETH or Tokens (output type) in exchange reserves.
            # @return Amount of ETH or Tokens bought.
            @private
            @constant
            def getInputPrice(input_amount: uint256, input_reserve: uint256, output_reserve: uint256) -> uint256:
                assert input_reserve > 0 and output_reserve > 0
                input_amount_with_fee: uint256 = input_amount * 997
                numerator: uint256 = input_amount_with_fee * output_reserve
                denominator: uint256 = (input_reserve * 1000) + input_amount_with_fee
                return numerator / denominator
            
            # @dev Pricing function for converting between ETH and Tokens.
            # @param output_amount Amount of ETH or Tokens being bought.
            # @param input_reserve Amount of ETH or Tokens (input type) in exchange reserves.
            # @param output_reserve Amount of ETH or Tokens (output type) in exchange reserves.
            # @return Amount of ETH or Tokens sold.
            @private
            @constant
            def getOutputPrice(output_amount: uint256, input_reserve: uint256, output_reserve: uint256) -> uint256:
                assert input_reserve > 0 and output_reserve > 0
                numerator: uint256 = input_reserve * output_amount * 1000
                denominator: uint256 = (output_reserve - output_amount) * 997
                return numerator / denominator + 1
            
            @private
            def ethToTokenInput(eth_sold: uint256(wei), min_tokens: uint256, deadline: timestamp, buyer: address, recipient: address) -> uint256:
                assert deadline >= block.timestamp and (eth_sold > 0 and min_tokens > 0)
                token_reserve: uint256 = self.token.balanceOf(self)
                tokens_bought: uint256 = self.getInputPrice(as_unitless_number(eth_sold), as_unitless_number(self.balance - eth_sold), token_reserve)
                assert tokens_bought >= min_tokens
                assert self.token.transfer(recipient, tokens_bought)
                log.TokenPurchase(buyer, eth_sold, tokens_bought)
                return tokens_bought
            
            # @notice Convert ETH to Tokens.
            # @dev User specifies exact input (msg.value).
            # @dev User cannot specify minimum output or deadline.
            @public
            @payable
            def __default__():
                self.ethToTokenInput(msg.value, 1, block.timestamp, msg.sender, msg.sender)
            
            # @notice Convert ETH to Tokens.
            # @dev User specifies exact input (msg.value) and minimum output.
            # @param min_tokens Minimum Tokens bought.
            # @param deadline Time after which this transaction can no longer be executed.
            # @return Amount of Tokens bought.
            @public
            @payable
            def ethToTokenSwapInput(min_tokens: uint256, deadline: timestamp) -> uint256:
                return self.ethToTokenInput(msg.value, min_tokens, deadline, msg.sender, msg.sender)
            
            # @notice Convert ETH to Tokens and transfers Tokens to recipient.
            # @dev User specifies exact input (msg.value) and minimum output
            # @param min_tokens Minimum Tokens bought.
            # @param deadline Time after which this transaction can no longer be executed.
            # @param recipient The address that receives output Tokens.
            # @return Amount of Tokens bought.
            @public
            @payable
            def ethToTokenTransferInput(min_tokens: uint256, deadline: timestamp, recipient: address) -> uint256:
                assert recipient != self and recipient != ZERO_ADDRESS
                return self.ethToTokenInput(msg.value, min_tokens, deadline, msg.sender, recipient)
            
            @private
            def ethToTokenOutput(tokens_bought: uint256, max_eth: uint256(wei), deadline: timestamp, buyer: address, recipient: address) -> uint256(wei):
                assert deadline >= block.timestamp and (tokens_bought > 0 and max_eth > 0)
                token_reserve: uint256 = self.token.balanceOf(self)
                eth_sold: uint256 = self.getOutputPrice(tokens_bought, as_unitless_number(self.balance - max_eth), token_reserve)
                # Throws if eth_sold > max_eth
                eth_refund: uint256(wei) = max_eth - as_wei_value(eth_sold, 'wei')
                if eth_refund > 0:
                    send(buyer, eth_refund)
                assert self.token.transfer(recipient, tokens_bought)
                log.TokenPurchase(buyer, as_wei_value(eth_sold, 'wei'), tokens_bought)
                return as_wei_value(eth_sold, 'wei')
            
            # @notice Convert ETH to Tokens.
            # @dev User specifies maximum input (msg.value) and exact output.
            # @param tokens_bought Amount of tokens bought.
            # @param deadline Time after which this transaction can no longer be executed.
            # @return Amount of ETH sold.
            @public
            @payable
            def ethToTokenSwapOutput(tokens_bought: uint256, deadline: timestamp) -> uint256(wei):
                return self.ethToTokenOutput(tokens_bought, msg.value, deadline, msg.sender, msg.sender)
            
            # @notice Convert ETH to Tokens and transfers Tokens to recipient.
            # @dev User specifies maximum input (msg.value) and exact output.
            # @param tokens_bought Amount of tokens bought.
            # @param deadline Time after which this transaction can no longer be executed.
            # @param recipient The address that receives output Tokens.
            # @return Amount of ETH sold.
            @public
            @payable
            def ethToTokenTransferOutput(tokens_bought: uint256, deadline: timestamp, recipient: address) -> uint256(wei):
                assert recipient != self and recipient != ZERO_ADDRESS
                return self.ethToTokenOutput(tokens_bought, msg.value, deadline, msg.sender, recipient)
            
            @private
            def tokenToEthInput(tokens_sold: uint256, min_eth: uint256(wei), deadline: timestamp, buyer: address, recipient: address) -> uint256(wei):
                assert deadline >= block.timestamp and (tokens_sold > 0 and min_eth > 0)
                token_reserve: uint256 = self.token.balanceOf(self)
                eth_bought: uint256 = self.getInputPrice(tokens_sold, token_reserve, as_unitless_number(self.balance))
                wei_bought: uint256(wei) = as_wei_value(eth_bought, 'wei')
                assert wei_bought >= min_eth
                send(recipient, wei_bought)
                assert self.token.transferFrom(buyer, self, tokens_sold)
                log.EthPurchase(buyer, tokens_sold, wei_bought)
                return wei_bought
            
            
            # @notice Convert Tokens to ETH.
            # @dev User specifies exact input and minimum output.
            # @param tokens_sold Amount of Tokens sold.
            # @param min_eth Minimum ETH purchased.
            # @param deadline Time after which this transaction can no longer be executed.
            # @return Amount of ETH bought.
            @public
            def tokenToEthSwapInput(tokens_sold: uint256, min_eth: uint256(wei), deadline: timestamp) -> uint256(wei):
                return self.tokenToEthInput(tokens_sold, min_eth, deadline, msg.sender, msg.sender)
            
            # @notice Convert Tokens to ETH and transfers ETH to recipient.
            # @dev User specifies exact input and minimum output.
            # @param tokens_sold Amount of Tokens sold.
            # @param min_eth Minimum ETH purchased.
            # @param deadline Time after which this transaction can no longer be executed.
            # @param recipient The address that receives output ETH.
            # @return Amount of ETH bought.
            @public
            def tokenToEthTransferInput(tokens_sold: uint256, min_eth: uint256(wei), deadline: timestamp, recipient: address) -> uint256(wei):
                assert recipient != self and recipient != ZERO_ADDRESS
                return self.tokenToEthInput(tokens_sold, min_eth, deadline, msg.sender, recipient)
            
            @private
            def tokenToEthOutput(eth_bought: uint256(wei), max_tokens: uint256, deadline: timestamp, buyer: address, recipient: address) -> uint256:
                assert deadline >= block.timestamp and eth_bought > 0
                token_reserve: uint256 = self.token.balanceOf(self)
                tokens_sold: uint256 = self.getOutputPrice(as_unitless_number(eth_bought), token_reserve, as_unitless_number(self.balance))
                # tokens sold is always > 0
                assert max_tokens >= tokens_sold
                send(recipient, eth_bought)
                assert self.token.transferFrom(buyer, self, tokens_sold)
                log.EthPurchase(buyer, tokens_sold, eth_bought)
                return tokens_sold
            
            # @notice Convert Tokens to ETH.
            # @dev User specifies maximum input and exact output.
            # @param eth_bought Amount of ETH purchased.
            # @param max_tokens Maximum Tokens sold.
            # @param deadline Time after which this transaction can no longer be executed.
            # @return Amount of Tokens sold.
            @public
            def tokenToEthSwapOutput(eth_bought: uint256(wei), max_tokens: uint256, deadline: timestamp) -> uint256:
                return self.tokenToEthOutput(eth_bought, max_tokens, deadline, msg.sender, msg.sender)
            
            # @notice Convert Tokens to ETH and transfers ETH to recipient.
            # @dev User specifies maximum input and exact output.
            # @param eth_bought Amount of ETH purchased.
            # @param max_tokens Maximum Tokens sold.
            # @param deadline Time after which this transaction can no longer be executed.
            # @param recipient The address that receives output ETH.
            # @return Amount of Tokens sold.
            @public
            def tokenToEthTransferOutput(eth_bought: uint256(wei), max_tokens: uint256, deadline: timestamp, recipient: address) -> uint256:
                assert recipient != self and recipient != ZERO_ADDRESS
                return self.tokenToEthOutput(eth_bought, max_tokens, deadline, msg.sender, recipient)
            
            @private
            def tokenToTokenInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_bought: uint256(wei), deadline: timestamp, buyer: address, recipient: address, exchange_addr: address) -> uint256:
                assert (deadline >= block.timestamp and tokens_sold > 0) and (min_tokens_bought > 0 and min_eth_bought > 0)
                assert exchange_addr != self and exchange_addr != ZERO_ADDRESS
                token_reserve: uint256 = self.token.balanceOf(self)
                eth_bought: uint256 = self.getInputPrice(tokens_sold, token_reserve, as_unitless_number(self.balance))
                wei_bought: uint256(wei) = as_wei_value(eth_bought, 'wei')
                assert wei_bought >= min_eth_bought
                assert self.token.transferFrom(buyer, self, tokens_sold)
                tokens_bought: uint256 = Exchange(exchange_addr).ethToTokenTransferInput(min_tokens_bought, deadline, recipient, value=wei_bought)
                log.EthPurchase(buyer, tokens_sold, wei_bought)
                return tokens_bought
            
            # @notice Convert Tokens (self.token) to Tokens (token_addr).
            # @dev User specifies exact input and minimum output.
            # @param tokens_sold Amount of Tokens sold.
            # @param min_tokens_bought Minimum Tokens (token_addr) purchased.
            # @param min_eth_bought Minimum ETH purchased as intermediary.
            # @param deadline Time after which this transaction can no longer be executed.
            # @param token_addr The address of the token being purchased.
            # @return Amount of Tokens (token_addr) bought.
            @public
            def tokenToTokenSwapInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_bought: uint256(wei), deadline: timestamp, token_addr: address) -> uint256:
                exchange_addr: address = self.factory.getExchange(token_addr)
                return self.tokenToTokenInput(tokens_sold, min_tokens_bought, min_eth_bought, deadline, msg.sender, msg.sender, exchange_addr)
            
            # @notice Convert Tokens (self.token) to Tokens (token_addr) and transfers
            #         Tokens (token_addr) to recipient.
            # @dev User specifies exact input and minimum output.
            # @param tokens_sold Amount of Tokens sold.
            # @param min_tokens_bought Minimum Tokens (token_addr) purchased.
            # @param min_eth_bought Minimum ETH purchased as intermediary.
            # @param deadline Time after which this transaction can no longer be executed.
            # @param recipient The address that receives output ETH.
            # @param token_addr The address of the token being purchased.
            # @return Amount of Tokens (token_addr) bought.
            @public
            def tokenToTokenTransferInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_bought: uint256(wei), deadline: timestamp, recipient: address, token_addr: address) -> uint256:
                exchange_addr: address = self.factory.getExchange(token_addr)
                return self.tokenToTokenInput(tokens_sold, min_tokens_bought, min_eth_bought, deadline, msg.sender, recipient, exchange_addr)
            
            @private
            def tokenToTokenOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth_sold: uint256(wei), deadline: timestamp, buyer: address, recipient: address, exchange_addr: address) -> uint256:
                assert deadline >= block.timestamp and (tokens_bought > 0 and max_eth_sold > 0)
                assert exchange_addr != self and exchange_addr != ZERO_ADDRESS
                eth_bought: uint256(wei) = Exchange(exchange_addr).getEthToTokenOutputPrice(tokens_bought)
                token_reserve: uint256 = self.token.balanceOf(self)
                tokens_sold: uint256 = self.getOutputPrice(as_unitless_number(eth_bought), token_reserve, as_unitless_number(self.balance))
                # tokens sold is always > 0
                assert max_tokens_sold >= tokens_sold and max_eth_sold >= eth_bought
                assert self.token.transferFrom(buyer, self, tokens_sold)
                eth_sold: uint256(wei) = Exchange(exchange_addr).ethToTokenTransferOutput(tokens_bought, deadline, recipient, value=eth_bought)
                log.EthPurchase(buyer, tokens_sold, eth_bought)
                return tokens_sold
            
            # @notice Convert Tokens (self.token) to Tokens (token_addr).
            # @dev User specifies maximum input and exact output.
            # @param tokens_bought Amount of Tokens (token_addr) bought.
            # @param max_tokens_sold Maximum Tokens (self.token) sold.
            # @param max_eth_sold Maximum ETH purchased as intermediary.
            # @param deadline Time after which this transaction can no longer be executed.
            # @param token_addr The address of the token being purchased.
            # @return Amount of Tokens (self.token) sold.
            @public
            def tokenToTokenSwapOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth_sold: uint256(wei), deadline: timestamp, token_addr: address) -> uint256:
                exchange_addr: address = self.factory.getExchange(token_addr)
                return self.tokenToTokenOutput(tokens_bought, max_tokens_sold, max_eth_sold, deadline, msg.sender, msg.sender, exchange_addr)
            
            # @notice Convert Tokens (self.token) to Tokens (token_addr) and transfers
            #         Tokens (token_addr) to recipient.
            # @dev User specifies maximum input and exact output.
            # @param tokens_bought Amount of Tokens (token_addr) bought.
            # @param max_tokens_sold Maximum Tokens (self.token) sold.
            # @param max_eth_sold Maximum ETH purchased as intermediary.
            # @param deadline Time after which this transaction can no longer be executed.
            # @param recipient The address that receives output ETH.
            # @param token_addr The address of the token being purchased.
            # @return Amount of Tokens (self.token) sold.
            @public
            def tokenToTokenTransferOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth_sold: uint256(wei), deadline: timestamp, recipient: address, token_addr: address) -> uint256:
                exchange_addr: address = self.factory.getExchange(token_addr)
                return self.tokenToTokenOutput(tokens_bought, max_tokens_sold, max_eth_sold, deadline, msg.sender, recipient, exchange_addr)
            
            # @notice Convert Tokens (self.token) to Tokens (exchange_addr.token).
            # @dev Allows trades through contracts that were not deployed from the same factory.
            # @dev User specifies exact input and minimum output.
            # @param tokens_sold Amount of Tokens sold.
            # @param min_tokens_bought Minimum Tokens (token_addr) purchased.
            # @param min_eth_bought Minimum ETH purchased as intermediary.
            # @param deadline Time after which this transaction can no longer be executed.
            # @param exchange_addr The address of the exchange for the token being purchased.
            # @return Amount of Tokens (exchange_addr.token) bought.
            @public
            def tokenToExchangeSwapInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_bought: uint256(wei), deadline: timestamp, exchange_addr: address) -> uint256:
                return self.tokenToTokenInput(tokens_sold, min_tokens_bought, min_eth_bought, deadline, msg.sender, msg.sender, exchange_addr)
            
            # @notice Convert Tokens (self.token) to Tokens (exchange_addr.token) and transfers
            #         Tokens (exchange_addr.token) to recipient.
            # @dev Allows trades through contracts that were not deployed from the same factory.
            # @dev User specifies exact input and minimum output.
            # @param tokens_sold Amount of Tokens sold.
            # @param min_tokens_bought Minimum Tokens (token_addr) purchased.
            # @param min_eth_bought Minimum ETH purchased as intermediary.
            # @param deadline Time after which this transaction can no longer be executed.
            # @param recipient The address that receives output ETH.
            # @param exchange_addr The address of the exchange for the token being purchased.
            # @return Amount of Tokens (exchange_addr.token) bought.
            @public
            def tokenToExchangeTransferInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_bought: uint256(wei), deadline: timestamp, recipient: address, exchange_addr: address) -> uint256:
                assert recipient != self
                return self.tokenToTokenInput(tokens_sold, min_tokens_bought, min_eth_bought, deadline, msg.sender, recipient, exchange_addr)
            
            # @notice Convert Tokens (self.token) to Tokens (exchange_addr.token).
            # @dev Allows trades through contracts that were not deployed from the same factory.
            # @dev User specifies maximum input and exact output.
            # @param tokens_bought Amount of Tokens (token_addr) bought.
            # @param max_tokens_sold Maximum Tokens (self.token) sold.
            # @param max_eth_sold Maximum ETH purchased as intermediary.
            # @param deadline Time after which this transaction can no longer be executed.
            # @param exchange_addr The address of the exchange for the token being purchased.
            # @return Amount of Tokens (self.token) sold.
            @public
            def tokenToExchangeSwapOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth_sold: uint256(wei), deadline: timestamp, exchange_addr: address) -> uint256:
                return self.tokenToTokenOutput(tokens_bought, max_tokens_sold, max_eth_sold, deadline, msg.sender, msg.sender, exchange_addr)
            
            # @notice Convert Tokens (self.token) to Tokens (exchange_addr.token) and transfers
            #         Tokens (exchange_addr.token) to recipient.
            # @dev Allows trades through contracts that were not deployed from the same factory.
            # @dev User specifies maximum input and exact output.
            # @param tokens_bought Amount of Tokens (token_addr) bought.
            # @param max_tokens_sold Maximum Tokens (self.token) sold.
            # @param max_eth_sold Maximum ETH purchased as intermediary.
            # @param deadline Time after which this transaction can no longer be executed.
            # @param recipient The address that receives output ETH.
            # @param token_addr The address of the token being purchased.
            # @return Amount of Tokens (self.token) sold.
            @public
            def tokenToExchangeTransferOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth_sold: uint256(wei), deadline: timestamp, recipient: address, exchange_addr: address) -> uint256:
                assert recipient != self
                return self.tokenToTokenOutput(tokens_bought, max_tokens_sold, max_eth_sold, deadline, msg.sender, recipient, exchange_addr)
            
            # @notice Public price function for ETH to Token trades with an exact input.
            # @param eth_sold Amount of ETH sold.
            # @return Amount of Tokens that can be bought with input ETH.
            @public
            @constant
            def getEthToTokenInputPrice(eth_sold: uint256(wei)) -> uint256:
                assert eth_sold > 0
                token_reserve: uint256 = self.token.balanceOf(self)
                return self.getInputPrice(as_unitless_number(eth_sold), as_unitless_number(self.balance), token_reserve)
            
            # @notice Public price function for ETH to Token trades with an exact output.
            # @param tokens_bought Amount of Tokens bought.
            # @return Amount of ETH needed to buy output Tokens.
            @public
            @constant
            def getEthToTokenOutputPrice(tokens_bought: uint256) -> uint256(wei):
                assert tokens_bought > 0
                token_reserve: uint256 = self.token.balanceOf(self)
                eth_sold: uint256 = self.getOutputPrice(tokens_bought, as_unitless_number(self.balance), token_reserve)
                return as_wei_value(eth_sold, 'wei')
            
            # @notice Public price function for Token to ETH trades with an exact input.
            # @param tokens_sold Amount of Tokens sold.
            # @return Amount of ETH that can be bought with input Tokens.
            @public
            @constant
            def getTokenToEthInputPrice(tokens_sold: uint256) -> uint256(wei):
                assert tokens_sold > 0
                token_reserve: uint256 = self.token.balanceOf(self)
                eth_bought: uint256 = self.getInputPrice(tokens_sold, token_reserve, as_unitless_number(self.balance))
                return as_wei_value(eth_bought, 'wei')
            
            # @notice Public price function for Token to ETH trades with an exact output.
            # @param eth_bought Amount of output ETH.
            # @return Amount of Tokens needed to buy output ETH.
            @public
            @constant
            def getTokenToEthOutputPrice(eth_bought: uint256(wei)) -> uint256:
                assert eth_bought > 0
                token_reserve: uint256 = self.token.balanceOf(self)
                return self.getOutputPrice(as_unitless_number(eth_bought), token_reserve, as_unitless_number(self.balance))
            
            # @return Address of Token that is sold on this exchange.
            @public
            @constant
            def tokenAddress() -> address:
                return self.token
            
            # @return Address of factory that created this exchange.
            @public
            @constant
            def factoryAddress() -> address(Factory):
                return self.factory
            
            # ERC20 compatibility for exchange liquidity modified from
            # https://github.com/ethereum/vyper/blob/master/examples/tokens/ERC20.vy
            @public
            @constant
            def balanceOf(_owner : address) -> uint256:
                return self.balances[_owner]
            
            @public
            def transfer(_to : address, _value : uint256) -> bool:
                self.balances[msg.sender] -= _value
                self.balances[_to] += _value
                log.Transfer(msg.sender, _to, _value)
                return True
            
            @public
            def transferFrom(_from : address, _to : address, _value : uint256) -> bool:
                self.balances[_from] -= _value
                self.balances[_to] += _value
                self.allowances[_from][msg.sender] -= _value
                log.Transfer(_from, _to, _value)
                return True
            
            @public
            def approve(_spender : address, _value : uint256) -> bool:
                self.allowances[msg.sender][_spender] = _value
                log.Approval(msg.sender, _spender, _value)
                return True
            
            @public
            @constant
            def allowance(_owner : address, _spender : address) -> uint256:
                return self.allowances[_owner][_spender]

            File 5 of 7: FiatTokenV1
            pragma solidity ^0.4.24;
            
            // File: contracts/Ownable.sol
            
            /**
            * Copyright CENTRE SECZ 2018
            *
            * Permission is hereby granted, free of charge, to any person obtaining a copy 
            * of this software and associated documentation files (the "Software"), to deal 
            * in the Software without restriction, including without limitation the rights 
            * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
            * copies of the Software, and to permit persons to whom the Software is furnished to 
            * do so, subject to the following conditions:
            *
            * The above copyright notice and this permission notice shall be included in all 
            * copies or substantial portions of the Software.
            *
            * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
            * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
            * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
            * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
            * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
            * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
            */
            
            pragma solidity ^0.4.24;
            
            /**
             * @title Ownable
             * @dev The Ownable contract from https://github.com/zeppelinos/labs/blob/master/upgradeability_ownership/contracts/ownership/Ownable.sol 
             * branch: master commit: 3887ab77b8adafba4a26ace002f3a684c1a3388b modified to:
             * 1) Add emit prefix to OwnershipTransferred event (7/13/18)
             * 2) Replace constructor with constructor syntax (7/13/18)
             * 3) consolidate OwnableStorage into this contract
             */
            contract Ownable {
            
              // Owner of the contract
              address private _owner;
            
              /**
              * @dev Event to show ownership has been transferred
              * @param previousOwner representing the address of the previous owner
              * @param newOwner representing the address of the new owner
              */
              event OwnershipTransferred(address previousOwner, address newOwner);
            
              /**
              * @dev The constructor sets the original owner of the contract to the sender account.
              */
              constructor() public {
                setOwner(msg.sender);
              }
            
              /**
             * @dev Tells the address of the owner
             * @return the address of the owner
             */
              function owner() public view returns (address) {
                return _owner;
              }
            
              /**
               * @dev Sets a new owner address
               */
              function setOwner(address newOwner) internal {
                _owner = newOwner;
              }
            
              /**
              * @dev Throws if called by any account other than the owner.
              */
              modifier onlyOwner() {
                require(msg.sender == owner());
                _;
              }
            
              /**
               * @dev Allows the current owner to transfer control of the contract to a newOwner.
               * @param newOwner The address to transfer ownership to.
               */
              function transferOwnership(address newOwner) public onlyOwner {
                require(newOwner != address(0));
                emit OwnershipTransferred(owner(), newOwner);
                setOwner(newOwner);
              }
            }
            
            // File: contracts/Blacklistable.sol
            
            /**
            * Copyright CENTRE SECZ 2018
            *
            * Permission is hereby granted, free of charge, to any person obtaining a copy 
            * of this software and associated documentation files (the "Software"), to deal 
            * in the Software without restriction, including without limitation the rights 
            * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
            * copies of the Software, and to permit persons to whom the Software is furnished to 
            * do so, subject to the following conditions:
            *
            * The above copyright notice and this permission notice shall be included in all 
            * copies or substantial portions of the Software.
            *
            * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
            * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
            * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
            * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
            * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
            * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
            */
            
            pragma solidity ^0.4.24;
            
            
            /**
             * @title Blacklistable Token
             * @dev Allows accounts to be blacklisted by a "blacklister" role
            */
            contract Blacklistable is Ownable {
            
                address public blacklister;
                mapping(address => bool) internal blacklisted;
            
                event Blacklisted(address indexed _account);
                event UnBlacklisted(address indexed _account);
                event BlacklisterChanged(address indexed newBlacklister);
            
                /**
                 * @dev Throws if called by any account other than the blacklister
                */
                modifier onlyBlacklister() {
                    require(msg.sender == blacklister);
                    _;
                }
            
                /**
                 * @dev Throws if argument account is blacklisted
                 * @param _account The address to check
                */
                modifier notBlacklisted(address _account) {
                    require(blacklisted[_account] == false);
                    _;
                }
            
                /**
                 * @dev Checks if account is blacklisted
                 * @param _account The address to check    
                */
                function isBlacklisted(address _account) public view returns (bool) {
                    return blacklisted[_account];
                }
            
                /**
                 * @dev Adds account to blacklist
                 * @param _account The address to blacklist
                */
                function blacklist(address _account) public onlyBlacklister {
                    blacklisted[_account] = true;
                    emit Blacklisted(_account);
                }
            
                /**
                 * @dev Removes account from blacklist
                 * @param _account The address to remove from the blacklist
                */
                function unBlacklist(address _account) public onlyBlacklister {
                    blacklisted[_account] = false;
                    emit UnBlacklisted(_account);
                }
            
                function updateBlacklister(address _newBlacklister) public onlyOwner {
                    require(_newBlacklister != address(0));
                    blacklister = _newBlacklister;
                    emit BlacklisterChanged(blacklister);
                }
            }
            
            // File: contracts/Pausable.sol
            
            /**
            * Copyright CENTRE SECZ 2018
            *
            * Permission is hereby granted, free of charge, to any person obtaining a copy 
            * of this software and associated documentation files (the "Software"), to deal 
            * in the Software without restriction, including without limitation the rights 
            * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
            * copies of the Software, and to permit persons to whom the Software is furnished to 
            * do so, subject to the following conditions:
            *
            * The above copyright notice and this permission notice shall be included in all 
            * copies or substantial portions of the Software.
            *
            * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
            * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
            * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
            * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
            * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
            * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
            */
            
            pragma solidity ^0.4.24;
            
            
            /**
             * @title Pausable
             * @dev Base contract which allows children to implement an emergency stop mechanism.
             * Based on openzeppelin tag v1.10.0 commit: feb665136c0dae9912e08397c1a21c4af3651ef3
             * Modifications:
             * 1) Added pauser role, switched pause/unpause to be onlyPauser (6/14/2018)
             * 2) Removed whenNotPause/whenPaused from pause/unpause (6/14/2018)
             * 3) Removed whenPaused (6/14/2018)
             * 4) Switches ownable library to use zeppelinos (7/12/18)
             * 5) Remove constructor (7/13/18)
             */
            contract Pausable is Ownable {
              event Pause();
              event Unpause();
              event PauserChanged(address indexed newAddress);
            
            
              address public pauser;
              bool public paused = false;
            
              /**
               * @dev Modifier to make a function callable only when the contract is not paused.
               */
              modifier whenNotPaused() {
                require(!paused);
                _;
              }
            
              /**
               * @dev throws if called by any account other than the pauser
               */
              modifier onlyPauser() {
                require(msg.sender == pauser);
                _;
              }
            
              /**
               * @dev called by the owner to pause, triggers stopped state
               */
              function pause() onlyPauser public {
                paused = true;
                emit Pause();
              }
            
              /**
               * @dev called by the owner to unpause, returns to normal state
               */
              function unpause() onlyPauser public {
                paused = false;
                emit Unpause();
              }
            
              /**
               * @dev update the pauser role
               */
              function updatePauser(address _newPauser) onlyOwner public {
                require(_newPauser != address(0));
                pauser = _newPauser;
                emit PauserChanged(pauser);
              }
            
            }
            
            // File: openzeppelin-solidity/contracts/math/SafeMath.sol
            
            /**
             * @title SafeMath
             * @dev Math operations with safety checks that throw on error
             */
            library SafeMath {
            
              /**
              * @dev Multiplies two numbers, throws on overflow.
              */
              function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
                // Gas optimization: this is cheaper than asserting 'a' not being zero, but the
                // benefit is lost if 'b' is also tested.
                // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
                if (a == 0) {
                  return 0;
                }
            
                c = a * b;
                assert(c / a == b);
                return c;
              }
            
              /**
              * @dev Integer division of two numbers, truncating the quotient.
              */
              function div(uint256 a, uint256 b) internal pure returns (uint256) {
                // assert(b > 0); // Solidity automatically throws when dividing by 0
                // uint256 c = a / b;
                // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                return a / b;
              }
            
              /**
              * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
              */
              function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                assert(b <= a);
                return a - b;
              }
            
              /**
              * @dev Adds two numbers, throws on overflow.
              */
              function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
                c = a + b;
                assert(c >= a);
                return c;
              }
            }
            
            // File: openzeppelin-solidity/contracts/token/ERC20/ERC20Basic.sol
            
            /**
             * @title ERC20Basic
             * @dev Simpler version of ERC20 interface
             * See https://github.com/ethereum/EIPs/issues/179
             */
            contract ERC20Basic {
              function totalSupply() public view returns (uint256);
              function balanceOf(address who) public view returns (uint256);
              function transfer(address to, uint256 value) public returns (bool);
              event Transfer(address indexed from, address indexed to, uint256 value);
            }
            
            // File: openzeppelin-solidity/contracts/token/ERC20/ERC20.sol
            
            /**
             * @title ERC20 interface
             * @dev see https://github.com/ethereum/EIPs/issues/20
             */
            contract ERC20 is ERC20Basic {
              function allowance(address owner, address spender)
                public view returns (uint256);
            
              function transferFrom(address from, address to, uint256 value)
                public returns (bool);
            
              function approve(address spender, uint256 value) public returns (bool);
              event Approval(
                address indexed owner,
                address indexed spender,
                uint256 value
              );
            }
            
            // File: contracts/FiatTokenV1.sol
            
            /**
            * Copyright CENTRE SECZ 2018
            *
            * Permission is hereby granted, free of charge, to any person obtaining a copy 
            * of this software and associated documentation files (the "Software"), to deal 
            * in the Software without restriction, including without limitation the rights 
            * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
            * copies of the Software, and to permit persons to whom the Software is furnished to 
            * do so, subject to the following conditions:
            *
            * The above copyright notice and this permission notice shall be included in all 
            * copies or substantial portions of the Software.
            *
            * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
            * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
            * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
            * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
            * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
            * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
            */
            
            pragma solidity ^0.4.24;
            
            
            
            
            
            
            /**
             * @title FiatToken
             * @dev ERC20 Token backed by fiat reserves
             */
            contract FiatTokenV1 is Ownable, ERC20, Pausable, Blacklistable {
                using SafeMath for uint256;
            
                string public name;
                string public symbol;
                uint8 public decimals;
                string public currency;
                address public masterMinter;
                bool internal initialized;
            
                mapping(address => uint256) internal balances;
                mapping(address => mapping(address => uint256)) internal allowed;
                uint256 internal totalSupply_ = 0;
                mapping(address => bool) internal minters;
                mapping(address => uint256) internal minterAllowed;
            
                event Mint(address indexed minter, address indexed to, uint256 amount);
                event Burn(address indexed burner, uint256 amount);
                event MinterConfigured(address indexed minter, uint256 minterAllowedAmount);
                event MinterRemoved(address indexed oldMinter);
                event MasterMinterChanged(address indexed newMasterMinter);
            
                function initialize(
                    string _name,
                    string _symbol,
                    string _currency,
                    uint8 _decimals,
                    address _masterMinter,
                    address _pauser,
                    address _blacklister,
                    address _owner
                ) public {
                    require(!initialized);
                    require(_masterMinter != address(0));
                    require(_pauser != address(0));
                    require(_blacklister != address(0));
                    require(_owner != address(0));
            
                    name = _name;
                    symbol = _symbol;
                    currency = _currency;
                    decimals = _decimals;
                    masterMinter = _masterMinter;
                    pauser = _pauser;
                    blacklister = _blacklister;
                    setOwner(_owner);
                    initialized = true;
                }
            
                /**
                 * @dev Throws if called by any account other than a minter
                */
                modifier onlyMinters() {
                    require(minters[msg.sender] == true);
                    _;
                }
            
                /**
                 * @dev Function to mint tokens
                 * @param _to The address that will receive the minted tokens.
                 * @param _amount The amount of tokens to mint. Must be less than or equal to the minterAllowance of the caller.
                 * @return A boolean that indicates if the operation was successful.
                */
                function mint(address _to, uint256 _amount) whenNotPaused onlyMinters notBlacklisted(msg.sender) notBlacklisted(_to) public returns (bool) {
                    require(_to != address(0));
                    require(_amount > 0);
            
                    uint256 mintingAllowedAmount = minterAllowed[msg.sender];
                    require(_amount <= mintingAllowedAmount);
            
                    totalSupply_ = totalSupply_.add(_amount);
                    balances[_to] = balances[_to].add(_amount);
                    minterAllowed[msg.sender] = mintingAllowedAmount.sub(_amount);
                    emit Mint(msg.sender, _to, _amount);
                    emit Transfer(0x0, _to, _amount);
                    return true;
                }
            
                /**
                 * @dev Throws if called by any account other than the masterMinter
                */
                modifier onlyMasterMinter() {
                    require(msg.sender == masterMinter);
                    _;
                }
            
                /**
                 * @dev Get minter allowance for an account
                 * @param minter The address of the minter
                */
                function minterAllowance(address minter) public view returns (uint256) {
                    return minterAllowed[minter];
                }
            
                /**
                 * @dev Checks if account is a minter
                 * @param account The address to check    
                */
                function isMinter(address account) public view returns (bool) {
                    return minters[account];
                }
            
                /**
                 * @dev Get allowed amount for an account
                 * @param owner address The account owner
                 * @param spender address The account spender
                */
                function allowance(address owner, address spender) public view returns (uint256) {
                    return allowed[owner][spender];
                }
            
                /**
                 * @dev Get totalSupply of token
                */
                function totalSupply() public view returns (uint256) {
                    return totalSupply_;
                }
            
                /**
                 * @dev Get token balance of an account
                 * @param account address The account
                */
                function balanceOf(address account) public view returns (uint256) {
                    return balances[account];
                }
            
                /**
                 * @dev Adds blacklisted check to approve
                 * @return True if the operation was successful.
                */
                function approve(address _spender, uint256 _value) whenNotPaused notBlacklisted(msg.sender) notBlacklisted(_spender) public returns (bool) {
                    allowed[msg.sender][_spender] = _value;
                    emit Approval(msg.sender, _spender, _value);
                    return true;
                }
            
                /**
                 * @dev Transfer tokens from one address to another.
                 * @param _from address The address which you want to send tokens from
                 * @param _to address The address which you want to transfer to
                 * @param _value uint256 the amount of tokens to be transferred
                 * @return bool success
                */
                function transferFrom(address _from, address _to, uint256 _value) whenNotPaused notBlacklisted(_to) notBlacklisted(msg.sender) notBlacklisted(_from) public returns (bool) {
                    require(_to != address(0));
                    require(_value <= balances[_from]);
                    require(_value <= allowed[_from][msg.sender]);
            
                    balances[_from] = balances[_from].sub(_value);
                    balances[_to] = balances[_to].add(_value);
                    allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
                    emit Transfer(_from, _to, _value);
                    return true;
                }
            
                /**
                 * @dev transfer token for a specified address
                 * @param _to The address to transfer to.
                 * @param _value The amount to be transferred.
                 * @return bool success
                */
                function transfer(address _to, uint256 _value) whenNotPaused notBlacklisted(msg.sender) notBlacklisted(_to) public returns (bool) {
                    require(_to != address(0));
                    require(_value <= balances[msg.sender]);
            
                    balances[msg.sender] = balances[msg.sender].sub(_value);
                    balances[_to] = balances[_to].add(_value);
                    emit Transfer(msg.sender, _to, _value);
                    return true;
                }
            
                /**
                 * @dev Function to add/update a new minter
                 * @param minter The address of the minter
                 * @param minterAllowedAmount The minting amount allowed for the minter
                 * @return True if the operation was successful.
                */
                function configureMinter(address minter, uint256 minterAllowedAmount) whenNotPaused onlyMasterMinter public returns (bool) {
                    minters[minter] = true;
                    minterAllowed[minter] = minterAllowedAmount;
                    emit MinterConfigured(minter, minterAllowedAmount);
                    return true;
                }
            
                /**
                 * @dev Function to remove a minter
                 * @param minter The address of the minter to remove
                 * @return True if the operation was successful.
                */
                function removeMinter(address minter) onlyMasterMinter public returns (bool) {
                    minters[minter] = false;
                    minterAllowed[minter] = 0;
                    emit MinterRemoved(minter);
                    return true;
                }
            
                /**
                 * @dev allows a minter to burn some of its own tokens
                 * Validates that caller is a minter and that sender is not blacklisted
                 * amount is less than or equal to the minter's account balance
                 * @param _amount uint256 the amount of tokens to be burned
                */
                function burn(uint256 _amount) whenNotPaused onlyMinters notBlacklisted(msg.sender) public {
                    uint256 balance = balances[msg.sender];
                    require(_amount > 0);
                    require(balance >= _amount);
            
                    totalSupply_ = totalSupply_.sub(_amount);
                    balances[msg.sender] = balance.sub(_amount);
                    emit Burn(msg.sender, _amount);
                    emit Transfer(msg.sender, address(0), _amount);
                }
            
                function updateMasterMinter(address _newMasterMinter) onlyOwner public {
                    require(_newMasterMinter != address(0));
                    masterMinter = _newMasterMinter;
                    emit MasterMinterChanged(masterMinter);
                }
            }

            File 6 of 7: Vyper_contract
            contract Exchange():
                def setup(token_addr: address): modifying
            
            NewExchange: event({token: indexed(address), exchange: indexed(address)})
            
            exchangeTemplate: public(address)
            tokenCount: public(uint256)
            token_to_exchange: address[address]
            exchange_to_token: address[address]
            id_to_token: address[uint256]
            
            @public
            def initializeFactory(template: address):
                assert self.exchangeTemplate == ZERO_ADDRESS
                assert template != ZERO_ADDRESS
                self.exchangeTemplate = template
            
            @public
            def createExchange(token: address) -> address:
                assert token != ZERO_ADDRESS
                assert self.exchangeTemplate != ZERO_ADDRESS
                assert self.token_to_exchange[token] == ZERO_ADDRESS
                exchange: address = create_with_code_of(self.exchangeTemplate)
                Exchange(exchange).setup(token)
                self.token_to_exchange[token] = exchange
                self.exchange_to_token[exchange] = token
                token_id: uint256 = self.tokenCount + 1
                self.tokenCount = token_id
                self.id_to_token[token_id] = token
                log.NewExchange(token, exchange)
                return exchange
            
            @public
            @constant
            def getExchange(token: address) -> address:
                return self.token_to_exchange[token]
            
            @public
            @constant
            def getToken(exchange: address) -> address:
                return self.exchange_to_token[exchange]
            
            @public
            @constant
            def getTokenWithId(token_id: uint256) -> address:
                return self.id_to_token[token_id]

            File 7 of 7: Vyper_contract
            # @title Uniswap Exchange Interface V1
            # @notice Source code found at https://github.com/uniswap
            # @notice Use at your own risk
            
            contract Factory():
                def getExchange(token_addr: address) -> address: constant
            
            contract Exchange():
                def getEthToTokenOutputPrice(tokens_bought: uint256) -> uint256(wei): constant
                def ethToTokenTransferInput(min_tokens: uint256, deadline: timestamp, recipient: address) -> uint256: modifying
                def ethToTokenTransferOutput(tokens_bought: uint256, deadline: timestamp, recipient: address) -> uint256(wei): modifying
            
            TokenPurchase: event({buyer: indexed(address), eth_sold: indexed(uint256(wei)), tokens_bought: indexed(uint256)})
            EthPurchase: event({buyer: indexed(address), tokens_sold: indexed(uint256), eth_bought: indexed(uint256(wei))})
            AddLiquidity: event({provider: indexed(address), eth_amount: indexed(uint256(wei)), token_amount: indexed(uint256)})
            RemoveLiquidity: event({provider: indexed(address), eth_amount: indexed(uint256(wei)), token_amount: indexed(uint256)})
            Transfer: event({_from: indexed(address), _to: indexed(address), _value: uint256})
            Approval: event({_owner: indexed(address), _spender: indexed(address), _value: uint256})
            
            name: public(bytes32)                             # Uniswap V1
            symbol: public(bytes32)                           # UNI-V1
            decimals: public(uint256)                         # 18
            totalSupply: public(uint256)                      # total number of UNI in existence
            balances: uint256[address]                        # UNI balance of an address
            allowances: (uint256[address])[address]           # UNI allowance of one address on another
            token: address(ERC20)                             # address of the ERC20 token traded on this contract
            factory: Factory                                  # interface for the factory that created this contract
            
            # @dev This function acts as a contract constructor which is not currently supported in contracts deployed
            #      using create_with_code_of(). It is called once by the factory during contract creation.
            @public
            def setup(token_addr: address):
                assert (self.factory == ZERO_ADDRESS and self.token == ZERO_ADDRESS) and token_addr != ZERO_ADDRESS
                self.factory = msg.sender
                self.token = token_addr
                self.name = 0x556e697377617020563100000000000000000000000000000000000000000000
                self.symbol = 0x554e492d56310000000000000000000000000000000000000000000000000000
                self.decimals = 18
            
            # @notice Deposit ETH and Tokens (self.token) at current ratio to mint UNI tokens.
            # @dev min_liquidity does nothing when total UNI supply is 0.
            # @param min_liquidity Minimum number of UNI sender will mint if total UNI supply is greater than 0.
            # @param max_tokens Maximum number of tokens deposited. Deposits max amount if total UNI supply is 0.
            # @param deadline Time after which this transaction can no longer be executed.
            # @return The amount of UNI minted.
            @public
            @payable
            def addLiquidity(min_liquidity: uint256, max_tokens: uint256, deadline: timestamp) -> uint256:
                assert deadline > block.timestamp and (max_tokens > 0 and msg.value > 0)
                total_liquidity: uint256 = self.totalSupply
                if total_liquidity > 0:
                    assert min_liquidity > 0
                    eth_reserve: uint256(wei) = self.balance - msg.value
                    token_reserve: uint256 = self.token.balanceOf(self)
                    token_amount: uint256 = msg.value * token_reserve / eth_reserve + 1
                    liquidity_minted: uint256 = msg.value * total_liquidity / eth_reserve
                    assert max_tokens >= token_amount and liquidity_minted >= min_liquidity
                    self.balances[msg.sender] += liquidity_minted
                    self.totalSupply = total_liquidity + liquidity_minted
                    assert self.token.transferFrom(msg.sender, self, token_amount)
                    log.AddLiquidity(msg.sender, msg.value, token_amount)
                    log.Transfer(ZERO_ADDRESS, msg.sender, liquidity_minted)
                    return liquidity_minted
                else:
                    assert (self.factory != ZERO_ADDRESS and self.token != ZERO_ADDRESS) and msg.value >= 1000000000
                    assert self.factory.getExchange(self.token) == self
                    token_amount: uint256 = max_tokens
                    initial_liquidity: uint256 = as_unitless_number(self.balance)
                    self.totalSupply = initial_liquidity
                    self.balances[msg.sender] = initial_liquidity
                    assert self.token.transferFrom(msg.sender, self, token_amount)
                    log.AddLiquidity(msg.sender, msg.value, token_amount)
                    log.Transfer(ZERO_ADDRESS, msg.sender, initial_liquidity)
                    return initial_liquidity
            
            # @dev Burn UNI tokens to withdraw ETH and Tokens at current ratio.
            # @param amount Amount of UNI burned.
            # @param min_eth Minimum ETH withdrawn.
            # @param min_tokens Minimum Tokens withdrawn.
            # @param deadline Time after which this transaction can no longer be executed.
            # @return The amount of ETH and Tokens withdrawn.
            @public
            def removeLiquidity(amount: uint256, min_eth: uint256(wei), min_tokens: uint256, deadline: timestamp) -> (uint256(wei), uint256):
                assert (amount > 0 and deadline > block.timestamp) and (min_eth > 0 and min_tokens > 0)
                total_liquidity: uint256 = self.totalSupply
                assert total_liquidity > 0
                token_reserve: uint256 = self.token.balanceOf(self)
                eth_amount: uint256(wei) = amount * self.balance / total_liquidity
                token_amount: uint256 = amount * token_reserve / total_liquidity
                assert eth_amount >= min_eth and token_amount >= min_tokens
                self.balances[msg.sender] -= amount
                self.totalSupply = total_liquidity - amount
                send(msg.sender, eth_amount)
                assert self.token.transfer(msg.sender, token_amount)
                log.RemoveLiquidity(msg.sender, eth_amount, token_amount)
                log.Transfer(msg.sender, ZERO_ADDRESS, amount)
                return eth_amount, token_amount
            
            # @dev Pricing function for converting between ETH and Tokens.
            # @param input_amount Amount of ETH or Tokens being sold.
            # @param input_reserve Amount of ETH or Tokens (input type) in exchange reserves.
            # @param output_reserve Amount of ETH or Tokens (output type) in exchange reserves.
            # @return Amount of ETH or Tokens bought.
            @private
            @constant
            def getInputPrice(input_amount: uint256, input_reserve: uint256, output_reserve: uint256) -> uint256:
                assert input_reserve > 0 and output_reserve > 0
                input_amount_with_fee: uint256 = input_amount * 997
                numerator: uint256 = input_amount_with_fee * output_reserve
                denominator: uint256 = (input_reserve * 1000) + input_amount_with_fee
                return numerator / denominator
            
            # @dev Pricing function for converting between ETH and Tokens.
            # @param output_amount Amount of ETH or Tokens being bought.
            # @param input_reserve Amount of ETH or Tokens (input type) in exchange reserves.
            # @param output_reserve Amount of ETH or Tokens (output type) in exchange reserves.
            # @return Amount of ETH or Tokens sold.
            @private
            @constant
            def getOutputPrice(output_amount: uint256, input_reserve: uint256, output_reserve: uint256) -> uint256:
                assert input_reserve > 0 and output_reserve > 0
                numerator: uint256 = input_reserve * output_amount * 1000
                denominator: uint256 = (output_reserve - output_amount) * 997
                return numerator / denominator + 1
            
            @private
            def ethToTokenInput(eth_sold: uint256(wei), min_tokens: uint256, deadline: timestamp, buyer: address, recipient: address) -> uint256:
                assert deadline >= block.timestamp and (eth_sold > 0 and min_tokens > 0)
                token_reserve: uint256 = self.token.balanceOf(self)
                tokens_bought: uint256 = self.getInputPrice(as_unitless_number(eth_sold), as_unitless_number(self.balance - eth_sold), token_reserve)
                assert tokens_bought >= min_tokens
                assert self.token.transfer(recipient, tokens_bought)
                log.TokenPurchase(buyer, eth_sold, tokens_bought)
                return tokens_bought
            
            # @notice Convert ETH to Tokens.
            # @dev User specifies exact input (msg.value).
            # @dev User cannot specify minimum output or deadline.
            @public
            @payable
            def __default__():
                self.ethToTokenInput(msg.value, 1, block.timestamp, msg.sender, msg.sender)
            
            # @notice Convert ETH to Tokens.
            # @dev User specifies exact input (msg.value) and minimum output.
            # @param min_tokens Minimum Tokens bought.
            # @param deadline Time after which this transaction can no longer be executed.
            # @return Amount of Tokens bought.
            @public
            @payable
            def ethToTokenSwapInput(min_tokens: uint256, deadline: timestamp) -> uint256:
                return self.ethToTokenInput(msg.value, min_tokens, deadline, msg.sender, msg.sender)
            
            # @notice Convert ETH to Tokens and transfers Tokens to recipient.
            # @dev User specifies exact input (msg.value) and minimum output
            # @param min_tokens Minimum Tokens bought.
            # @param deadline Time after which this transaction can no longer be executed.
            # @param recipient The address that receives output Tokens.
            # @return Amount of Tokens bought.
            @public
            @payable
            def ethToTokenTransferInput(min_tokens: uint256, deadline: timestamp, recipient: address) -> uint256:
                assert recipient != self and recipient != ZERO_ADDRESS
                return self.ethToTokenInput(msg.value, min_tokens, deadline, msg.sender, recipient)
            
            @private
            def ethToTokenOutput(tokens_bought: uint256, max_eth: uint256(wei), deadline: timestamp, buyer: address, recipient: address) -> uint256(wei):
                assert deadline >= block.timestamp and (tokens_bought > 0 and max_eth > 0)
                token_reserve: uint256 = self.token.balanceOf(self)
                eth_sold: uint256 = self.getOutputPrice(tokens_bought, as_unitless_number(self.balance - max_eth), token_reserve)
                # Throws if eth_sold > max_eth
                eth_refund: uint256(wei) = max_eth - as_wei_value(eth_sold, 'wei')
                if eth_refund > 0:
                    send(buyer, eth_refund)
                assert self.token.transfer(recipient, tokens_bought)
                log.TokenPurchase(buyer, as_wei_value(eth_sold, 'wei'), tokens_bought)
                return as_wei_value(eth_sold, 'wei')
            
            # @notice Convert ETH to Tokens.
            # @dev User specifies maximum input (msg.value) and exact output.
            # @param tokens_bought Amount of tokens bought.
            # @param deadline Time after which this transaction can no longer be executed.
            # @return Amount of ETH sold.
            @public
            @payable
            def ethToTokenSwapOutput(tokens_bought: uint256, deadline: timestamp) -> uint256(wei):
                return self.ethToTokenOutput(tokens_bought, msg.value, deadline, msg.sender, msg.sender)
            
            # @notice Convert ETH to Tokens and transfers Tokens to recipient.
            # @dev User specifies maximum input (msg.value) and exact output.
            # @param tokens_bought Amount of tokens bought.
            # @param deadline Time after which this transaction can no longer be executed.
            # @param recipient The address that receives output Tokens.
            # @return Amount of ETH sold.
            @public
            @payable
            def ethToTokenTransferOutput(tokens_bought: uint256, deadline: timestamp, recipient: address) -> uint256(wei):
                assert recipient != self and recipient != ZERO_ADDRESS
                return self.ethToTokenOutput(tokens_bought, msg.value, deadline, msg.sender, recipient)
            
            @private
            def tokenToEthInput(tokens_sold: uint256, min_eth: uint256(wei), deadline: timestamp, buyer: address, recipient: address) -> uint256(wei):
                assert deadline >= block.timestamp and (tokens_sold > 0 and min_eth > 0)
                token_reserve: uint256 = self.token.balanceOf(self)
                eth_bought: uint256 = self.getInputPrice(tokens_sold, token_reserve, as_unitless_number(self.balance))
                wei_bought: uint256(wei) = as_wei_value(eth_bought, 'wei')
                assert wei_bought >= min_eth
                send(recipient, wei_bought)
                assert self.token.transferFrom(buyer, self, tokens_sold)
                log.EthPurchase(buyer, tokens_sold, wei_bought)
                return wei_bought
            
            
            # @notice Convert Tokens to ETH.
            # @dev User specifies exact input and minimum output.
            # @param tokens_sold Amount of Tokens sold.
            # @param min_eth Minimum ETH purchased.
            # @param deadline Time after which this transaction can no longer be executed.
            # @return Amount of ETH bought.
            @public
            def tokenToEthSwapInput(tokens_sold: uint256, min_eth: uint256(wei), deadline: timestamp) -> uint256(wei):
                return self.tokenToEthInput(tokens_sold, min_eth, deadline, msg.sender, msg.sender)
            
            # @notice Convert Tokens to ETH and transfers ETH to recipient.
            # @dev User specifies exact input and minimum output.
            # @param tokens_sold Amount of Tokens sold.
            # @param min_eth Minimum ETH purchased.
            # @param deadline Time after which this transaction can no longer be executed.
            # @param recipient The address that receives output ETH.
            # @return Amount of ETH bought.
            @public
            def tokenToEthTransferInput(tokens_sold: uint256, min_eth: uint256(wei), deadline: timestamp, recipient: address) -> uint256(wei):
                assert recipient != self and recipient != ZERO_ADDRESS
                return self.tokenToEthInput(tokens_sold, min_eth, deadline, msg.sender, recipient)
            
            @private
            def tokenToEthOutput(eth_bought: uint256(wei), max_tokens: uint256, deadline: timestamp, buyer: address, recipient: address) -> uint256:
                assert deadline >= block.timestamp and eth_bought > 0
                token_reserve: uint256 = self.token.balanceOf(self)
                tokens_sold: uint256 = self.getOutputPrice(as_unitless_number(eth_bought), token_reserve, as_unitless_number(self.balance))
                # tokens sold is always > 0
                assert max_tokens >= tokens_sold
                send(recipient, eth_bought)
                assert self.token.transferFrom(buyer, self, tokens_sold)
                log.EthPurchase(buyer, tokens_sold, eth_bought)
                return tokens_sold
            
            # @notice Convert Tokens to ETH.
            # @dev User specifies maximum input and exact output.
            # @param eth_bought Amount of ETH purchased.
            # @param max_tokens Maximum Tokens sold.
            # @param deadline Time after which this transaction can no longer be executed.
            # @return Amount of Tokens sold.
            @public
            def tokenToEthSwapOutput(eth_bought: uint256(wei), max_tokens: uint256, deadline: timestamp) -> uint256:
                return self.tokenToEthOutput(eth_bought, max_tokens, deadline, msg.sender, msg.sender)
            
            # @notice Convert Tokens to ETH and transfers ETH to recipient.
            # @dev User specifies maximum input and exact output.
            # @param eth_bought Amount of ETH purchased.
            # @param max_tokens Maximum Tokens sold.
            # @param deadline Time after which this transaction can no longer be executed.
            # @param recipient The address that receives output ETH.
            # @return Amount of Tokens sold.
            @public
            def tokenToEthTransferOutput(eth_bought: uint256(wei), max_tokens: uint256, deadline: timestamp, recipient: address) -> uint256:
                assert recipient != self and recipient != ZERO_ADDRESS
                return self.tokenToEthOutput(eth_bought, max_tokens, deadline, msg.sender, recipient)
            
            @private
            def tokenToTokenInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_bought: uint256(wei), deadline: timestamp, buyer: address, recipient: address, exchange_addr: address) -> uint256:
                assert (deadline >= block.timestamp and tokens_sold > 0) and (min_tokens_bought > 0 and min_eth_bought > 0)
                assert exchange_addr != self and exchange_addr != ZERO_ADDRESS
                token_reserve: uint256 = self.token.balanceOf(self)
                eth_bought: uint256 = self.getInputPrice(tokens_sold, token_reserve, as_unitless_number(self.balance))
                wei_bought: uint256(wei) = as_wei_value(eth_bought, 'wei')
                assert wei_bought >= min_eth_bought
                assert self.token.transferFrom(buyer, self, tokens_sold)
                tokens_bought: uint256 = Exchange(exchange_addr).ethToTokenTransferInput(min_tokens_bought, deadline, recipient, value=wei_bought)
                log.EthPurchase(buyer, tokens_sold, wei_bought)
                return tokens_bought
            
            # @notice Convert Tokens (self.token) to Tokens (token_addr).
            # @dev User specifies exact input and minimum output.
            # @param tokens_sold Amount of Tokens sold.
            # @param min_tokens_bought Minimum Tokens (token_addr) purchased.
            # @param min_eth_bought Minimum ETH purchased as intermediary.
            # @param deadline Time after which this transaction can no longer be executed.
            # @param token_addr The address of the token being purchased.
            # @return Amount of Tokens (token_addr) bought.
            @public
            def tokenToTokenSwapInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_bought: uint256(wei), deadline: timestamp, token_addr: address) -> uint256:
                exchange_addr: address = self.factory.getExchange(token_addr)
                return self.tokenToTokenInput(tokens_sold, min_tokens_bought, min_eth_bought, deadline, msg.sender, msg.sender, exchange_addr)
            
            # @notice Convert Tokens (self.token) to Tokens (token_addr) and transfers
            #         Tokens (token_addr) to recipient.
            # @dev User specifies exact input and minimum output.
            # @param tokens_sold Amount of Tokens sold.
            # @param min_tokens_bought Minimum Tokens (token_addr) purchased.
            # @param min_eth_bought Minimum ETH purchased as intermediary.
            # @param deadline Time after which this transaction can no longer be executed.
            # @param recipient The address that receives output ETH.
            # @param token_addr The address of the token being purchased.
            # @return Amount of Tokens (token_addr) bought.
            @public
            def tokenToTokenTransferInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_bought: uint256(wei), deadline: timestamp, recipient: address, token_addr: address) -> uint256:
                exchange_addr: address = self.factory.getExchange(token_addr)
                return self.tokenToTokenInput(tokens_sold, min_tokens_bought, min_eth_bought, deadline, msg.sender, recipient, exchange_addr)
            
            @private
            def tokenToTokenOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth_sold: uint256(wei), deadline: timestamp, buyer: address, recipient: address, exchange_addr: address) -> uint256:
                assert deadline >= block.timestamp and (tokens_bought > 0 and max_eth_sold > 0)
                assert exchange_addr != self and exchange_addr != ZERO_ADDRESS
                eth_bought: uint256(wei) = Exchange(exchange_addr).getEthToTokenOutputPrice(tokens_bought)
                token_reserve: uint256 = self.token.balanceOf(self)
                tokens_sold: uint256 = self.getOutputPrice(as_unitless_number(eth_bought), token_reserve, as_unitless_number(self.balance))
                # tokens sold is always > 0
                assert max_tokens_sold >= tokens_sold and max_eth_sold >= eth_bought
                assert self.token.transferFrom(buyer, self, tokens_sold)
                eth_sold: uint256(wei) = Exchange(exchange_addr).ethToTokenTransferOutput(tokens_bought, deadline, recipient, value=eth_bought)
                log.EthPurchase(buyer, tokens_sold, eth_bought)
                return tokens_sold
            
            # @notice Convert Tokens (self.token) to Tokens (token_addr).
            # @dev User specifies maximum input and exact output.
            # @param tokens_bought Amount of Tokens (token_addr) bought.
            # @param max_tokens_sold Maximum Tokens (self.token) sold.
            # @param max_eth_sold Maximum ETH purchased as intermediary.
            # @param deadline Time after which this transaction can no longer be executed.
            # @param token_addr The address of the token being purchased.
            # @return Amount of Tokens (self.token) sold.
            @public
            def tokenToTokenSwapOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth_sold: uint256(wei), deadline: timestamp, token_addr: address) -> uint256:
                exchange_addr: address = self.factory.getExchange(token_addr)
                return self.tokenToTokenOutput(tokens_bought, max_tokens_sold, max_eth_sold, deadline, msg.sender, msg.sender, exchange_addr)
            
            # @notice Convert Tokens (self.token) to Tokens (token_addr) and transfers
            #         Tokens (token_addr) to recipient.
            # @dev User specifies maximum input and exact output.
            # @param tokens_bought Amount of Tokens (token_addr) bought.
            # @param max_tokens_sold Maximum Tokens (self.token) sold.
            # @param max_eth_sold Maximum ETH purchased as intermediary.
            # @param deadline Time after which this transaction can no longer be executed.
            # @param recipient The address that receives output ETH.
            # @param token_addr The address of the token being purchased.
            # @return Amount of Tokens (self.token) sold.
            @public
            def tokenToTokenTransferOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth_sold: uint256(wei), deadline: timestamp, recipient: address, token_addr: address) -> uint256:
                exchange_addr: address = self.factory.getExchange(token_addr)
                return self.tokenToTokenOutput(tokens_bought, max_tokens_sold, max_eth_sold, deadline, msg.sender, recipient, exchange_addr)
            
            # @notice Convert Tokens (self.token) to Tokens (exchange_addr.token).
            # @dev Allows trades through contracts that were not deployed from the same factory.
            # @dev User specifies exact input and minimum output.
            # @param tokens_sold Amount of Tokens sold.
            # @param min_tokens_bought Minimum Tokens (token_addr) purchased.
            # @param min_eth_bought Minimum ETH purchased as intermediary.
            # @param deadline Time after which this transaction can no longer be executed.
            # @param exchange_addr The address of the exchange for the token being purchased.
            # @return Amount of Tokens (exchange_addr.token) bought.
            @public
            def tokenToExchangeSwapInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_bought: uint256(wei), deadline: timestamp, exchange_addr: address) -> uint256:
                return self.tokenToTokenInput(tokens_sold, min_tokens_bought, min_eth_bought, deadline, msg.sender, msg.sender, exchange_addr)
            
            # @notice Convert Tokens (self.token) to Tokens (exchange_addr.token) and transfers
            #         Tokens (exchange_addr.token) to recipient.
            # @dev Allows trades through contracts that were not deployed from the same factory.
            # @dev User specifies exact input and minimum output.
            # @param tokens_sold Amount of Tokens sold.
            # @param min_tokens_bought Minimum Tokens (token_addr) purchased.
            # @param min_eth_bought Minimum ETH purchased as intermediary.
            # @param deadline Time after which this transaction can no longer be executed.
            # @param recipient The address that receives output ETH.
            # @param exchange_addr The address of the exchange for the token being purchased.
            # @return Amount of Tokens (exchange_addr.token) bought.
            @public
            def tokenToExchangeTransferInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_bought: uint256(wei), deadline: timestamp, recipient: address, exchange_addr: address) -> uint256:
                assert recipient != self
                return self.tokenToTokenInput(tokens_sold, min_tokens_bought, min_eth_bought, deadline, msg.sender, recipient, exchange_addr)
            
            # @notice Convert Tokens (self.token) to Tokens (exchange_addr.token).
            # @dev Allows trades through contracts that were not deployed from the same factory.
            # @dev User specifies maximum input and exact output.
            # @param tokens_bought Amount of Tokens (token_addr) bought.
            # @param max_tokens_sold Maximum Tokens (self.token) sold.
            # @param max_eth_sold Maximum ETH purchased as intermediary.
            # @param deadline Time after which this transaction can no longer be executed.
            # @param exchange_addr The address of the exchange for the token being purchased.
            # @return Amount of Tokens (self.token) sold.
            @public
            def tokenToExchangeSwapOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth_sold: uint256(wei), deadline: timestamp, exchange_addr: address) -> uint256:
                return self.tokenToTokenOutput(tokens_bought, max_tokens_sold, max_eth_sold, deadline, msg.sender, msg.sender, exchange_addr)
            
            # @notice Convert Tokens (self.token) to Tokens (exchange_addr.token) and transfers
            #         Tokens (exchange_addr.token) to recipient.
            # @dev Allows trades through contracts that were not deployed from the same factory.
            # @dev User specifies maximum input and exact output.
            # @param tokens_bought Amount of Tokens (token_addr) bought.
            # @param max_tokens_sold Maximum Tokens (self.token) sold.
            # @param max_eth_sold Maximum ETH purchased as intermediary.
            # @param deadline Time after which this transaction can no longer be executed.
            # @param recipient The address that receives output ETH.
            # @param token_addr The address of the token being purchased.
            # @return Amount of Tokens (self.token) sold.
            @public
            def tokenToExchangeTransferOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth_sold: uint256(wei), deadline: timestamp, recipient: address, exchange_addr: address) -> uint256:
                assert recipient != self
                return self.tokenToTokenOutput(tokens_bought, max_tokens_sold, max_eth_sold, deadline, msg.sender, recipient, exchange_addr)
            
            # @notice Public price function for ETH to Token trades with an exact input.
            # @param eth_sold Amount of ETH sold.
            # @return Amount of Tokens that can be bought with input ETH.
            @public
            @constant
            def getEthToTokenInputPrice(eth_sold: uint256(wei)) -> uint256:
                assert eth_sold > 0
                token_reserve: uint256 = self.token.balanceOf(self)
                return self.getInputPrice(as_unitless_number(eth_sold), as_unitless_number(self.balance), token_reserve)
            
            # @notice Public price function for ETH to Token trades with an exact output.
            # @param tokens_bought Amount of Tokens bought.
            # @return Amount of ETH needed to buy output Tokens.
            @public
            @constant
            def getEthToTokenOutputPrice(tokens_bought: uint256) -> uint256(wei):
                assert tokens_bought > 0
                token_reserve: uint256 = self.token.balanceOf(self)
                eth_sold: uint256 = self.getOutputPrice(tokens_bought, as_unitless_number(self.balance), token_reserve)
                return as_wei_value(eth_sold, 'wei')
            
            # @notice Public price function for Token to ETH trades with an exact input.
            # @param tokens_sold Amount of Tokens sold.
            # @return Amount of ETH that can be bought with input Tokens.
            @public
            @constant
            def getTokenToEthInputPrice(tokens_sold: uint256) -> uint256(wei):
                assert tokens_sold > 0
                token_reserve: uint256 = self.token.balanceOf(self)
                eth_bought: uint256 = self.getInputPrice(tokens_sold, token_reserve, as_unitless_number(self.balance))
                return as_wei_value(eth_bought, 'wei')
            
            # @notice Public price function for Token to ETH trades with an exact output.
            # @param eth_bought Amount of output ETH.
            # @return Amount of Tokens needed to buy output ETH.
            @public
            @constant
            def getTokenToEthOutputPrice(eth_bought: uint256(wei)) -> uint256:
                assert eth_bought > 0
                token_reserve: uint256 = self.token.balanceOf(self)
                return self.getOutputPrice(as_unitless_number(eth_bought), token_reserve, as_unitless_number(self.balance))
            
            # @return Address of Token that is sold on this exchange.
            @public
            @constant
            def tokenAddress() -> address:
                return self.token
            
            # @return Address of factory that created this exchange.
            @public
            @constant
            def factoryAddress() -> address(Factory):
                return self.factory
            
            # ERC20 compatibility for exchange liquidity modified from
            # https://github.com/ethereum/vyper/blob/master/examples/tokens/ERC20.vy
            @public
            @constant
            def balanceOf(_owner : address) -> uint256:
                return self.balances[_owner]
            
            @public
            def transfer(_to : address, _value : uint256) -> bool:
                self.balances[msg.sender] -= _value
                self.balances[_to] += _value
                log.Transfer(msg.sender, _to, _value)
                return True
            
            @public
            def transferFrom(_from : address, _to : address, _value : uint256) -> bool:
                self.balances[_from] -= _value
                self.balances[_to] += _value
                self.allowances[_from][msg.sender] -= _value
                log.Transfer(_from, _to, _value)
                return True
            
            @public
            def approve(_spender : address, _value : uint256) -> bool:
                self.allowances[msg.sender][_spender] = _value
                log.Approval(msg.sender, _spender, _value)
                return True
            
            @public
            @constant
            def allowance(_owner : address, _spender : address) -> uint256:
                return self.allowances[_owner][_spender]