ETH Price: $2,499.86 (+0.18%)

Transaction Decoder

Block:
11314948 at Nov-23-2020 02:13:09 PM +UTC
Transaction Fee:
0.009415532434 ETH $23.54
Gas Used:
84,062 Gas / 112.007 Gwei

Emitted Events:

261 CentrallyIssuedToken.Transfer( from=[Sender] 0x6e7ea33a90719bdec3886d0eebbfdcaf88f6893a, to=0xe4e8F73096301B0938B896a79521088633B85B75, value=200060000000 )
262 CentrallyIssuedToken.Approval( owner=0xe4e8F73096301B0938B896a79521088633B85B75, spender=Vyper_contract, value=200060000000 )
263 CentrallyIssuedToken.Transfer( from=0xe4e8F73096301B0938B896a79521088633B85B75, to=Vyper_contract, value=200060000000 )
264 Vyper_contract.EthPurchase( buyer=0xe4e8F73096301B0938B896a79521088633B85B75, tokens_sold=200060000000, eth_bought=276479787832991910 )
265 OneInchExchange.Swapped( sender=[Sender] 0x6e7ea33a90719bdec3886d0eebbfdcaf88f6893a, srcToken=CentrallyIssuedToken, dstToken=0xEeeeeEee...eeeeeEEeE, dstReceiver=[Sender] 0x6e7ea33a90719bdec3886d0eebbfdcaf88f6893a, amount=200060000000, spentAmount=200060000000, returnAmount=276479787832991910, minReturnAmount=268185394198002152, guaranteedAmount=276479787832991910, referrer=0x00000000...000000000 )

Account State Difference:

  Address   Before After State Difference Code
0x1C6c712b...c945d3b9a 2.189242022751960501 Eth1.912762234918968591 Eth0.27647978783299191
0x41e55600...E20e94E45
0x6E7eA33a...f88F6893A
0.458375619242913024 Eth
Nonce: 129
0.725439874641904934 Eth
Nonce: 130
0.26706425539899191
(Babel Pool)
8,439.32807583311021071 Eth8,439.33749136554421071 Eth0.009415532434

Execution Trace

OneInchExchange.swap( caller=0xe4e8F73096301B0938B896a79521088633B85B75, desc=[{name:srcToken, type:address, order:1, indexed:false, value:0x41e5560054824eA6B0732E656E3Ad64E20e94E45, valueString:0x41e5560054824eA6B0732E656E3Ad64E20e94E45}, {name:dstToken, type:address, order:2, indexed:false, value:0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, valueString:0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE}, {name:srcReceiver, type:address, order:3, indexed:false, value:0xe4e8F73096301B0938B896a79521088633B85B75, valueString:0xe4e8F73096301B0938B896a79521088633B85B75}, {name:dstReceiver, type:address, order:4, indexed:false, value:0x6E7eA33a90719BDEC3886d0EEbBFdcAf88F6893A, valueString:0x6E7eA33a90719BDEC3886d0EEbBFdcAf88F6893A}, {name:amount, type:uint256, order:5, indexed:false, value:200060000000, valueString:200060000000}, {name:minReturnAmount, type:uint256, order:6, indexed:false, value:268185394198002152, valueString:268185394198002152}, {name:guaranteedAmount, type:uint256, order:7, indexed:false, value:276479787832991910, valueString:276479787832991910}, {name:flags, type:uint256, order:8, indexed:false, value:4, valueString:4}, {name:referrer, type:address, order:9, indexed:false, value:0x0000000000000000000000000000000000000000, valueString:0x0000000000000000000000000000000000000000}, {name:permit, type:bytes, order:10, indexed:false, value:0x, valueString:0x}], calls= ) => ( returnAmount=276479787832991910 )
  • CentrallyIssuedToken.transferFrom( _from=0x6E7eA33a90719BDEC3886d0EEbBFdcAf88F6893A, _to=0xe4e8F73096301B0938B896a79521088633B85B75, _value=200060000000 ) => ( success=True )
  • 0xe4e8f73096301b0938b896a79521088633b85b75.a8920d2b( )
    • 0xe4e8f73096301b0938b896a79521088633b85b75.eb5625d9( )
      • CentrallyIssuedToken.allowance( _owner=0xe4e8F73096301B0938B896a79521088633B85B75, _spender=0x1C6c712b1F4a7c263B1DBd8F97fb447c945d3b9a ) => ( remaining=0 )
      • CentrallyIssuedToken.allowance( _owner=0xe4e8F73096301B0938B896a79521088633B85B75, _spender=0x1C6c712b1F4a7c263B1DBd8F97fb447c945d3b9a ) => ( remaining=0 )
      • CentrallyIssuedToken.approve( _spender=0x1C6c712b1F4a7c263B1DBd8F97fb447c945d3b9a, _value=200060000000 ) => ( success=True )
      • Vyper_contract.tokenToEthTransferInput( tokens_sold=200060000000, min_eth=1, deadline=1606227183, recipient=0x6E7eA33a90719BDEC3886d0EEbBFdcAf88F6893A ) => ( out=276479787832991910 )
        • Vyper_contract.tokenToEthTransferInput( tokens_sold=200060000000, min_eth=1, deadline=1606227183, recipient=0x6E7eA33a90719BDEC3886d0EEbBFdcAf88F6893A ) => ( out=276479787832991910 )
          • CentrallyIssuedToken.balanceOf( _owner=0x1C6c712b1F4a7c263B1DBd8F97fb447c945d3b9a ) => ( balance=1379917186967 )
          • ETH 0.27647978783299191 0x6e7ea33a90719bdec3886d0eebbfdcaf88f6893a.CALL( )
          • CentrallyIssuedToken.transferFrom( _from=0xe4e8F73096301B0938B896a79521088633B85B75, _to=0x1C6c712b1F4a7c263B1DBd8F97fb447c945d3b9a, _value=200060000000 ) => ( success=True )
            File 1 of 4: OneInchExchange
            // File: @openzeppelin/contracts/GSN/Context.sol
            
            // SPDX-License-Identifier: MIT
            
            pragma solidity ^0.6.0;
            
            /*
             * @dev Provides information about the current execution context, including the
             * sender of the transaction and its data. While these are generally available
             * via msg.sender and msg.data, they should not be accessed in such a direct
             * manner, since when dealing with GSN meta-transactions the account sending and
             * paying for execution may not be the actual sender (as far as an application
             * is concerned).
             *
             * This contract is only required for intermediate, library-like contracts.
             */
            abstract contract Context {
                function _msgSender() internal view virtual returns (address payable) {
                    return msg.sender;
                }
            
                function _msgData() internal view virtual returns (bytes memory) {
                    this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                    return msg.data;
                }
            }
            
            // File: @openzeppelin/contracts/access/Ownable.sol
            
            
            pragma solidity ^0.6.0;
            
            /**
             * @dev Contract module which provides a basic access control mechanism, where
             * there is an account (an owner) that can be granted exclusive access to
             * specific functions.
             *
             * By default, the owner account will be the one that deploys the contract. This
             * can later be changed with {transferOwnership}.
             *
             * This module is used through inheritance. It will make available the modifier
             * `onlyOwner`, which can be applied to your functions to restrict their use to
             * the owner.
             */
            contract Ownable is Context {
                address private _owner;
            
                event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
            
                /**
                 * @dev Initializes the contract setting the deployer as the initial owner.
                 */
                constructor () internal {
                    address msgSender = _msgSender();
                    _owner = msgSender;
                    emit OwnershipTransferred(address(0), msgSender);
                }
            
                /**
                 * @dev Returns the address of the current owner.
                 */
                function owner() public view returns (address) {
                    return _owner;
                }
            
                /**
                 * @dev Throws if called by any account other than the owner.
                 */
                modifier onlyOwner() {
                    require(_owner == _msgSender(), "Ownable: caller is not the owner");
                    _;
                }
            
                /**
                 * @dev Leaves the contract without owner. It will not be possible to call
                 * `onlyOwner` functions anymore. Can only be called by the current owner.
                 *
                 * NOTE: Renouncing ownership will leave the contract without an owner,
                 * thereby removing any functionality that is only available to the owner.
                 */
                function renounceOwnership() public virtual onlyOwner {
                    emit OwnershipTransferred(_owner, address(0));
                    _owner = address(0);
                }
            
                /**
                 * @dev Transfers ownership of the contract to a new account (`newOwner`).
                 * Can only be called by the current owner.
                 */
                function transferOwnership(address newOwner) public virtual onlyOwner {
                    require(newOwner != address(0), "Ownable: new owner is the zero address");
                    emit OwnershipTransferred(_owner, newOwner);
                    _owner = newOwner;
                }
            }
            
            // File: @openzeppelin/contracts/token/ERC20/IERC20.sol
            
            
            pragma solidity ^0.6.0;
            
            /**
             * @dev Interface of the ERC20 standard as defined in the EIP.
             */
            interface IERC20 {
                /**
                 * @dev Returns the amount of tokens in existence.
                 */
                function totalSupply() external view returns (uint256);
            
                /**
                 * @dev Returns the amount of tokens owned by `account`.
                 */
                function balanceOf(address account) external view returns (uint256);
            
                /**
                 * @dev Moves `amount` tokens from the caller's account to `recipient`.
                 *
                 * Returns a boolean value indicating whether the operation succeeded.
                 *
                 * Emits a {Transfer} event.
                 */
                function transfer(address recipient, uint256 amount) external returns (bool);
            
                /**
                 * @dev Returns the remaining number of tokens that `spender` will be
                 * allowed to spend on behalf of `owner` through {transferFrom}. This is
                 * zero by default.
                 *
                 * This value changes when {approve} or {transferFrom} are called.
                 */
                function allowance(address owner, address spender) external view returns (uint256);
            
                /**
                 * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                 *
                 * Returns a boolean value indicating whether the operation succeeded.
                 *
                 * IMPORTANT: Beware that changing an allowance with this method brings the risk
                 * that someone may use both the old and the new allowance by unfortunate
                 * transaction ordering. One possible solution to mitigate this race
                 * condition is to first reduce the spender's allowance to 0 and set the
                 * desired value afterwards:
                 * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                 *
                 * Emits an {Approval} event.
                 */
                function approve(address spender, uint256 amount) external returns (bool);
            
                /**
                 * @dev Moves `amount` tokens from `sender` to `recipient` using the
                 * allowance mechanism. `amount` is then deducted from the caller's
                 * allowance.
                 *
                 * Returns a boolean value indicating whether the operation succeeded.
                 *
                 * Emits a {Transfer} event.
                 */
                function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
            
                /**
                 * @dev Emitted when `value` tokens are moved from one account (`from`) to
                 * another (`to`).
                 *
                 * Note that `value` may be zero.
                 */
                event Transfer(address indexed from, address indexed to, uint256 value);
            
                /**
                 * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                 * a call to {approve}. `value` is the new allowance.
                 */
                event Approval(address indexed owner, address indexed spender, uint256 value);
            }
            
            // File: @openzeppelin/contracts/math/SafeMath.sol
            
            
            pragma solidity ^0.6.0;
            
            /**
             * @dev Wrappers over Solidity's arithmetic operations with added overflow
             * checks.
             *
             * Arithmetic operations in Solidity wrap on overflow. This can easily result
             * in bugs, because programmers usually assume that an overflow raises an
             * error, which is the standard behavior in high level programming languages.
             * `SafeMath` restores this intuition by reverting the transaction when an
             * operation overflows.
             *
             * Using this library instead of the unchecked operations eliminates an entire
             * class of bugs, so it's recommended to use it always.
             */
            library SafeMath {
                /**
                 * @dev Returns the addition of two unsigned integers, reverting on
                 * overflow.
                 *
                 * Counterpart to Solidity's `+` operator.
                 *
                 * Requirements:
                 *
                 * - Addition cannot overflow.
                 */
                function add(uint256 a, uint256 b) internal pure returns (uint256) {
                    uint256 c = a + b;
                    require(c >= a, "SafeMath: addition overflow");
            
                    return c;
                }
            
                /**
                 * @dev Returns the subtraction of two unsigned integers, reverting on
                 * overflow (when the result is negative).
                 *
                 * Counterpart to Solidity's `-` operator.
                 *
                 * Requirements:
                 *
                 * - Subtraction cannot overflow.
                 */
                function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                    return sub(a, b, "SafeMath: subtraction overflow");
                }
            
                /**
                 * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                 * overflow (when the result is negative).
                 *
                 * Counterpart to Solidity's `-` operator.
                 *
                 * Requirements:
                 *
                 * - Subtraction cannot overflow.
                 */
                function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                    require(b <= a, errorMessage);
                    uint256 c = a - b;
            
                    return c;
                }
            
                /**
                 * @dev Returns the multiplication of two unsigned integers, reverting on
                 * overflow.
                 *
                 * Counterpart to Solidity's `*` operator.
                 *
                 * Requirements:
                 *
                 * - Multiplication cannot overflow.
                 */
                function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                    // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                    // benefit is lost if 'b' is also tested.
                    // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                    if (a == 0) {
                        return 0;
                    }
            
                    uint256 c = a * b;
                    require(c / a == b, "SafeMath: multiplication overflow");
            
                    return c;
                }
            
                /**
                 * @dev Returns the integer division of two unsigned integers. Reverts on
                 * division by zero. The result is rounded towards zero.
                 *
                 * Counterpart to Solidity's `/` operator. Note: this function uses a
                 * `revert` opcode (which leaves remaining gas untouched) while Solidity
                 * uses an invalid opcode to revert (consuming all remaining gas).
                 *
                 * Requirements:
                 *
                 * - The divisor cannot be zero.
                 */
                function div(uint256 a, uint256 b) internal pure returns (uint256) {
                    return div(a, b, "SafeMath: division by zero");
                }
            
                /**
                 * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
                 * division by zero. The result is rounded towards zero.
                 *
                 * Counterpart to Solidity's `/` operator. Note: this function uses a
                 * `revert` opcode (which leaves remaining gas untouched) while Solidity
                 * uses an invalid opcode to revert (consuming all remaining gas).
                 *
                 * Requirements:
                 *
                 * - The divisor cannot be zero.
                 */
                function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                    require(b > 0, errorMessage);
                    uint256 c = a / b;
                    // assert(a == b * c + a % b); // There is no case in which this doesn't hold
            
                    return c;
                }
            
                /**
                 * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                 * Reverts when dividing by zero.
                 *
                 * Counterpart to Solidity's `%` operator. This function uses a `revert`
                 * opcode (which leaves remaining gas untouched) while Solidity uses an
                 * invalid opcode to revert (consuming all remaining gas).
                 *
                 * Requirements:
                 *
                 * - The divisor cannot be zero.
                 */
                function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                    return mod(a, b, "SafeMath: modulo by zero");
                }
            
                /**
                 * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                 * Reverts with custom message when dividing by zero.
                 *
                 * Counterpart to Solidity's `%` operator. This function uses a `revert`
                 * opcode (which leaves remaining gas untouched) while Solidity uses an
                 * invalid opcode to revert (consuming all remaining gas).
                 *
                 * Requirements:
                 *
                 * - The divisor cannot be zero.
                 */
                function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                    require(b != 0, errorMessage);
                    return a % b;
                }
            }
            
            // File: @openzeppelin/contracts/utils/Address.sol
            
            
            pragma solidity ^0.6.2;
            
            /**
             * @dev Collection of functions related to the address type
             */
            library Address {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                    // This method relies in extcodesize, which returns 0 for contracts in
                    // construction, since the code is only stored at the end of the
                    // constructor execution.
            
                    uint256 size;
                    // solhint-disable-next-line no-inline-assembly
                    assembly { size := extcodesize(account) }
                    return size > 0;
                }
            
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                    require(address(this).balance >= amount, "Address: insufficient balance");
            
                    // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                    (bool success, ) = recipient.call{ value: amount }("");
                    require(success, "Address: unable to send value, recipient may have reverted");
                }
            
                /**
                 * @dev Performs a Solidity function call using a low level `call`. A
                 * plain`call` is an unsafe replacement for a function call: use this
                 * function instead.
                 *
                 * If `target` reverts with a revert reason, it is bubbled up by this
                 * function (like regular Solidity function calls).
                 *
                 * Returns the raw returned data. To convert to the expected return value,
                 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                 *
                 * Requirements:
                 *
                 * - `target` must be a contract.
                 * - calling `target` with `data` must not revert.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                  return functionCall(target, data, "Address: low-level call failed");
                }
            
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                 * `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                    return _functionCallWithValue(target, data, 0, errorMessage);
                }
            
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but also transferring `value` wei to `target`.
                 *
                 * Requirements:
                 *
                 * - the calling contract must have an ETH balance of at least `value`.
                 * - the called Solidity function must be `payable`.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                }
            
                /**
                 * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                 * with `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
                    require(address(this).balance >= value, "Address: insufficient balance for call");
                    return _functionCallWithValue(target, data, value, errorMessage);
                }
            
                function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
                    require(isContract(target), "Address: call to non-contract");
            
                    // solhint-disable-next-line avoid-low-level-calls
                    (bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
                    if (success) {
                        return returndata;
                    } else {
                        // Look for revert reason and bubble it up if present
                        if (returndata.length > 0) {
                            // The easiest way to bubble the revert reason is using memory via assembly
            
                            // solhint-disable-next-line no-inline-assembly
                            assembly {
                                let returndata_size := mload(returndata)
                                revert(add(32, returndata), returndata_size)
                            }
                        } else {
                            revert(errorMessage);
                        }
                    }
                }
            }
            
            // File: @openzeppelin/contracts/token/ERC20/SafeERC20.sol
            
            
            pragma solidity ^0.6.0;
            
            
            
            
            /**
             * @title SafeERC20
             * @dev Wrappers around ERC20 operations that throw on failure (when the token
             * contract returns false). Tokens that return no value (and instead revert or
             * throw on failure) are also supported, non-reverting calls are assumed to be
             * successful.
             * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
             * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
             */
            library SafeERC20 {
                using SafeMath for uint256;
                using Address for address;
            
                function safeTransfer(IERC20 token, address to, uint256 value) internal {
                    _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                }
            
                function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                    _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                }
            
                /**
                 * @dev Deprecated. This function has issues similar to the ones found in
                 * {IERC20-approve}, and its usage is discouraged.
                 *
                 * Whenever possible, use {safeIncreaseAllowance} and
                 * {safeDecreaseAllowance} instead.
                 */
                function safeApprove(IERC20 token, address spender, uint256 value) internal {
                    // safeApprove should only be called when setting an initial allowance,
                    // or when resetting it to zero. To increase and decrease it, use
                    // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                    // solhint-disable-next-line max-line-length
                    require((value == 0) || (token.allowance(address(this), spender) == 0),
                        "SafeERC20: approve from non-zero to non-zero allowance"
                    );
                    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                }
            
                function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                    uint256 newAllowance = token.allowance(address(this), spender).add(value);
                    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                }
            
                function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                    uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
                    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                }
            
                /**
                 * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                 * on the return value: the return value is optional (but if data is returned, it must not be false).
                 * @param token The token targeted by the call.
                 * @param data The call data (encoded using abi.encode or one of its variants).
                 */
                function _callOptionalReturn(IERC20 token, bytes memory data) private {
                    // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                    // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
                    // the target address contains contract code and also asserts for success in the low-level call.
            
                    bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                    if (returndata.length > 0) { // Return data is optional
                        // solhint-disable-next-line max-line-length
                        require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                    }
                }
            }
            
            // File: @openzeppelin/contracts/utils/Pausable.sol
            
            
            pragma solidity ^0.6.0;
            
            
            /**
             * @dev Contract module which allows children to implement an emergency stop
             * mechanism that can be triggered by an authorized account.
             *
             * This module is used through inheritance. It will make available the
             * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
             * the functions of your contract. Note that they will not be pausable by
             * simply including this module, only once the modifiers are put in place.
             */
            contract Pausable is Context {
                /**
                 * @dev Emitted when the pause is triggered by `account`.
                 */
                event Paused(address account);
            
                /**
                 * @dev Emitted when the pause is lifted by `account`.
                 */
                event Unpaused(address account);
            
                bool private _paused;
            
                /**
                 * @dev Initializes the contract in unpaused state.
                 */
                constructor () internal {
                    _paused = false;
                }
            
                /**
                 * @dev Returns true if the contract is paused, and false otherwise.
                 */
                function paused() public view returns (bool) {
                    return _paused;
                }
            
                /**
                 * @dev Modifier to make a function callable only when the contract is not paused.
                 *
                 * Requirements:
                 *
                 * - The contract must not be paused.
                 */
                modifier whenNotPaused() {
                    require(!_paused, "Pausable: paused");
                    _;
                }
            
                /**
                 * @dev Modifier to make a function callable only when the contract is paused.
                 *
                 * Requirements:
                 *
                 * - The contract must be paused.
                 */
                modifier whenPaused() {
                    require(_paused, "Pausable: not paused");
                    _;
                }
            
                /**
                 * @dev Triggers stopped state.
                 *
                 * Requirements:
                 *
                 * - The contract must not be paused.
                 */
                function _pause() internal virtual whenNotPaused {
                    _paused = true;
                    emit Paused(_msgSender());
                }
            
                /**
                 * @dev Returns to normal state.
                 *
                 * Requirements:
                 *
                 * - The contract must be paused.
                 */
                function _unpause() internal virtual whenPaused {
                    _paused = false;
                    emit Unpaused(_msgSender());
                }
            }
            
            // File: contracts/interfaces/IChi.sol
            
            
            pragma solidity ^0.6.12;
            
            
            
            interface IChi is IERC20 {
                function mint(uint256 value) external;
                function free(uint256 value) external returns (uint256 freed);
                function freeFromUpTo(address from, uint256 value) external returns (uint256 freed);
            }
            
            // File: contracts/interfaces/IERC20Permit.sol
            
            
            pragma solidity ^0.6.12;
            
            
            interface IERC20Permit {
                function permit(address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
            }
            
            // File: contracts/interfaces/IGasDiscountExtension.sol
            
            
            pragma solidity ^0.6.12;
            
            
            
            interface IGasDiscountExtension {
                function calculateGas(uint256 gasUsed, uint256 flags, uint256 calldataLength) external pure returns (IChi, uint256);
            }
            
            // File: contracts/interfaces/ISafeERC20Extension.sol
            
            
            pragma solidity ^0.6.12;
            
            
            
            interface ISafeERC20Extension {
                function safeApprove(IERC20 token, address spender, uint256 amount) external;
                function safeTransfer(IERC20 token, address payable target, uint256 amount) external;
            }
            
            // File: contracts/interfaces/IOneInchCaller.sol
            
            
            pragma solidity ^0.6.12;
            pragma experimental ABIEncoderV2;
            
            
            
            
            
            interface IOneInchCaller is ISafeERC20Extension, IGasDiscountExtension {
                struct CallDescription {
                    uint256 targetWithMandatory;
                    uint256 gasLimit;
                    uint256 value;
                    bytes data;
                }
            
                function makeCall(CallDescription memory desc) external;
                function makeCalls(CallDescription[] memory desc) external payable;
            }
            
            // File: contracts/helpers/RevertReasonParser.sol
            
            
            pragma solidity ^0.6.12;
            
            
            library RevertReasonParser {
                function parse(bytes memory data, string memory prefix) internal pure returns (string memory) {
                    // https://solidity.readthedocs.io/en/latest/control-structures.html#revert
                    // We assume that revert reason is abi-encoded as Error(string)
            
                    // 68 = 4-byte selector 0x08c379a0 + 32 bytes offset + 32 bytes length
                    if (data.length >= 68 && data[0] == "\x08" && data[1] == "\xc3" && data[2] == "\x79" && data[3] == "\xa0") {
                        string memory reason;
                        // solhint-disable no-inline-assembly
                        assembly {
                            // 68 = 32 bytes data length + 4-byte selector + 32 bytes offset
                            reason := add(data, 68)
                        }
                        /*
                            revert reason is padded up to 32 bytes with ABI encoder: Error(string)
                            also sometimes there is extra 32 bytes of zeros padded in the end:
                            https://github.com/ethereum/solidity/issues/10170
                            because of that we can't check for equality and instead check
                            that string length + extra 68 bytes is less than overall data length
                        */
                        require(data.length >= 68 + bytes(reason).length, "Invalid revert reason");
                        return string(abi.encodePacked(prefix, "Error(", reason, ")"));
                    }
                    // 36 = 4-byte selector 0x4e487b71 + 32 bytes integer
                    else if (data.length == 36 && data[0] == "\x4e" && data[1] == "\x48" && data[2] == "\x7b" && data[3] == "\x71") {
                        uint256 code;
                        // solhint-disable no-inline-assembly
                        assembly {
                            // 36 = 32 bytes data length + 4-byte selector
                            code := mload(add(data, 36))
                        }
                        return string(abi.encodePacked(prefix, "Panic(", _toHex(code), ")"));
                    }
            
                    return string(abi.encodePacked(prefix, "Unknown()"));
                }
            
                function _toHex(uint256 value) private pure returns(string memory) {
                    return _toHex(abi.encodePacked(value));
                }
            
                function _toHex(bytes memory data) private pure returns(string memory) {
                    bytes memory alphabet = "0123456789abcdef";
                    bytes memory str = new bytes(2 + data.length * 2);
                    str[0] = "0";
                    str[1] = "x";
                    for (uint256 i = 0; i < data.length; i++) {
                        str[2 * i + 2] = alphabet[uint8(data[i] >> 4)];
                        str[2 * i + 3] = alphabet[uint8(data[i] & 0x0f)];
                    }
                    return string(str);
                }
            }
            
            // File: contracts/helpers/UniERC20.sol
            
            
            pragma solidity ^0.6.12;
            
            
            
            
            
            library UniERC20 {
                using SafeMath for uint256;
                using SafeERC20 for IERC20;
            
                IERC20 private constant _ETH_ADDRESS = IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
                IERC20 private constant _ZERO_ADDRESS = IERC20(0);
            
                function isETH(IERC20 token) internal pure returns (bool) {
                    return (token == _ZERO_ADDRESS || token == _ETH_ADDRESS);
                }
            
                function uniBalanceOf(IERC20 token, address account) internal view returns (uint256) {
                    if (isETH(token)) {
                        return account.balance;
                    } else {
                        return token.balanceOf(account);
                    }
                }
            
                function uniTransfer(IERC20 token, address payable to, uint256 amount) internal {
                    if (amount > 0) {
                        if (isETH(token)) {
                            to.transfer(amount);
                        } else {
                            token.safeTransfer(to, amount);
                        }
                    }
                }
            
                function uniApprove(IERC20 token, address to, uint256 amount) internal {
                    require(!isETH(token), "Approve called on ETH");
            
                    if (amount == 0) {
                        token.safeApprove(to, 0);
                    } else {
                        uint256 allowance = token.allowance(address(this), to);
                        if (allowance < amount) {
                            if (allowance > 0) {
                                token.safeApprove(to, 0);
                            }
                            token.safeApprove(to, amount);
                        }
                    }
                }
            }
            
            // File: contracts/OneInchExchange.sol
            
            
            pragma solidity ^0.6.12;
            
            
            
            
            
            
            
            
            
            
            contract OneInchExchange is Ownable, Pausable {
                using SafeMath for uint256;
                using SafeERC20 for IERC20;
                using UniERC20 for IERC20;
            
                uint256 private constant _PARTIAL_FILL = 0x01;
                uint256 private constant _REQUIRES_EXTRA_ETH = 0x02;
                uint256 private constant _SHOULD_CLAIM = 0x04;
                uint256 private constant _BURN_FROM_MSG_SENDER = 0x08;
                uint256 private constant _BURN_FROM_TX_ORIGIN = 0x10;
            
                struct SwapDescription {
                    IERC20 srcToken;
                    IERC20 dstToken;
                    address srcReceiver;
                    address dstReceiver;
                    uint256 amount;
                    uint256 minReturnAmount;
                    uint256 guaranteedAmount;
                    uint256 flags;
                    address referrer;
                    bytes permit;
                }
            
                event Swapped(
                    address indexed sender,
                    IERC20 indexed srcToken,
                    IERC20 indexed dstToken,
                    address dstReceiver,
                    uint256 amount,
                    uint256 spentAmount,
                    uint256 returnAmount,
                    uint256 minReturnAmount,
                    uint256 guaranteedAmount,
                    address referrer
                );
            
                event Error(
                    string reason
                );
            
                function discountedSwap(
                    IOneInchCaller caller,
                    SwapDescription calldata desc,
                    IOneInchCaller.CallDescription[] calldata calls
                )
                    external
                    payable
                    returns (uint256 returnAmount)
                {
                    uint256 initialGas = gasleft();
            
                    address chiSource = address(0);
                    if (desc.flags & _BURN_FROM_MSG_SENDER != 0) {
                        chiSource = msg.sender;
                    } else if (desc.flags & _BURN_FROM_TX_ORIGIN != 0) {
                        chiSource = tx.origin; // solhint-disable-line avoid-tx-origin
                    } else {
                        revert("Incorrect CHI burn flags");
                    }
            
                    // solhint-disable-next-line avoid-low-level-calls
                    (bool success, bytes memory data) = address(this).delegatecall(abi.encodeWithSelector(this.swap.selector, caller, desc, calls));
                    if (success) {
                        returnAmount = abi.decode(data, (uint256));
                    } else {
                        if (msg.value > 0) {
                            msg.sender.transfer(msg.value);
                        }
                        emit Error(RevertReasonParser.parse(data, "Swap failed: "));
                    }
            
                    (IChi chi, uint256 amount) = caller.calculateGas(initialGas.sub(gasleft()), desc.flags, msg.data.length);
                    chi.freeFromUpTo(chiSource, amount);
                }
            
                function swap(
                    IOneInchCaller caller,
                    SwapDescription calldata desc,
                    IOneInchCaller.CallDescription[] calldata calls
                )
                    external
                    payable
                    whenNotPaused
                    returns (uint256 returnAmount)
                {
                    require(desc.minReturnAmount > 0, "Min return should not be 0");
                    require(calls.length > 0, "Call data should exist");
            
                    uint256 flags = desc.flags;
                    IERC20 srcToken = desc.srcToken;
                    IERC20 dstToken = desc.dstToken;
            
                    if (flags & _REQUIRES_EXTRA_ETH != 0) {
                        require(msg.value > (srcToken.isETH() ? desc.amount : 0), "Invalid msg.value");
                    } else {
                        require(msg.value == (srcToken.isETH() ? desc.amount : 0), "Invalid msg.value");
                    }
            
                    if (flags & _SHOULD_CLAIM != 0) {
                        require(!srcToken.isETH(), "Claim token is ETH");
                        _claim(srcToken, desc.srcReceiver, desc.amount, desc.permit);
                    }
            
                    address dstReceiver = (desc.dstReceiver == address(0)) ? msg.sender : desc.dstReceiver;
                    uint256 initialSrcBalance = (flags & _PARTIAL_FILL != 0) ? srcToken.uniBalanceOf(msg.sender) : 0;
                    uint256 initialDstBalance = dstToken.uniBalanceOf(dstReceiver);
            
                    caller.makeCalls{value: msg.value}(calls);
            
                    uint256 spentAmount = desc.amount;
                    returnAmount = dstToken.uniBalanceOf(dstReceiver).sub(initialDstBalance);
            
                    if (flags & _PARTIAL_FILL != 0) {
                        spentAmount = initialSrcBalance.add(desc.amount).sub(srcToken.uniBalanceOf(msg.sender));
                        require(returnAmount.mul(desc.amount) >= desc.minReturnAmount.mul(spentAmount), "Return amount is not enough");
                    } else {
                        require(returnAmount >= desc.minReturnAmount, "Return amount is not enough");
                    }
            
                    _emitSwapped(desc, srcToken, dstToken, dstReceiver, spentAmount, returnAmount);
                }
            
                function _emitSwapped(
                    SwapDescription calldata desc,
                    IERC20 srcToken,
                    IERC20 dstToken,
                    address dstReceiver,
                    uint256 spentAmount,
                    uint256 returnAmount
                 ) private {
                    emit Swapped(
                        msg.sender,
                        srcToken,
                        dstToken,
                        dstReceiver,
                        desc.amount,
                        spentAmount,
                        returnAmount,
                        desc.minReturnAmount,
                        desc.guaranteedAmount,
                        desc.referrer
                    );
                }
            
                function _claim(IERC20 token, address dst, uint256 amount, bytes calldata permit) private {
                    // TODO: Is it safe to call permit on tokens without implemented permit? Fallback will be called. Is it bad for proxies?
            
                    if (permit.length == 32 * 7) {
                        // solhint-disable-next-line avoid-low-level-calls
                        (bool success, bytes memory result) = address(token).call(abi.encodeWithSelector(IERC20Permit.permit.selector, permit));
                        if (!success) {
                            revert(RevertReasonParser.parse(result, "Permit call failed: "));
                        }
                    }
            
                    token.safeTransferFrom(msg.sender, dst, amount);
                }
            
                function rescueFunds(IERC20 token, uint256 amount) external onlyOwner {
                    token.uniTransfer(msg.sender, amount);
                }
            
                function pause() external onlyOwner {
                    _pause();
                }
            }

            File 2 of 4: CentrallyIssuedToken
            /*
             * ERC20 interface
             * see https://github.com/ethereum/EIPs/issues/20
             */
            contract ERC20 {
              uint public totalSupply;
              function balanceOf(address who) constant returns (uint);
              function allowance(address owner, address spender) constant returns (uint);
            
              function transfer(address to, uint value) returns (bool ok);
              function transferFrom(address from, address to, uint value) returns (bool ok);
              function approve(address spender, uint value) returns (bool ok);
              event Transfer(address indexed from, address indexed to, uint value);
              event Approval(address indexed owner, address indexed spender, uint value);
            }
            
            
            
            
            
            
            /**
             * Math operations with safety checks
             */
            contract SafeMath {
              function safeMul(uint a, uint b) internal returns (uint) {
                uint c = a * b;
                assert(a == 0 || c / a == b);
                return c;
              }
            
              function safeDiv(uint a, uint b) internal returns (uint) {
                assert(b > 0);
                uint c = a / b;
                assert(a == b * c + a % b);
                return c;
              }
            
              function safeSub(uint a, uint b) internal returns (uint) {
                assert(b <= a);
                return a - b;
              }
            
              function safeAdd(uint a, uint b) internal returns (uint) {
                uint c = a + b;
                assert(c>=a && c>=b);
                return c;
              }
            
              function max64(uint64 a, uint64 b) internal constant returns (uint64) {
                return a >= b ? a : b;
              }
            
              function min64(uint64 a, uint64 b) internal constant returns (uint64) {
                return a < b ? a : b;
              }
            
              function max256(uint256 a, uint256 b) internal constant returns (uint256) {
                return a >= b ? a : b;
              }
            
              function min256(uint256 a, uint256 b) internal constant returns (uint256) {
                return a < b ? a : b;
              }
            
              function assert(bool assertion) internal {
                if (!assertion) {
                  throw;
                }
              }
            }
            
            
            
            /**
             * Standard ERC20 token with Short Hand Attack and approve() race condition mitigation.
             *
             * Based on code by FirstBlood:
             * https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
             */
            contract StandardToken is ERC20, SafeMath {
            
              /* Actual balances of token holders */
              mapping(address => uint) balances;
            
              /* approve() allowances */
              mapping (address => mapping (address => uint)) allowed;
            
              /* Interface declaration */
              function isToken() public constant returns (bool weAre) {
                return true;
              }
            
              function transfer(address _to, uint _value) returns (bool success) {
                balances[msg.sender] = safeSub(balances[msg.sender], _value);
                balances[_to] = safeAdd(balances[_to], _value);
                Transfer(msg.sender, _to, _value);
                return true;
              }
            
              function transferFrom(address _from, address _to, uint _value) returns (bool success) {
                uint _allowance = allowed[_from][msg.sender];
            
                balances[_to] = safeAdd(balances[_to], _value);
                balances[_from] = safeSub(balances[_from], _value);
                allowed[_from][msg.sender] = safeSub(_allowance, _value);
                Transfer(_from, _to, _value);
                return true;
              }
            
              function balanceOf(address _owner) constant returns (uint balance) {
                return balances[_owner];
              }
            
              function approve(address _spender, uint _value) returns (bool success) {
            
                // To change the approve amount you first have to reduce the addresses`
                //  allowance to zero by calling `approve(_spender, 0)` if it is not
                //  already 0 to mitigate the race condition described here:
                //  https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                if ((_value != 0) && (allowed[msg.sender][_spender] != 0)) throw;
            
                allowed[msg.sender][_spender] = _value;
                Approval(msg.sender, _spender, _value);
                return true;
              }
            
              function allowance(address _owner, address _spender) constant returns (uint remaining) {
                return allowed[_owner][_spender];
              }
            
            }
            
            
            /**
             * Upgrade agent interface inspired by Lunyr.
             *
             * Upgrade agent transfers tokens to a new version of a token contract.
             * Upgrade agent can be set on a token by the upgrade master.
             *
             * Steps are
             * - Upgradeabletoken.upgradeMaster calls UpgradeableToken.setUpgradeAgent()
             * - Individual token holders can now call UpgradeableToken.upgrade()
             *   -> This results to call UpgradeAgent.upgradeFrom() that issues new tokens
             *   -> UpgradeableToken.upgrade() reduces the original total supply based on amount of upgraded tokens
             *
             * Upgrade agent itself can be the token contract, or just a middle man contract doing the heavy lifting.
             */
            contract UpgradeAgent {
            
              uint public originalSupply;
            
              /** Interface marker */
              function isUpgradeAgent() public constant returns (bool) {
                return true;
              }
            
              /**
               * Upgrade amount of tokens to a new version.
               *
               * Only callable by UpgradeableToken.
               *
               * @param _tokenHolder Address that wants to upgrade its tokens
               * @param _amount Number of tokens to upgrade. The address may consider to hold back some amount of tokens in the old version.
               */
              function upgradeFrom(address _tokenHolder, uint256 _amount) external;
            }
            
            
            /**
             * A token upgrade mechanism where users can opt-in amount of tokens to the next smart contract revision.
             *
             * First envisioned by Golem and Lunyr projects.
             */
            contract UpgradeableToken is StandardToken {
            
              /** Contract / person who can set the upgrade path. This can be the same as team multisig wallet, as what it is with its default value. */
              address public upgradeMaster;
            
              /** The next contract where the tokens will be migrated. */
              UpgradeAgent public upgradeAgent;
            
              /** How many tokens we have upgraded by now. */
              uint256 public totalUpgraded;
            
              /**
               * Upgrade states.
               *
               * - NotAllowed: The child contract has not reached a condition where the upgrade can bgun
               * - WaitingForAgent: Token allows upgrade, but we don't have a new agent yet
               * - ReadyToUpgrade: The agent is set, but not a single token has been upgraded yet
               * - Upgrading: Upgrade agent is set and the balance holders can upgrade their tokens
               *
               */
              enum UpgradeState {Unknown, NotAllowed, WaitingForAgent, ReadyToUpgrade, Upgrading}
            
              /**
               * Somebody has upgraded some of his tokens.
               */
              event Upgrade(address indexed _from, address indexed _to, uint256 _value);
            
              /**
               * New upgrade agent available.
               */
              event UpgradeAgentSet(address agent);
            
              /**
               * Upgrade master updated.
               */
              event NewUpgradeMaster(address upgradeMaster);
            
              /**
               * Do not allow construction without upgrade master set.
               */
              function UpgradeableToken(address _upgradeMaster) {
                upgradeMaster = _upgradeMaster;
                NewUpgradeMaster(upgradeMaster);
              }
            
              /**
               * Allow the token holder to upgrade some of their tokens to a new contract.
               */
              function upgrade(uint256 value) public {
            
                  UpgradeState state = getUpgradeState();
                  if(!(state == UpgradeState.ReadyToUpgrade || state == UpgradeState.Upgrading)) {
                    // Called in a bad state
                    throw;
                  }
            
                  // Validate input value.
                  if (value == 0) throw;
            
                  balances[msg.sender] = safeSub(balances[msg.sender], value);
            
                  // Take tokens out from circulation
                  totalSupply = safeSub(totalSupply, value);
                  totalUpgraded = safeAdd(totalUpgraded, value);
            
                  // Upgrade agent reissues the tokens
                  upgradeAgent.upgradeFrom(msg.sender, value);
                  Upgrade(msg.sender, upgradeAgent, value);
              }
            
              /**
               * Set an upgrade agent that handles
               */
              function setUpgradeAgent(address agent) external {
            
                  if(!canUpgrade()) {
                    // The token is not yet in a state that we could think upgrading
                    throw;
                  }
            
                  if (agent == 0x0) throw;
                  // Only a master can designate the next agent
                  if (msg.sender != upgradeMaster) throw;
                  // Upgrade has already begun for an agent
                  if (getUpgradeState() == UpgradeState.Upgrading) throw;
            
                  upgradeAgent = UpgradeAgent(agent);
            
                  // Bad interface
                  if(!upgradeAgent.isUpgradeAgent()) throw;
                  // Make sure that token supplies match in source and target
                  if (upgradeAgent.originalSupply() != totalSupply) throw;
            
                  UpgradeAgentSet(upgradeAgent);
              }
            
              /**
               * Get the state of the token upgrade.
               */
              function getUpgradeState() public constant returns(UpgradeState) {
                if(!canUpgrade()) return UpgradeState.NotAllowed;
                else if(address(upgradeAgent) == 0x00) return UpgradeState.WaitingForAgent;
                else if(totalUpgraded == 0) return UpgradeState.ReadyToUpgrade;
                else return UpgradeState.Upgrading;
              }
            
              /**
               * Change the upgrade master.
               *
               * This allows us to set a new owner for the upgrade mechanism.
               */
              function setUpgradeMaster(address master) public {
                  if (master == 0x0) throw;
                  if (msg.sender != upgradeMaster) throw;
                  upgradeMaster = master;
                  NewUpgradeMaster(upgradeMaster);
              }
            
              /**
               * Child contract can enable to provide the condition when the upgrade can begun.
               */
              function canUpgrade() public constant returns(bool) {
                 return true;
              }
            
            }
            
            
            
            /**
             * Centrally issued Ethereum token.
             *
             * We mix in burnable and upgradeable traits.
             *
             * Token supply is created in the token contract creation and allocated to owner.
             * The owner can then transfer from its supply to crowdsale participants.
             * The owner, or anybody, can burn any excessive tokens they are holding.
             *
             */
            contract CentrallyIssuedToken is UpgradeableToken {
            
              string public name;
              string public symbol;
              uint public decimals;
            
              /** Name and symbol were updated. */
              event UpdatedTokenInformation(string newName, string newSymbol);
            
              function CentrallyIssuedToken(address _owner, string _name, string _symbol, uint _totalSupply, uint _decimals)  UpgradeableToken(_owner) {
                name = _name;
                symbol = _symbol;
                totalSupply = _totalSupply;
                decimals = _decimals;
            
                // Allocate initial balance to the owner
                balances[_owner] = _totalSupply;
              }
            
              /**
               * Owner can update token information here.
               *
               * It is often useful to conceal the actual token association, until
               * the token operations, like central issuance or reissuance have been completed.
               * In this case the initial token can be supplied with empty name and symbol information.
               *
               * This function allows the token owner to rename the token after the operations
               * have been completed and then point the audience to use the token contract.
               */
              function setTokenInformation(string _name, string _symbol) {
            
                if(msg.sender != upgradeMaster) {
                  throw;
                }
            
                if(bytes(name).length > 0 || bytes(symbol).length > 0) {
                  // Information already set
                  // Allow owner to set this information only once
                  throw;
                }
            
                name = _name;
                symbol = _symbol;
                UpdatedTokenInformation(name, symbol);
              }
            
            }

            File 3 of 4: 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 4 of 4: 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]