ETH Price: $2,174.88 (+1.96%)
Gas: 0.04 Gwei

Transaction Decoder

Block:
7660533 at Apr-29-2019 05:38:15 AM +UTC
Transaction Fee:
0.00021191999994702 ETH $0.46
Gas Used:
52,980 Gas / 3.999999999 Gwei

Account State Difference:

  Address   Before After State Difference Code
(Nanopool)
7,352.285836580816663587 Eth7,352.286048500816610607 Eth0.00021191999994702
0xDE139153...32E23126a
0.103163362680713272 Eth
Nonce: 2496
0.102951442680766252 Eth
Nonce: 2497
0.00021191999994702

Execution Trace

0x778fdaf34bb5993d69968f541152451241fab7e3.e42a91ef( )
  • Vyper_contract.getEthToTokenInputPrice( eth_sold=23250000000000000000 ) => ( out=3633213997708571361260 )
    • Vyper_contract.getEthToTokenInputPrice( eth_sold=23250000000000000000 ) => ( out=3633213997708571361260 )
      • DSToken.balanceOf( src=0x09cabEC1eAd1c0Ba254B09efb3EE13841712bE14 ) => ( 1299182604383572327888648 )
      • DSToken.CALL( )
      • KyberOasisReserve.getConversionRate( src=0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359, dest=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, srcQty=3633213997708571361260, blockNumber=7660533 ) => ( 6341385886840431 )
        • MatchingMarket.getBestOffer( sell_gem=0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, buy_gem=0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359 ) => ( 91726 )
        • MatchingMarket.getOffer( id=91726 ) => ( 233311690360448352691, 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, 36699928893698525878296, 0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359 )
        • DSToken.balanceOf( src=0x04A487aFd662c4F9DEAcC07A7B10cFb686B682A4 ) => ( 7185707144979830293449 )
          File 1 of 5: 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 2 of 5: 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 3 of 5: DSToken
          pragma solidity ^0.4.13;
          
          ////// lib/ds-math/src/math.sol
          /// math.sol -- mixin for inline numerical wizardry
          
          // This program is free software: you can redistribute it and/or modify
          // it under the terms of the GNU General Public License as published by
          // the Free Software Foundation, either version 3 of the License, or
          // (at your option) any later version.
          
          // This program is distributed in the hope that it will be useful,
          // but WITHOUT ANY WARRANTY; without even the implied warranty of
          // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
          // GNU General Public License for more details.
          
          // You should have received a copy of the GNU General Public License
          // along with this program.  If not, see <http://www.gnu.org/licenses/>.
          
          /* pragma solidity ^0.4.13; */
          
          contract DSMath {
              function add(uint x, uint y) internal pure returns (uint z) {
                  require((z = x + y) >= x);
              }
              function sub(uint x, uint y) internal pure returns (uint z) {
                  require((z = x - y) <= x);
              }
              function mul(uint x, uint y) internal pure returns (uint z) {
                  require(y == 0 || (z = x * y) / y == x);
              }
          
              function min(uint x, uint y) internal pure returns (uint z) {
                  return x <= y ? x : y;
              }
              function max(uint x, uint y) internal pure returns (uint z) {
                  return x >= y ? x : y;
              }
              function imin(int x, int y) internal pure returns (int z) {
                  return x <= y ? x : y;
              }
              function imax(int x, int y) internal pure returns (int z) {
                  return x >= y ? x : y;
              }
          
              uint constant WAD = 10 ** 18;
              uint constant RAY = 10 ** 27;
          
              function wmul(uint x, uint y) internal pure returns (uint z) {
                  z = add(mul(x, y), WAD / 2) / WAD;
              }
              function rmul(uint x, uint y) internal pure returns (uint z) {
                  z = add(mul(x, y), RAY / 2) / RAY;
              }
              function wdiv(uint x, uint y) internal pure returns (uint z) {
                  z = add(mul(x, WAD), y / 2) / y;
              }
              function rdiv(uint x, uint y) internal pure returns (uint z) {
                  z = add(mul(x, RAY), y / 2) / y;
              }
          
              // This famous algorithm is called "exponentiation by squaring"
              // and calculates x^n with x as fixed-point and n as regular unsigned.
              //
              // It's O(log n), instead of O(n) for naive repeated multiplication.
              //
              // These facts are why it works:
              //
              //  If n is even, then x^n = (x^2)^(n/2).
              //  If n is odd,  then x^n = x * x^(n-1),
              //   and applying the equation for even x gives
              //    x^n = x * (x^2)^((n-1) / 2).
              //
              //  Also, EVM division is flooring and
              //    floor[(n-1) / 2] = floor[n / 2].
              //
              function rpow(uint x, uint n) internal pure returns (uint z) {
                  z = n % 2 != 0 ? x : RAY;
          
                  for (n /= 2; n != 0; n /= 2) {
                      x = rmul(x, x);
          
                      if (n % 2 != 0) {
                          z = rmul(z, x);
                      }
                  }
              }
          }
          
          ////// lib/ds-stop/lib/ds-auth/src/auth.sol
          // This program is free software: you can redistribute it and/or modify
          // it under the terms of the GNU General Public License as published by
          // the Free Software Foundation, either version 3 of the License, or
          // (at your option) any later version.
          
          // This program is distributed in the hope that it will be useful,
          // but WITHOUT ANY WARRANTY; without even the implied warranty of
          // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
          // GNU General Public License for more details.
          
          // You should have received a copy of the GNU General Public License
          // along with this program.  If not, see <http://www.gnu.org/licenses/>.
          
          /* pragma solidity ^0.4.13; */
          
          contract DSAuthority {
              function canCall(
                  address src, address dst, bytes4 sig
              ) public view returns (bool);
          }
          
          contract DSAuthEvents {
              event LogSetAuthority (address indexed authority);
              event LogSetOwner     (address indexed owner);
          }
          
          contract DSAuth is DSAuthEvents {
              DSAuthority  public  authority;
              address      public  owner;
          
              function DSAuth() public {
                  owner = msg.sender;
                  LogSetOwner(msg.sender);
              }
          
              function setOwner(address owner_)
                  public
                  auth
              {
                  owner = owner_;
                  LogSetOwner(owner);
              }
          
              function setAuthority(DSAuthority authority_)
                  public
                  auth
              {
                  authority = authority_;
                  LogSetAuthority(authority);
              }
          
              modifier auth {
                  require(isAuthorized(msg.sender, msg.sig));
                  _;
              }
          
              function isAuthorized(address src, bytes4 sig) internal view returns (bool) {
                  if (src == address(this)) {
                      return true;
                  } else if (src == owner) {
                      return true;
                  } else if (authority == DSAuthority(0)) {
                      return false;
                  } else {
                      return authority.canCall(src, this, sig);
                  }
              }
          }
          
          ////// lib/ds-stop/lib/ds-note/src/note.sol
          /// note.sol -- the `note' modifier, for logging calls as events
          
          // This program is free software: you can redistribute it and/or modify
          // it under the terms of the GNU General Public License as published by
          // the Free Software Foundation, either version 3 of the License, or
          // (at your option) any later version.
          
          // This program is distributed in the hope that it will be useful,
          // but WITHOUT ANY WARRANTY; without even the implied warranty of
          // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
          // GNU General Public License for more details.
          
          // You should have received a copy of the GNU General Public License
          // along with this program.  If not, see <http://www.gnu.org/licenses/>.
          
          /* pragma solidity ^0.4.13; */
          
          contract DSNote {
              event LogNote(
                  bytes4   indexed  sig,
                  address  indexed  guy,
                  bytes32  indexed  foo,
                  bytes32  indexed  bar,
                  uint              wad,
                  bytes             fax
              ) anonymous;
          
              modifier note {
                  bytes32 foo;
                  bytes32 bar;
          
                  assembly {
                      foo := calldataload(4)
                      bar := calldataload(36)
                  }
          
                  LogNote(msg.sig, msg.sender, foo, bar, msg.value, msg.data);
          
                  _;
              }
          }
          
          ////// lib/ds-stop/src/stop.sol
          /// stop.sol -- mixin for enable/disable functionality
          
          // Copyright (C) 2017  DappHub, LLC
          
          // This program is free software: you can redistribute it and/or modify
          // it under the terms of the GNU General Public License as published by
          // the Free Software Foundation, either version 3 of the License, or
          // (at your option) any later version.
          
          // This program is distributed in the hope that it will be useful,
          // but WITHOUT ANY WARRANTY; without even the implied warranty of
          // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
          // GNU General Public License for more details.
          
          // You should have received a copy of the GNU General Public License
          // along with this program.  If not, see <http://www.gnu.org/licenses/>.
          
          /* pragma solidity ^0.4.13; */
          
          /* import "ds-auth/auth.sol"; */
          /* import "ds-note/note.sol"; */
          
          contract DSStop is DSNote, DSAuth {
          
              bool public stopped;
          
              modifier stoppable {
                  require(!stopped);
                  _;
              }
              function stop() public auth note {
                  stopped = true;
              }
              function start() public auth note {
                  stopped = false;
              }
          
          }
          
          ////// lib/erc20/src/erc20.sol
          /// erc20.sol -- API for the ERC20 token standard
          
          // See <https://github.com/ethereum/EIPs/issues/20>.
          
          // This file likely does not meet the threshold of originality
          // required for copyright to apply.  As a result, this is free and
          // unencumbered software belonging to the public domain.
          
          /* pragma solidity ^0.4.8; */
          
          contract ERC20Events {
              event Approval(address indexed src, address indexed guy, uint wad);
              event Transfer(address indexed src, address indexed dst, uint wad);
          }
          
          contract ERC20 is ERC20Events {
              function totalSupply() public view returns (uint);
              function balanceOf(address guy) public view returns (uint);
              function allowance(address src, address guy) public view returns (uint);
          
              function approve(address guy, uint wad) public returns (bool);
              function transfer(address dst, uint wad) public returns (bool);
              function transferFrom(
                  address src, address dst, uint wad
              ) public returns (bool);
          }
          
          ////// src/base.sol
          /// base.sol -- basic ERC20 implementation
          
          // Copyright (C) 2015, 2016, 2017  DappHub, LLC
          
          // This program is free software: you can redistribute it and/or modify
          // it under the terms of the GNU General Public License as published by
          // the Free Software Foundation, either version 3 of the License, or
          // (at your option) any later version.
          
          // This program is distributed in the hope that it will be useful,
          // but WITHOUT ANY WARRANTY; without even the implied warranty of
          // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
          // GNU General Public License for more details.
          
          // You should have received a copy of the GNU General Public License
          // along with this program.  If not, see <http://www.gnu.org/licenses/>.
          
          /* pragma solidity ^0.4.13; */
          
          /* import "erc20/erc20.sol"; */
          /* import "ds-math/math.sol"; */
          
          contract DSTokenBase is ERC20, DSMath {
              uint256                                            _supply;
              mapping (address => uint256)                       _balances;
              mapping (address => mapping (address => uint256))  _approvals;
          
              function DSTokenBase(uint supply) public {
                  _balances[msg.sender] = supply;
                  _supply = supply;
              }
          
              function totalSupply() public view returns (uint) {
                  return _supply;
              }
              function balanceOf(address src) public view returns (uint) {
                  return _balances[src];
              }
              function allowance(address src, address guy) public view returns (uint) {
                  return _approvals[src][guy];
              }
          
              function transfer(address dst, uint wad) public returns (bool) {
                  return transferFrom(msg.sender, dst, wad);
              }
          
              function transferFrom(address src, address dst, uint wad)
                  public
                  returns (bool)
              {
                  if (src != msg.sender) {
                      _approvals[src][msg.sender] = sub(_approvals[src][msg.sender], wad);
                  }
          
                  _balances[src] = sub(_balances[src], wad);
                  _balances[dst] = add(_balances[dst], wad);
          
                  Transfer(src, dst, wad);
          
                  return true;
              }
          
              function approve(address guy, uint wad) public returns (bool) {
                  _approvals[msg.sender][guy] = wad;
          
                  Approval(msg.sender, guy, wad);
          
                  return true;
              }
          }
          
          ////// src/token.sol
          /// token.sol -- ERC20 implementation with minting and burning
          
          // Copyright (C) 2015, 2016, 2017  DappHub, LLC
          
          // This program is free software: you can redistribute it and/or modify
          // it under the terms of the GNU General Public License as published by
          // the Free Software Foundation, either version 3 of the License, or
          // (at your option) any later version.
          
          // This program is distributed in the hope that it will be useful,
          // but WITHOUT ANY WARRANTY; without even the implied warranty of
          // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
          // GNU General Public License for more details.
          
          // You should have received a copy of the GNU General Public License
          // along with this program.  If not, see <http://www.gnu.org/licenses/>.
          
          /* pragma solidity ^0.4.13; */
          
          /* import "ds-stop/stop.sol"; */
          
          /* import "./base.sol"; */
          
          contract DSToken is DSTokenBase(0), DSStop {
          
              bytes32  public  symbol;
              uint256  public  decimals = 18; // standard token precision. override to customize
          
              function DSToken(bytes32 symbol_) public {
                  symbol = symbol_;
              }
          
              event Mint(address indexed guy, uint wad);
              event Burn(address indexed guy, uint wad);
          
              function approve(address guy) public stoppable returns (bool) {
                  return super.approve(guy, uint(-1));
              }
          
              function approve(address guy, uint wad) public stoppable returns (bool) {
                  return super.approve(guy, wad);
              }
          
              function transferFrom(address src, address dst, uint wad)
                  public
                  stoppable
                  returns (bool)
              {
                  if (src != msg.sender && _approvals[src][msg.sender] != uint(-1)) {
                      _approvals[src][msg.sender] = sub(_approvals[src][msg.sender], wad);
                  }
          
                  _balances[src] = sub(_balances[src], wad);
                  _balances[dst] = add(_balances[dst], wad);
          
                  Transfer(src, dst, wad);
          
                  return true;
              }
          
              function push(address dst, uint wad) public {
                  transferFrom(msg.sender, dst, wad);
              }
              function pull(address src, uint wad) public {
                  transferFrom(src, msg.sender, wad);
              }
              function move(address src, address dst, uint wad) public {
                  transferFrom(src, dst, wad);
              }
          
              function mint(uint wad) public {
                  mint(msg.sender, wad);
              }
              function burn(uint wad) public {
                  burn(msg.sender, wad);
              }
              function mint(address guy, uint wad) public auth stoppable {
                  _balances[guy] = add(_balances[guy], wad);
                  _supply = add(_supply, wad);
                  Mint(guy, wad);
              }
              function burn(address guy, uint wad) public auth stoppable {
                  if (guy != msg.sender && _approvals[guy][msg.sender] != uint(-1)) {
                      _approvals[guy][msg.sender] = sub(_approvals[guy][msg.sender], wad);
                  }
          
                  _balances[guy] = sub(_balances[guy], wad);
                  _supply = sub(_supply, wad);
                  Burn(guy, wad);
              }
          
              // Optional token name
              bytes32   public  name = "";
          
              function setName(bytes32 name_) public auth {
                  name = name_;
              }
          }

          File 4 of 5: KyberOasisReserve
          pragma solidity 0.4.18;
          
          // File: /home/yaron/kyber/oasis_set_otc/smart-contracts/contracts/ERC20Interface.sol
          
          // https://github.com/ethereum/EIPs/issues/20
          interface ERC20 {
              function totalSupply() public view returns (uint supply);
              function balanceOf(address _owner) public view returns (uint balance);
              function transfer(address _to, uint _value) public returns (bool success);
              function transferFrom(address _from, address _to, uint _value) public returns (bool success);
              function approve(address _spender, uint _value) public returns (bool success);
              function allowance(address _owner, address _spender) public view returns (uint remaining);
              function decimals() public view returns(uint digits);
              event Approval(address indexed _owner, address indexed _spender, uint _value);
          }
          
          // File: /home/yaron/kyber/oasis_set_otc/smart-contracts/contracts/Utils.sol
          
          /// @title Kyber constants contract
          contract Utils {
          
              ERC20 constant internal ETH_TOKEN_ADDRESS = ERC20(0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee);
              uint  constant internal PRECISION = (10**18);
              uint  constant internal MAX_QTY   = (10**28); // 10B tokens
              uint  constant internal MAX_RATE  = (PRECISION * 10**6); // up to 1M tokens per ETH
              uint  constant internal MAX_DECIMALS = 18;
              uint  constant internal ETH_DECIMALS = 18;
              mapping(address=>uint) internal decimals;
          
              function setDecimals(ERC20 token) internal {
                  if (token == ETH_TOKEN_ADDRESS) decimals[token] = ETH_DECIMALS;
                  else decimals[token] = token.decimals();
              }
          
              function getDecimals(ERC20 token) internal view returns(uint) {
                  if (token == ETH_TOKEN_ADDRESS) return ETH_DECIMALS; // save storage access
                  uint tokenDecimals = decimals[token];
                  // technically, there might be token with decimals 0
                  // moreover, very possible that old tokens have decimals 0
                  // these tokens will just have higher gas fees.
                  if(tokenDecimals == 0) return token.decimals();
          
                  return tokenDecimals;
              }
          
              function calcDstQty(uint srcQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {
                  require(srcQty <= MAX_QTY);
                  require(rate <= MAX_RATE);
          
                  if (dstDecimals >= srcDecimals) {
                      require((dstDecimals - srcDecimals) <= MAX_DECIMALS);
                      return (srcQty * rate * (10**(dstDecimals - srcDecimals))) / PRECISION;
                  } else {
                      require((srcDecimals - dstDecimals) <= MAX_DECIMALS);
                      return (srcQty * rate) / (PRECISION * (10**(srcDecimals - dstDecimals)));
                  }
              }
          
              function calcSrcQty(uint dstQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {
                  require(dstQty <= MAX_QTY);
                  require(rate <= MAX_RATE);
                  
                  //source quantity is rounded up. to avoid dest quantity being too low.
                  uint numerator;
                  uint denominator;
                  if (srcDecimals >= dstDecimals) {
                      require((srcDecimals - dstDecimals) <= MAX_DECIMALS);
                      numerator = (PRECISION * dstQty * (10**(srcDecimals - dstDecimals)));
                      denominator = rate;
                  } else {
                      require((dstDecimals - srcDecimals) <= MAX_DECIMALS);
                      numerator = (PRECISION * dstQty);
                      denominator = (rate * (10**(dstDecimals - srcDecimals)));
                  }
                  return (numerator + denominator - 1) / denominator; //avoid rounding down errors
              }
          }
          
          // File: /home/yaron/kyber/oasis_set_otc/smart-contracts/contracts/Utils2.sol
          
          contract Utils2 is Utils {
          
              /// @dev get the balance of a user.
              /// @param token The token type
              /// @return The balance
              function getBalance(ERC20 token, address user) public view returns(uint) {
                  if (token == ETH_TOKEN_ADDRESS)
                      return user.balance;
                  else
                      return token.balanceOf(user);
              }
          
              function getDecimalsSafe(ERC20 token) internal returns(uint) {
          
                  if (decimals[token] == 0) {
                      setDecimals(token);
                  }
          
                  return decimals[token];
              }
          
              function calcDestAmount(ERC20 src, ERC20 dest, uint srcAmount, uint rate) internal view returns(uint) {
                  return calcDstQty(srcAmount, getDecimals(src), getDecimals(dest), rate);
              }
          
              function calcSrcAmount(ERC20 src, ERC20 dest, uint destAmount, uint rate) internal view returns(uint) {
                  return calcSrcQty(destAmount, getDecimals(src), getDecimals(dest), rate);
              }
          
              function calcRateFromQty(uint srcAmount, uint destAmount, uint srcDecimals, uint dstDecimals)
                  internal pure returns(uint)
              {
                  require(srcAmount <= MAX_QTY);
                  require(destAmount <= MAX_QTY);
          
                  if (dstDecimals >= srcDecimals) {
                      require((dstDecimals - srcDecimals) <= MAX_DECIMALS);
                      return (destAmount * PRECISION / ((10 ** (dstDecimals - srcDecimals)) * srcAmount));
                  } else {
                      require((srcDecimals - dstDecimals) <= MAX_DECIMALS);
                      return (destAmount * PRECISION * (10 ** (srcDecimals - dstDecimals)) / srcAmount);
                  }
              }
          }
          
          // File: /home/yaron/kyber/oasis_set_otc/smart-contracts/contracts/PermissionGroups.sol
          
          contract PermissionGroups {
          
              address public admin;
              address public pendingAdmin;
              mapping(address=>bool) internal operators;
              mapping(address=>bool) internal alerters;
              address[] internal operatorsGroup;
              address[] internal alertersGroup;
              uint constant internal MAX_GROUP_SIZE = 50;
          
              function PermissionGroups() public {
                  admin = msg.sender;
              }
          
              modifier onlyAdmin() {
                  require(msg.sender == admin);
                  _;
              }
          
              modifier onlyOperator() {
                  require(operators[msg.sender]);
                  _;
              }
          
              modifier onlyAlerter() {
                  require(alerters[msg.sender]);
                  _;
              }
          
              function getOperators () external view returns(address[]) {
                  return operatorsGroup;
              }
          
              function getAlerters () external view returns(address[]) {
                  return alertersGroup;
              }
          
              event TransferAdminPending(address pendingAdmin);
          
              /**
               * @dev Allows the current admin to set the pendingAdmin address.
               * @param newAdmin The address to transfer ownership to.
               */
              function transferAdmin(address newAdmin) public onlyAdmin {
                  require(newAdmin != address(0));
                  TransferAdminPending(pendingAdmin);
                  pendingAdmin = newAdmin;
              }
          
              /**
               * @dev Allows the current admin to set the admin in one tx. Useful initial deployment.
               * @param newAdmin The address to transfer ownership to.
               */
              function transferAdminQuickly(address newAdmin) public onlyAdmin {
                  require(newAdmin != address(0));
                  TransferAdminPending(newAdmin);
                  AdminClaimed(newAdmin, admin);
                  admin = newAdmin;
              }
          
              event AdminClaimed( address newAdmin, address previousAdmin);
          
              /**
               * @dev Allows the pendingAdmin address to finalize the change admin process.
               */
              function claimAdmin() public {
                  require(pendingAdmin == msg.sender);
                  AdminClaimed(pendingAdmin, admin);
                  admin = pendingAdmin;
                  pendingAdmin = address(0);
              }
          
              event AlerterAdded (address newAlerter, bool isAdd);
          
              function addAlerter(address newAlerter) public onlyAdmin {
                  require(!alerters[newAlerter]); // prevent duplicates.
                  require(alertersGroup.length < MAX_GROUP_SIZE);
          
                  AlerterAdded(newAlerter, true);
                  alerters[newAlerter] = true;
                  alertersGroup.push(newAlerter);
              }
          
              function removeAlerter (address alerter) public onlyAdmin {
                  require(alerters[alerter]);
                  alerters[alerter] = false;
          
                  for (uint i = 0; i < alertersGroup.length; ++i) {
                      if (alertersGroup[i] == alerter) {
                          alertersGroup[i] = alertersGroup[alertersGroup.length - 1];
                          alertersGroup.length--;
                          AlerterAdded(alerter, false);
                          break;
                      }
                  }
              }
          
              event OperatorAdded(address newOperator, bool isAdd);
          
              function addOperator(address newOperator) public onlyAdmin {
                  require(!operators[newOperator]); // prevent duplicates.
                  require(operatorsGroup.length < MAX_GROUP_SIZE);
          
                  OperatorAdded(newOperator, true);
                  operators[newOperator] = true;
                  operatorsGroup.push(newOperator);
              }
          
              function removeOperator (address operator) public onlyAdmin {
                  require(operators[operator]);
                  operators[operator] = false;
          
                  for (uint i = 0; i < operatorsGroup.length; ++i) {
                      if (operatorsGroup[i] == operator) {
                          operatorsGroup[i] = operatorsGroup[operatorsGroup.length - 1];
                          operatorsGroup.length -= 1;
                          OperatorAdded(operator, false);
                          break;
                      }
                  }
              }
          }
          
          // File: /home/yaron/kyber/oasis_set_otc/smart-contracts/contracts/Withdrawable.sol
          
          /**
           * @title Contracts that should be able to recover tokens or ethers
           * @author Ilan Doron
           * @dev This allows to recover any tokens or Ethers received in a contract.
           * This will prevent any accidental loss of tokens.
           */
          contract Withdrawable is PermissionGroups {
          
              event TokenWithdraw(ERC20 token, uint amount, address sendTo);
          
              /**
               * @dev Withdraw all ERC20 compatible tokens
               * @param token ERC20 The address of the token contract
               */
              function withdrawToken(ERC20 token, uint amount, address sendTo) external onlyAdmin {
                  require(token.transfer(sendTo, amount));
                  TokenWithdraw(token, amount, sendTo);
              }
          
              event EtherWithdraw(uint amount, address sendTo);
          
              /**
               * @dev Withdraw Ethers
               */
              function withdrawEther(uint amount, address sendTo) external onlyAdmin {
                  sendTo.transfer(amount);
                  EtherWithdraw(amount, sendTo);
              }
          }
          
          // File: /home/yaron/kyber/oasis_set_otc/smart-contracts/contracts/KyberReserveInterface.sol
          
          /// @title Kyber Reserve contract
          interface KyberReserveInterface {
          
              function trade(
                  ERC20 srcToken,
                  uint srcAmount,
                  ERC20 destToken,
                  address destAddress,
                  uint conversionRate,
                  bool validate
              )
                  public
                  payable
                  returns(bool);
          
              function getConversionRate(ERC20 src, ERC20 dest, uint srcQty, uint blockNumber) public view returns(uint);
          }
          
          // File: contracts/oasisContracts/KyberOasisReserve.sol
          
          contract OtcInterface {
              function getOffer(uint id) public constant returns (uint, ERC20, uint, ERC20);
              function getBestOffer(ERC20 sellGem, ERC20 buyGem) public constant returns(uint);
              function getWorseOffer(uint id) public constant returns(uint);
              function take(bytes32 id, uint128 maxTakeAmount) public;
          }
          
          
          contract WethInterface is ERC20 {
              function deposit() public payable;
              function withdraw(uint) public;
          }
          
          
          contract KyberOasisReserve is KyberReserveInterface, Withdrawable, Utils2 {
          
              uint constant internal COMMON_DECIMALS = 18;
              address public sanityRatesContract = 0;
              address public kyberNetwork;
              OtcInterface public otc;
              WethInterface public wethToken;
              mapping(address=>bool) public isTokenListed;
              mapping(address=>uint) public tokenMinSrcAmount;
              mapping(address=>uint) public minTokenBalance;
              mapping(address=>uint) public maxTokenBalance;
              mapping(address=>uint) public internalPricePremiumBps;
              mapping(address=>uint) public minOasisSpreadForinternalPricingBps;
              bool public tradeEnabled;
              uint public feeBps;
          
              function KyberOasisReserve(
                  address _kyberNetwork,
                  OtcInterface _otc,
                  WethInterface _wethToken,
                  address _admin,
                  uint _feeBps
              )
                  public
              {
                  require(_admin != address(0));
                  require(_kyberNetwork != address(0));
                  require(_otc != address(0));
                  require(_wethToken != address(0));
                  require(_feeBps < 10000);
                  require(getDecimals(_wethToken) == COMMON_DECIMALS);
          
                  kyberNetwork = _kyberNetwork;
                  otc = _otc;
                  wethToken = _wethToken;
                  admin = _admin;
                  feeBps = _feeBps;
                  tradeEnabled = true;
          
                  require(wethToken.approve(otc, 2**255));
              }
          
              function() public payable {
                  // anyone can deposit ether
              }
          
              function listToken(ERC20 token, uint minSrcAmount) public onlyAdmin {
                  require(token != address(0));
                  require(!isTokenListed[token]);
                  require(getDecimals(token) == COMMON_DECIMALS);
          
                  require(token.approve(otc, 2**255));
                  isTokenListed[token] = true;
                  tokenMinSrcAmount[token] = minSrcAmount;
                  minTokenBalance[token] = 2 ** 255; // disable by default
                  maxTokenBalance[token] = 0; // disable by default;
                  internalPricePremiumBps[token] = 0; // NA by default
                  minOasisSpreadForinternalPricingBps[token] = 0; // NA by default
              }
          
              function delistToken(ERC20 token) public onlyAdmin {
                  require(isTokenListed[token]);
          
                  require(token.approve(otc, 0));
                  delete isTokenListed[token];
                  delete tokenMinSrcAmount[token];
                  delete minTokenBalance[token];
                  delete maxTokenBalance[token];
                  delete internalPricePremiumBps[token];
                  delete minOasisSpreadForinternalPricingBps[token];
              }
          
              function setInternalPriceAdminParams(ERC20 token,
                                                   uint minSpreadBps,
                                                   uint premiumBps) public onlyAdmin {
                  require(isTokenListed[token]);
                  require(premiumBps <= 500); // premium <= 5%
                  require(minSpreadBps <= 1000); // min spread <= 10%
          
                  internalPricePremiumBps[token] = premiumBps;
                  minOasisSpreadForinternalPricingBps[token] = minSpreadBps;
              }
          
              function setInternalInventoryMinMax(ERC20 token,
                                                  uint  minBalance,
                                                  uint  maxBalance) public onlyOperator {
                  require(isTokenListed[token]);
          
                  // don't require anything on min and max balance as it might be used for disable
                  minTokenBalance[token] = minBalance;
                  maxTokenBalance[token] = maxBalance;
              }
          
              event TradeExecute(
                  address indexed sender,
                  address src,
                  uint srcAmount,
                  address destToken,
                  uint destAmount,
                  address destAddress
              );
          
              function trade(
                  ERC20 srcToken,
                  uint srcAmount,
                  ERC20 destToken,
                  address destAddress,
                  uint conversionRate,
                  bool validate
              )
                  public
                  payable
                  returns(bool)
              {
          
                  require(tradeEnabled);
                  require(msg.sender == kyberNetwork);
          
                  require(doTrade(srcToken, srcAmount, destToken, destAddress, conversionRate, validate));
          
                  return true;
              }
          
              event TradeEnabled(bool enable);
          
              function enableTrade() public onlyAdmin returns(bool) {
                  tradeEnabled = true;
                  TradeEnabled(true);
          
                  return true;
              }
          
              function disableTrade() public onlyAlerter returns(bool) {
                  tradeEnabled = false;
                  TradeEnabled(false);
          
                  return true;
              }
          
              event KyberNetworkSet(address kyberNetwork);
          
              function setKyberNetwork(address _kyberNetwork) public onlyAdmin {
                  require(_kyberNetwork != address(0));
          
                  kyberNetwork = _kyberNetwork;
                  KyberNetworkSet(kyberNetwork);
              }
          
              event FeeBpsSet(uint feeBps);
          
              function setFeeBps(uint _feeBps) public onlyAdmin {
                  require(_feeBps < 10000);
          
                  feeBps = _feeBps;
                  FeeBpsSet(feeBps);
              }
          
              function valueAfterReducingFee(uint val) public view returns(uint) {
                  require(val <= MAX_QTY);
                  return ((10000 - feeBps) * val) / 10000;
              }
          
              function valueBeforeFeesWereReduced(uint val) public view returns(uint) {
                  require(val <= MAX_QTY);
                  return val * 10000 / (10000 - feeBps);
              }
          
              function valueAfterAddingPremium(ERC20 token, uint val) public view returns(uint) {
                  require(val <= MAX_QTY);
                  uint premium = internalPricePremiumBps[token];
          
                  return val * (10000 + premium) / 10000;
              }
              function shouldUseInternalInventory(ERC20 token,
                                                  uint tokenVal,
                                                  uint ethVal,
                                                  bool ethToToken) public view returns(bool) {
                  require(tokenVal <= MAX_QTY);
          
                  uint tokenBalance = token.balanceOf(this);
                  if (ethToToken) {
                      if (tokenBalance < tokenVal) return false;
                      if (tokenBalance - tokenVal < minTokenBalance[token]) return false;
                  }
                  else {
                      if (this.balance < ethVal) return false;
                      if (tokenBalance + tokenVal > maxTokenBalance[token]) return false;
                  }
          
                  // check that spread in first level makes sense
                  uint x1; uint y1; uint x2; uint y2;
                  (,x1,y1) = getMatchingOffer(token, wethToken, 0);
                  (,y2,x2) = getMatchingOffer(wethToken, token, 0);
          
                  require(x1 <= MAX_QTY && x2 <= MAX_QTY && y1 <= MAX_QTY && y2 <= MAX_QTY);
          
                  // check if there is an arbitrage
                  if (x1*y2 > x2*y1) return false;
          
                  // spread is (x1/y1 - x2/y2) / (x1/y1)
                  if (10000 * (x2*y1 - x1*y2) < x1*y2*minOasisSpreadForinternalPricingBps[token]) return false;
          
          
                  return true;
              }
          
              function getConversionRate(ERC20 src, ERC20 dest, uint srcQty, uint blockNumber) public view returns(uint) {
                  uint  rate;
                  uint  actualSrcQty;
                  ERC20 actualSrc;
                  ERC20 actualDest;
                  uint offerPayAmt;
                  uint offerBuyAmt;
          
                  blockNumber;
          
                  if (!tradeEnabled) return 0;
                  if (!validTokens(src, dest)) return 0;
          
                  if (src == ETH_TOKEN_ADDRESS) {
                      actualSrc = wethToken;
                      actualDest = dest;
                      actualSrcQty = srcQty;
                  } else if (dest == ETH_TOKEN_ADDRESS) {
                      actualSrc = src;
                      actualDest = wethToken;
          
                      if (srcQty < tokenMinSrcAmount[src]) {
                          /* remove rounding errors and present rate for 0 src amount. */
                          actualSrcQty = tokenMinSrcAmount[src];
                      } else {
                          actualSrcQty = srcQty;
                      }
                  } else {
                      return 0;
                  }
          
                  // otc's terminology is of offer maker, so their sellGem is the taker's dest token.
                  (, offerPayAmt, offerBuyAmt) = getMatchingOffer(actualDest, actualSrc, actualSrcQty);
          
                  // make sure to take only one level of order book to avoid gas inflation.
                  if (actualSrcQty > offerBuyAmt) return 0;
          
                  bool tradeFromInventory = false;
                  uint valueWithPremium = valueAfterAddingPremium(token, offerPayAmt);
                  ERC20 token;
                  if (src == ETH_TOKEN_ADDRESS) {
                      token = dest;
                      tradeFromInventory = shouldUseInternalInventory(token,
                                                                      valueWithPremium,
                                                                      offerBuyAmt,
                                                                      true);
                  }
                  else {
                      token = src;
                      tradeFromInventory = shouldUseInternalInventory(token,
                                                                      offerBuyAmt,
                                                                      valueWithPremium,
                                                                      false);
                  }
          
                  rate = calcRateFromQty(offerBuyAmt, offerPayAmt, COMMON_DECIMALS, COMMON_DECIMALS);
          
                  if (tradeFromInventory) return valueAfterAddingPremium(token,rate);
                  else return valueAfterReducingFee(rate);
              }
          
              function doTrade(
                  ERC20 srcToken,
                  uint srcAmount,
                  ERC20 destToken,
                  address destAddress,
                  uint conversionRate,
                  bool validate
              )
                  internal
                  returns(bool)
              {
                  uint actualDestAmount;
          
                  require(validTokens(srcToken, destToken));
          
                  // can skip validation if done at kyber network level
                  if (validate) {
                      require(conversionRate > 0);
                      if (srcToken == ETH_TOKEN_ADDRESS)
                          require(msg.value == srcAmount);
                      else
                          require(msg.value == 0);
                  }
          
                  uint userExpectedDestAmount = calcDstQty(srcAmount, COMMON_DECIMALS, COMMON_DECIMALS, conversionRate);
                  require(userExpectedDestAmount > 0); // sanity check
          
                  uint destAmountIncludingFees = valueBeforeFeesWereReduced(userExpectedDestAmount);
          
                  if (srcToken == ETH_TOKEN_ADDRESS) {
                      if(!shouldUseInternalInventory(destToken,
                                                     userExpectedDestAmount,
                                                     0,
                                                     true)) {
                          wethToken.deposit.value(msg.value)();
          
                          actualDestAmount = takeMatchingOffer(wethToken, destToken, srcAmount);
                          require(actualDestAmount >= destAmountIncludingFees);
                      }
          
                      // transfer back only requested dest amount.
                      require(destToken.transfer(destAddress, userExpectedDestAmount));
                  } else {
                      require(srcToken.transferFrom(msg.sender, this, srcAmount));
          
                      if(!shouldUseInternalInventory(srcToken,
                                                     0,
                                                     userExpectedDestAmount,
                                                     false)) {
                          actualDestAmount = takeMatchingOffer(srcToken, wethToken, srcAmount);
                          require(actualDestAmount >= destAmountIncludingFees);
                          wethToken.withdraw(actualDestAmount);
                      }
          
                      // transfer back only requested dest amount.
                      destAddress.transfer(userExpectedDestAmount);
                  }
          
                  TradeExecute(msg.sender, srcToken, srcAmount, destToken, userExpectedDestAmount, destAddress);
          
                  return true;
              }
          
              function takeMatchingOffer(
                  ERC20 srcToken,
                  ERC20 destToken,
                  uint srcAmount
              )
                  internal
                  returns(uint actualDestAmount)
              {
                  uint offerId;
                  uint offerPayAmt;
                  uint offerBuyAmt;
          
                  // otc's terminology is of offer maker, so their sellGem is our (the taker's) dest token.
                  (offerId, offerPayAmt, offerBuyAmt) = getMatchingOffer(destToken, srcToken, srcAmount);
          
                  require(srcAmount <= MAX_QTY);
                  require(offerPayAmt <= MAX_QTY);
                  actualDestAmount = srcAmount * offerPayAmt / offerBuyAmt;
          
                  require(uint128(actualDestAmount) == actualDestAmount);
                  otc.take(bytes32(offerId), uint128(actualDestAmount));  // Take the portion of the offer that we need
                  return;
              }
          
              function getMatchingOffer(
                  ERC20 offerSellGem,
                  ERC20 offerBuyGem,
                  uint payAmount
              )
                  internal
                  view
                  returns(
                      uint offerId,
                      uint offerPayAmount,
                      uint offerBuyAmount
                  )
              {
                  offerId = otc.getBestOffer(offerSellGem, offerBuyGem);
                  (offerPayAmount, , offerBuyAmount, ) = otc.getOffer(offerId);
                  uint depth = 1;
          
                  while (payAmount > offerBuyAmount) {
                      offerId = otc.getWorseOffer(offerId); // We look for the next best offer
                      if (offerId == 0 || ++depth > 7) {
                          offerId = 0;
                          offerPayAmount = 0;
                          offerBuyAmount = 0;
                          break;
                      }
                      (offerPayAmount, , offerBuyAmount, ) = otc.getOffer(offerId);
                  }
          
                  return;
              }
          
              function validTokens(ERC20 src, ERC20 dest) internal view returns (bool valid) {
                  return ((isTokenListed[src] && ETH_TOKEN_ADDRESS == dest) ||
                          (isTokenListed[dest] && ETH_TOKEN_ADDRESS == src));
              }
          }

          File 5 of 5: MatchingMarket
          /// matching_market.sol
          
          //
          // This program is free software: you can redistribute it and/or modify
          // it under the terms of the GNU Affero General Public License as published by
          // the Free Software Foundation, either version 3 of the License, or
          // (at your option) any later version.
          //
          // This program is distributed in the hope that it will be useful,
          // but WITHOUT ANY WARRANTY; without even the implied warranty of
          // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
          // GNU Affero General Public License for more details.
          //
          // You should have received a copy of the GNU Affero General Public License
          // along with this program.  If not, see <https://www.gnu.org/licenses/>.
          
          pragma solidity ^0.4.18;
          
          /// expiring_market.sol
          
          //
          // This program is free software: you can redistribute it and/or modify
          // it under the terms of the GNU Affero General Public License as published by
          // the Free Software Foundation, either version 3 of the License, or
          // (at your option) any later version.
          //
          // This program is distributed in the hope that it will be useful,
          // but WITHOUT ANY WARRANTY; without even the implied warranty of
          // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
          // GNU Affero General Public License for more details.
          //
          // You should have received a copy of the GNU Affero General Public License
          // along with this program.  If not, see <https://www.gnu.org/licenses/>.
          
          pragma solidity ^0.4.18;
          
          // This program is free software: you can redistribute it and/or modify
          // it under the terms of the GNU General Public License as published by
          // the Free Software Foundation, either version 3 of the License, or
          // (at your option) any later version.
          
          // This program is distributed in the hope that it will be useful,
          // but WITHOUT ANY WARRANTY; without even the implied warranty of
          // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
          // GNU General Public License for more details.
          
          // You should have received a copy of the GNU General Public License
          // along with this program.  If not, see <http://www.gnu.org/licenses/>.
          
          pragma solidity ^0.4.13;
          
          contract DSAuthority {
              function canCall(
                  address src, address dst, bytes4 sig
              ) public view returns (bool);
          }
          
          contract DSAuthEvents {
              event LogSetAuthority (address indexed authority);
              event LogSetOwner     (address indexed owner);
          }
          
          contract DSAuth is DSAuthEvents {
              DSAuthority  public  authority;
              address      public  owner;
          
              function DSAuth() public {
                  owner = msg.sender;
                  LogSetOwner(msg.sender);
              }
          
              function setOwner(address owner_)
                  public
                  auth
              {
                  owner = owner_;
                  LogSetOwner(owner);
              }
          
              function setAuthority(DSAuthority authority_)
                  public
                  auth
              {
                  authority = authority_;
                  LogSetAuthority(authority);
              }
          
              modifier auth {
                  require(isAuthorized(msg.sender, msg.sig));
                  _;
              }
          
              function isAuthorized(address src, bytes4 sig) internal view returns (bool) {
                  if (src == address(this)) {
                      return true;
                  } else if (src == owner) {
                      return true;
                  } else if (authority == DSAuthority(0)) {
                      return false;
                  } else {
                      return authority.canCall(src, this, sig);
                  }
              }
          }
          
          /// simple_market.sol
          
          //
          // This program is free software: you can redistribute it and/or modify
          // it under the terms of the GNU Affero General Public License as published by
          // the Free Software Foundation, either version 3 of the License, or
          // (at your option) any later version.
          //
          // This program is distributed in the hope that it will be useful,
          // but WITHOUT ANY WARRANTY; without even the implied warranty of
          // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
          // GNU Affero General Public License for more details.
          //
          // You should have received a copy of the GNU Affero General Public License
          // along with this program.  If not, see <https://www.gnu.org/licenses/>.
          
          pragma solidity ^0.4.18;
          
          /// math.sol -- mixin for inline numerical wizardry
          
          // This program is free software: you can redistribute it and/or modify
          // it under the terms of the GNU General Public License as published by
          // the Free Software Foundation, either version 3 of the License, or
          // (at your option) any later version.
          
          // This program is distributed in the hope that it will be useful,
          // but WITHOUT ANY WARRANTY; without even the implied warranty of
          // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
          // GNU General Public License for more details.
          
          // You should have received a copy of the GNU General Public License
          // along with this program.  If not, see <http://www.gnu.org/licenses/>.
          
          pragma solidity ^0.4.13;
          
          contract DSMath {
              function add(uint x, uint y) internal pure returns (uint z) {
                  require((z = x + y) >= x);
              }
              function sub(uint x, uint y) internal pure returns (uint z) {
                  require((z = x - y) <= x);
              }
              function mul(uint x, uint y) internal pure returns (uint z) {
                  require(y == 0 || (z = x * y) / y == x);
              }
          
              function min(uint x, uint y) internal pure returns (uint z) {
                  return x <= y ? x : y;
              }
              function max(uint x, uint y) internal pure returns (uint z) {
                  return x >= y ? x : y;
              }
              function imin(int x, int y) internal pure returns (int z) {
                  return x <= y ? x : y;
              }
              function imax(int x, int y) internal pure returns (int z) {
                  return x >= y ? x : y;
              }
          
              uint constant WAD = 10 ** 18;
              uint constant RAY = 10 ** 27;
          
              function wmul(uint x, uint y) internal pure returns (uint z) {
                  z = add(mul(x, y), WAD / 2) / WAD;
              }
              function rmul(uint x, uint y) internal pure returns (uint z) {
                  z = add(mul(x, y), RAY / 2) / RAY;
              }
              function wdiv(uint x, uint y) internal pure returns (uint z) {
                  z = add(mul(x, WAD), y / 2) / y;
              }
              function rdiv(uint x, uint y) internal pure returns (uint z) {
                  z = add(mul(x, RAY), y / 2) / y;
              }
          
              // This famous algorithm is called "exponentiation by squaring"
              // and calculates x^n with x as fixed-point and n as regular unsigned.
              //
              // It's O(log n), instead of O(n) for naive repeated multiplication.
              //
              // These facts are why it works:
              //
              //  If n is even, then x^n = (x^2)^(n/2).
              //  If n is odd,  then x^n = x * x^(n-1),
              //   and applying the equation for even x gives
              //    x^n = x * (x^2)^((n-1) / 2).
              //
              //  Also, EVM division is flooring and
              //    floor[(n-1) / 2] = floor[n / 2].
              //
              function rpow(uint x, uint n) internal pure returns (uint z) {
                  z = n % 2 != 0 ? x : RAY;
          
                  for (n /= 2; n != 0; n /= 2) {
                      x = rmul(x, x);
          
                      if (n % 2 != 0) {
                          z = rmul(z, x);
                      }
                  }
              }
          }
          
          /// erc20.sol -- API for the ERC20 token standard
          
          // See <https://github.com/ethereum/EIPs/issues/20>.
          
          // This file likely does not meet the threshold of originality
          // required for copyright to apply.  As a result, this is free and
          // unencumbered software belonging to the public domain.
          
          pragma solidity ^0.4.8;
          
          contract ERC20Events {
              event Approval(address indexed src, address indexed guy, uint wad);
              event Transfer(address indexed src, address indexed dst, uint wad);
          }
          
          contract ERC20 is ERC20Events {
              function totalSupply() public view returns (uint);
              function balanceOf(address guy) public view returns (uint);
              function allowance(address src, address guy) public view returns (uint);
          
              function approve(address guy, uint wad) public returns (bool);
              function transfer(address dst, uint wad) public returns (bool);
              function transferFrom(
                  address src, address dst, uint wad
              ) public returns (bool);
          }
          
          contract EventfulMarket {
              event LogItemUpdate(uint id);
              event LogTrade(uint pay_amt, address indexed pay_gem,
                             uint buy_amt, address indexed buy_gem);
          
              event LogMake(
                  bytes32  indexed  id,
                  bytes32  indexed  pair,
                  address  indexed  maker,
                  ERC20             pay_gem,
                  ERC20             buy_gem,
                  uint128           pay_amt,
                  uint128           buy_amt,
                  uint64            timestamp
              );
          
              event LogBump(
                  bytes32  indexed  id,
                  bytes32  indexed  pair,
                  address  indexed  maker,
                  ERC20             pay_gem,
                  ERC20             buy_gem,
                  uint128           pay_amt,
                  uint128           buy_amt,
                  uint64            timestamp
              );
          
              event LogTake(
                  bytes32           id,
                  bytes32  indexed  pair,
                  address  indexed  maker,
                  ERC20             pay_gem,
                  ERC20             buy_gem,
                  address  indexed  taker,
                  uint128           take_amt,
                  uint128           give_amt,
                  uint64            timestamp
              );
          
              event LogKill(
                  bytes32  indexed  id,
                  bytes32  indexed  pair,
                  address  indexed  maker,
                  ERC20             pay_gem,
                  ERC20             buy_gem,
                  uint128           pay_amt,
                  uint128           buy_amt,
                  uint64            timestamp
              );
          }
          
          contract SimpleMarket is EventfulMarket, DSMath {
          
              uint public last_offer_id;
          
              mapping (uint => OfferInfo) public offers;
          
              bool locked;
          
              struct OfferInfo {
                  uint     pay_amt;
                  ERC20    pay_gem;
                  uint     buy_amt;
                  ERC20    buy_gem;
                  address  owner;
                  uint64   timestamp;
              }
          
              modifier can_buy(uint id) {
                  require(isActive(id));
                  _;
              }
          
              modifier can_cancel(uint id) {
                  require(isActive(id));
                  require(getOwner(id) == msg.sender);
                  _;
              }
          
              modifier can_offer {
                  _;
              }
          
              modifier synchronized {
                  require(!locked);
                  locked = true;
                  _;
                  locked = false;
              }
          
              function isActive(uint id) public constant returns (bool active) {
                  return offers[id].timestamp > 0;
              }
          
              function getOwner(uint id) public constant returns (address owner) {
                  return offers[id].owner;
              }
          
              function getOffer(uint id) public constant returns (uint, ERC20, uint, ERC20) {
                var offer = offers[id];
                return (offer.pay_amt, offer.pay_gem,
                        offer.buy_amt, offer.buy_gem);
              }
          
              // ---- Public entrypoints ---- //
          
              function bump(bytes32 id_)
                  public
                  can_buy(uint256(id_))
              {
                  var id = uint256(id_);
                  LogBump(
                      id_,
                      keccak256(offers[id].pay_gem, offers[id].buy_gem),
                      offers[id].owner,
                      offers[id].pay_gem,
                      offers[id].buy_gem,
                      uint128(offers[id].pay_amt),
                      uint128(offers[id].buy_amt),
                      offers[id].timestamp
                  );
              }
          
              // Accept given `quantity` of an offer. Transfers funds from caller to
              // offer maker, and from market to caller.
              function buy(uint id, uint quantity)
                  public
                  can_buy(id)
                  synchronized
                  returns (bool)
              {
                  OfferInfo memory offer = offers[id];
                  uint spend = mul(quantity, offer.buy_amt) / offer.pay_amt;
          
                  require(uint128(spend) == spend);
                  require(uint128(quantity) == quantity);
          
                  // For backwards semantic compatibility.
                  if (quantity == 0 || spend == 0 ||
                      quantity > offer.pay_amt || spend > offer.buy_amt)
                  {
                      return false;
                  }
          
                  offers[id].pay_amt = sub(offer.pay_amt, quantity);
                  offers[id].buy_amt = sub(offer.buy_amt, spend);
                  require( offer.buy_gem.transferFrom(msg.sender, offer.owner, spend) );
                  require( offer.pay_gem.transfer(msg.sender, quantity) );
          
                  LogItemUpdate(id);
                  LogTake(
                      bytes32(id),
                      keccak256(offer.pay_gem, offer.buy_gem),
                      offer.owner,
                      offer.pay_gem,
                      offer.buy_gem,
                      msg.sender,
                      uint128(quantity),
                      uint128(spend),
                      uint64(now)
                  );
                  LogTrade(quantity, offer.pay_gem, spend, offer.buy_gem);
          
                  if (offers[id].pay_amt == 0) {
                    delete offers[id];
                  }
          
                  return true;
              }
          
              // Cancel an offer. Refunds offer maker.
              function cancel(uint id)
                  public
                  can_cancel(id)
                  synchronized
                  returns (bool success)
              {
                  // read-only offer. Modify an offer by directly accessing offers[id]
                  OfferInfo memory offer = offers[id];
                  delete offers[id];
          
                  require( offer.pay_gem.transfer(offer.owner, offer.pay_amt) );
          
                  LogItemUpdate(id);
                  LogKill(
                      bytes32(id),
                      keccak256(offer.pay_gem, offer.buy_gem),
                      offer.owner,
                      offer.pay_gem,
                      offer.buy_gem,
                      uint128(offer.pay_amt),
                      uint128(offer.buy_amt),
                      uint64(now)
                  );
          
                  success = true;
              }
          
              function kill(bytes32 id)
                  public
              {
                  require(cancel(uint256(id)));
              }
          
              function make(
                  ERC20    pay_gem,
                  ERC20    buy_gem,
                  uint128  pay_amt,
                  uint128  buy_amt
              )
                  public
                  returns (bytes32 id)
              {
                  return bytes32(offer(pay_amt, pay_gem, buy_amt, buy_gem));
              }
          
              // Make a new offer. Takes funds from the caller into market escrow.
              function offer(uint pay_amt, ERC20 pay_gem, uint buy_amt, ERC20 buy_gem)
                  public
                  can_offer
                  synchronized
                  returns (uint id)
              {
                  require(uint128(pay_amt) == pay_amt);
                  require(uint128(buy_amt) == buy_amt);
                  require(pay_amt > 0);
                  require(pay_gem != ERC20(0x0));
                  require(buy_amt > 0);
                  require(buy_gem != ERC20(0x0));
                  require(pay_gem != buy_gem);
          
                  OfferInfo memory info;
                  info.pay_amt = pay_amt;
                  info.pay_gem = pay_gem;
                  info.buy_amt = buy_amt;
                  info.buy_gem = buy_gem;
                  info.owner = msg.sender;
                  info.timestamp = uint64(now);
                  id = _next_id();
                  offers[id] = info;
          
                  require( pay_gem.transferFrom(msg.sender, this, pay_amt) );
          
                  LogItemUpdate(id);
                  LogMake(
                      bytes32(id),
                      keccak256(pay_gem, buy_gem),
                      msg.sender,
                      pay_gem,
                      buy_gem,
                      uint128(pay_amt),
                      uint128(buy_amt),
                      uint64(now)
                  );
              }
          
              function take(bytes32 id, uint128 maxTakeAmount)
                  public
              {
                  require(buy(uint256(id), maxTakeAmount));
              }
          
              function _next_id()
                  internal
                  returns (uint)
              {
                  last_offer_id++; return last_offer_id;
              }
          }
          
          // Simple Market with a market lifetime. When the close_time has been reached,
          // offers can only be cancelled (offer and buy will throw).
          
          contract ExpiringMarket is DSAuth, SimpleMarket {
              uint64 public close_time;
              bool public stopped;
          
              // after close_time has been reached, no new offers are allowed
              modifier can_offer {
                  require(!isClosed());
                  _;
              }
          
              // after close, no new buys are allowed
              modifier can_buy(uint id) {
                  require(isActive(id));
                  require(!isClosed());
                  _;
              }
          
              // after close, anyone can cancel an offer
              modifier can_cancel(uint id) {
                  require(isActive(id));
                  require((msg.sender == getOwner(id)) || isClosed());
                  _;
              }
          
              function ExpiringMarket(uint64 _close_time)
                  public
              {
                  close_time = _close_time;
              }
          
              function isClosed() public constant returns (bool closed) {
                  return stopped || getTime() > close_time;
              }
          
              function getTime() public constant returns (uint64) {
                  return uint64(now);
              }
          
              function stop() public auth {
                  stopped = true;
              }
          }
          
          /// note.sol -- the `note' modifier, for logging calls as events
          
          // This program is free software: you can redistribute it and/or modify
          // it under the terms of the GNU General Public License as published by
          // the Free Software Foundation, either version 3 of the License, or
          // (at your option) any later version.
          
          // This program is distributed in the hope that it will be useful,
          // but WITHOUT ANY WARRANTY; without even the implied warranty of
          // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
          // GNU General Public License for more details.
          
          // You should have received a copy of the GNU General Public License
          // along with this program.  If not, see <http://www.gnu.org/licenses/>.
          
          pragma solidity ^0.4.13;
          
          contract DSNote {
              event LogNote(
                  bytes4   indexed  sig,
                  address  indexed  guy,
                  bytes32  indexed  foo,
                  bytes32  indexed  bar,
                  uint              wad,
                  bytes             fax
              ) anonymous;
          
              modifier note {
                  bytes32 foo;
                  bytes32 bar;
          
                  assembly {
                      foo := calldataload(4)
                      bar := calldataload(36)
                  }
          
                  LogNote(msg.sig, msg.sender, foo, bar, msg.value, msg.data);
          
                  _;
              }
          }
          
          contract MatchingEvents {
              event LogBuyEnabled(bool isEnabled);
              event LogMinSell(address pay_gem, uint min_amount);
              event LogMatchingEnabled(bool isEnabled);
              event LogUnsortedOffer(uint id);
              event LogSortedOffer(uint id);
              event LogInsert(address keeper, uint id);
              event LogDelete(address keeper, uint id);
          }
          
          contract MatchingMarket is MatchingEvents, ExpiringMarket, DSNote {
              bool public buyEnabled = true;      //buy enabled
              bool public matchingEnabled = true; //true: enable matching,
                                                   //false: revert to expiring market
              struct sortInfo {
                  uint next;  //points to id of next higher offer
                  uint prev;  //points to id of previous lower offer
                  uint delb;  //the blocknumber where this entry was marked for delete
              }
              mapping(uint => sortInfo) public _rank;                     //doubly linked lists of sorted offer ids
              mapping(address => mapping(address => uint)) public _best;  //id of the highest offer for a token pair
              mapping(address => mapping(address => uint)) public _span;  //number of offers stored for token pair in sorted orderbook
              mapping(address => uint) public _dust;                      //minimum sell amount for a token to avoid dust offers
              mapping(uint => uint) public _near;         //next unsorted offer id
              uint _head;                                 //first unsorted offer id
              uint public dustId;                         // id of the latest offer marked as dust
          
          
              function MatchingMarket(uint64 close_time) ExpiringMarket(close_time) public {
              }
          
              // After close, anyone can cancel an offer
              modifier can_cancel(uint id) {
                  require(isActive(id), "Offer was deleted or taken, or never existed.");
                  require(
                      isClosed() || msg.sender == getOwner(id) || id == dustId,
                      "Offer can not be cancelled because user is not owner, and market is open, and offer sells required amount of tokens."
                  );
                  _;
              }
          
              // ---- Public entrypoints ---- //
          
              function make(
                  ERC20    pay_gem,
                  ERC20    buy_gem,
                  uint128  pay_amt,
                  uint128  buy_amt
              )
                  public
                  returns (bytes32)
              {
                  return bytes32(offer(pay_amt, pay_gem, buy_amt, buy_gem));
              }
          
              function take(bytes32 id, uint128 maxTakeAmount) public {
                  require(buy(uint256(id), maxTakeAmount));
              }
          
              function kill(bytes32 id) public {
                  require(cancel(uint256(id)));
              }
          
              // Make a new offer. Takes funds from the caller into market escrow.
              //
              // If matching is enabled:
              //     * creates new offer without putting it in
              //       the sorted list.
              //     * available to authorized contracts only!
              //     * keepers should call insert(id,pos)
              //       to put offer in the sorted list.
              //
              // If matching is disabled:
              //     * calls expiring market's offer().
              //     * available to everyone without authorization.
              //     * no sorting is done.
              //
              function offer(
                  uint pay_amt,    //maker (ask) sell how much
                  ERC20 pay_gem,   //maker (ask) sell which token
                  uint buy_amt,    //taker (ask) buy how much
                  ERC20 buy_gem    //taker (ask) buy which token
              )
                  public
                  returns (uint)
              {
                  require(!locked, "Reentrancy attempt");
                  var fn = matchingEnabled ? _offeru : super.offer;
                  return fn(pay_amt, pay_gem, buy_amt, buy_gem);
              }
          
              // Make a new offer. Takes funds from the caller into market escrow.
              function offer(
                  uint pay_amt,    //maker (ask) sell how much
                  ERC20 pay_gem,   //maker (ask) sell which token
                  uint buy_amt,    //maker (ask) buy how much
                  ERC20 buy_gem,   //maker (ask) buy which token
                  uint pos         //position to insert offer, 0 should be used if unknown
              )
                  public
                  can_offer
                  returns (uint)
              {
                  return offer(pay_amt, pay_gem, buy_amt, buy_gem, pos, true);
              }
          
              function offer(
                  uint pay_amt,    //maker (ask) sell how much
                  ERC20 pay_gem,   //maker (ask) sell which token
                  uint buy_amt,    //maker (ask) buy how much
                  ERC20 buy_gem,   //maker (ask) buy which token
                  uint pos,        //position to insert offer, 0 should be used if unknown
                  bool rounding    //match "close enough" orders?
              )
                  public
                  can_offer
                  returns (uint)
              {
                  require(!locked, "Reentrancy attempt");
                  require(_dust[pay_gem] <= pay_amt);
          
                  if (matchingEnabled) {
                    return _matcho(pay_amt, pay_gem, buy_amt, buy_gem, pos, rounding);
                  }
                  return super.offer(pay_amt, pay_gem, buy_amt, buy_gem);
              }
          
              //Transfers funds from caller to offer maker, and from market to caller.
              function buy(uint id, uint amount)
                  public
                  can_buy(id)
                  returns (bool)
              {
                  require(!locked, "Reentrancy attempt");
                  var fn = matchingEnabled ? _buys : super.buy;
                  return fn(id, amount);
              }
          
              // Cancel an offer. Refunds offer maker.
              function cancel(uint id)
                  public
                  can_cancel(id)
                  returns (bool success)
              {
                  require(!locked, "Reentrancy attempt");
                  if (matchingEnabled) {
                      if (isOfferSorted(id)) {
                          require(_unsort(id));
                      } else {
                          require(_hide(id));
                      }
                  }
                  return super.cancel(id);    //delete the offer.
              }
          
              //insert offer into the sorted list
              //keepers need to use this function
              function insert(
                  uint id,   //maker (ask) id
                  uint pos   //position to insert into
              )
                  public
                  returns (bool)
              {
                  require(!locked, "Reentrancy attempt");
                  require(!isOfferSorted(id));    //make sure offers[id] is not yet sorted
                  require(isActive(id));          //make sure offers[id] is active
          
                  _hide(id);                      //remove offer from unsorted offers list
                  _sort(id, pos);                 //put offer into the sorted offers list
                  LogInsert(msg.sender, id);
                  return true;
              }
          
              //deletes _rank [id]
              //  Function should be called by keepers.
              function del_rank(uint id)
                  public
                  returns (bool)
              {
                  require(!locked, "Reentrancy attempt");
                  require(!isActive(id) && _rank[id].delb != 0 && _rank[id].delb < block.number - 10);
                  delete _rank[id];
                  LogDelete(msg.sender, id);
                  return true;
              }
          
              //set the minimum sell amount for a token
              //    Function is used to avoid "dust offers" that have
              //    very small amount of tokens to sell, and it would
              //    cost more gas to accept the offer, than the value
              //    of tokens received.
              function setMinSell(
                  ERC20 pay_gem,     //token to assign minimum sell amount to
                  uint dust          //maker (ask) minimum sell amount
              )
                  public
                  auth
                  note
                  returns (bool)
              {
                  _dust[pay_gem] = dust;
                  LogMinSell(pay_gem, dust);
                  return true;
              }
          
              //returns the minimum sell amount for an offer
              function getMinSell(
                  ERC20 pay_gem      //token for which minimum sell amount is queried
              )
                  public
                  constant
                  returns (uint)
              {
                  return _dust[pay_gem];
              }
          
              //set buy functionality enabled/disabled
              function setBuyEnabled(bool buyEnabled_) public auth returns (bool) {
                  buyEnabled = buyEnabled_;
                  LogBuyEnabled(buyEnabled);
                  return true;
              }
          
              //set matching enabled/disabled
              //    If matchingEnabled true(default), then inserted offers are matched.
              //    Except the ones inserted by contracts, because those end up
              //    in the unsorted list of offers, that must be later sorted by
              //    keepers using insert().
              //    If matchingEnabled is false then MatchingMarket is reverted to ExpiringMarket,
              //    and matching is not done, and sorted lists are disabled.
              function setMatchingEnabled(bool matchingEnabled_) public auth returns (bool) {
                  matchingEnabled = matchingEnabled_;
                  LogMatchingEnabled(matchingEnabled);
                  return true;
              }
          
              //return the best offer for a token pair
              //      the best offer is the lowest one if it's an ask,
              //      and highest one if it's a bid offer
              function getBestOffer(ERC20 sell_gem, ERC20 buy_gem) public constant returns(uint) {
                  return _best[sell_gem][buy_gem];
              }
          
              //return the next worse offer in the sorted list
              //      the worse offer is the higher one if its an ask,
              //      a lower one if its a bid offer,
              //      and in both cases the newer one if they're equal.
              function getWorseOffer(uint id) public constant returns(uint) {
                  return _rank[id].prev;
              }
          
              //return the next better offer in the sorted list
              //      the better offer is in the lower priced one if its an ask,
              //      the next higher priced one if its a bid offer
              //      and in both cases the older one if they're equal.
              function getBetterOffer(uint id) public constant returns(uint) {
          
                  return _rank[id].next;
              }
          
              //return the amount of better offers for a token pair
              function getOfferCount(ERC20 sell_gem, ERC20 buy_gem) public constant returns(uint) {
                  return _span[sell_gem][buy_gem];
              }
          
              //get the first unsorted offer that was inserted by a contract
              //      Contracts can't calculate the insertion position of their offer because it is not an O(1) operation.
              //      Their offers get put in the unsorted list of offers.
              //      Keepers can calculate the insertion position offchain and pass it to the insert() function to insert
              //      the unsorted offer into the sorted list. Unsorted offers will not be matched, but can be bought with buy().
              function getFirstUnsortedOffer() public constant returns(uint) {
                  return _head;
              }
          
              //get the next unsorted offer
              //      Can be used to cycle through all the unsorted offers.
              function getNextUnsortedOffer(uint id) public constant returns(uint) {
                  return _near[id];
              }
          
              function isOfferSorted(uint id) public constant returns(bool) {
                  return _rank[id].next != 0
                         || _rank[id].prev != 0
                         || _best[offers[id].pay_gem][offers[id].buy_gem] == id;
              }
          
              function sellAllAmount(ERC20 pay_gem, uint pay_amt, ERC20 buy_gem, uint min_fill_amount)
                  public
                  returns (uint fill_amt)
              {
                  require(!locked, "Reentrancy attempt");
                  uint offerId;
                  while (pay_amt > 0) {                           //while there is amount to sell
                      offerId = getBestOffer(buy_gem, pay_gem);   //Get the best offer for the token pair
                      require(offerId != 0);                      //Fails if there are not more offers
          
                      // There is a chance that pay_amt is smaller than 1 wei of the other token
                      if (pay_amt * 1 ether < wdiv(offers[offerId].buy_amt, offers[offerId].pay_amt)) {
                          break;                                  //We consider that all amount is sold
                      }
                      if (pay_amt >= offers[offerId].buy_amt) {                       //If amount to sell is higher or equal than current offer amount to buy
                          fill_amt = add(fill_amt, offers[offerId].pay_amt);          //Add amount bought to acumulator
                          pay_amt = sub(pay_amt, offers[offerId].buy_amt);            //Decrease amount to sell
                          take(bytes32(offerId), uint128(offers[offerId].pay_amt));   //We take the whole offer
                      } else { // if lower
                          var baux = rmul(pay_amt * 10 ** 9, rdiv(offers[offerId].pay_amt, offers[offerId].buy_amt)) / 10 ** 9;
                          fill_amt = add(fill_amt, baux);         //Add amount bought to acumulator
                          take(bytes32(offerId), uint128(baux));  //We take the portion of the offer that we need
                          pay_amt = 0;                            //All amount is sold
                      }
                  }
                  require(fill_amt >= min_fill_amount);
              }
          
              function buyAllAmount(ERC20 buy_gem, uint buy_amt, ERC20 pay_gem, uint max_fill_amount)
                  public
                  returns (uint fill_amt)
              {
                  require(!locked, "Reentrancy attempt");
                  uint offerId;
                  while (buy_amt > 0) {                           //Meanwhile there is amount to buy
                      offerId = getBestOffer(buy_gem, pay_gem);   //Get the best offer for the token pair
                      require(offerId != 0);
          
                      // There is a chance that buy_amt is smaller than 1 wei of the other token
                      if (buy_amt * 1 ether < wdiv(offers[offerId].pay_amt, offers[offerId].buy_amt)) {
                          break;                                  //We consider that all amount is sold
                      }
                      if (buy_amt >= offers[offerId].pay_amt) {                       //If amount to buy is higher or equal than current offer amount to sell
                          fill_amt = add(fill_amt, offers[offerId].buy_amt);          //Add amount sold to acumulator
                          buy_amt = sub(buy_amt, offers[offerId].pay_amt);            //Decrease amount to buy
                          take(bytes32(offerId), uint128(offers[offerId].pay_amt));   //We take the whole offer
                      } else {                                                        //if lower
                          fill_amt = add(fill_amt, rmul(buy_amt * 10 ** 9, rdiv(offers[offerId].buy_amt, offers[offerId].pay_amt)) / 10 ** 9); //Add amount sold to acumulator
                          take(bytes32(offerId), uint128(buy_amt));                   //We take the portion of the offer that we need
                          buy_amt = 0;                                                //All amount is bought
                      }
                  }
                  require(fill_amt <= max_fill_amount);
              }
          
              function getBuyAmount(ERC20 buy_gem, ERC20 pay_gem, uint pay_amt) public constant returns (uint fill_amt) {
                  var offerId = getBestOffer(buy_gem, pay_gem);           //Get best offer for the token pair
                  while (pay_amt > offers[offerId].buy_amt) {
                      fill_amt = add(fill_amt, offers[offerId].pay_amt);  //Add amount to buy accumulator
                      pay_amt = sub(pay_amt, offers[offerId].buy_amt);    //Decrease amount to pay
                      if (pay_amt > 0) {                                  //If we still need more offers
                          offerId = getWorseOffer(offerId);               //We look for the next best offer
                          require(offerId != 0);                          //Fails if there are not enough offers to complete
                      }
                  }
                  fill_amt = add(fill_amt, rmul(pay_amt * 10 ** 9, rdiv(offers[offerId].pay_amt, offers[offerId].buy_amt)) / 10 ** 9); //Add proportional amount of last offer to buy accumulator
              }
          
              function getPayAmount(ERC20 pay_gem, ERC20 buy_gem, uint buy_amt) public constant returns (uint fill_amt) {
                  var offerId = getBestOffer(buy_gem, pay_gem);           //Get best offer for the token pair
                  while (buy_amt > offers[offerId].pay_amt) {
                      fill_amt = add(fill_amt, offers[offerId].buy_amt);  //Add amount to pay accumulator
                      buy_amt = sub(buy_amt, offers[offerId].pay_amt);    //Decrease amount to buy
                      if (buy_amt > 0) {                                  //If we still need more offers
                          offerId = getWorseOffer(offerId);               //We look for the next best offer
                          require(offerId != 0);                          //Fails if there are not enough offers to complete
                      }
                  }
                  fill_amt = add(fill_amt, rmul(buy_amt * 10 ** 9, rdiv(offers[offerId].buy_amt, offers[offerId].pay_amt)) / 10 ** 9); //Add proportional amount of last offer to pay accumulator
              }
          
              // ---- Internal Functions ---- //
          
              function _buys(uint id, uint amount)
                  internal
                  returns (bool)
              {
                  require(buyEnabled);
                  if (amount == offers[id].pay_amt) {
                      if (isOfferSorted(id)) {
                          //offers[id] must be removed from sorted list because all of it is bought
                          _unsort(id);
                      }else{
                          _hide(id);
                      }
                  }
                  require(super.buy(id, amount));
                  // If offer has become dust during buy, we cancel it
                  if (isActive(id) && offers[id].pay_amt < _dust[offers[id].pay_gem]) {
                      dustId = id; //enable current msg.sender to call cancel(id)
                      cancel(id);
                  }
                  return true;
              }
          
              //find the id of the next higher offer after offers[id]
              function _find(uint id)
                  internal
                  view
                  returns (uint)
              {
                  require( id > 0 );
          
                  address buy_gem = address(offers[id].buy_gem);
                  address pay_gem = address(offers[id].pay_gem);
                  uint top = _best[pay_gem][buy_gem];
                  uint old_top = 0;
          
                  // Find the larger-than-id order whose successor is less-than-id.
                  while (top != 0 && _isPricedLtOrEq(id, top)) {
                      old_top = top;
                      top = _rank[top].prev;
                  }
                  return old_top;
              }
          
              //find the id of the next higher offer after offers[id]
              function _findpos(uint id, uint pos)
                  internal
                  view
                  returns (uint)
              {
                  require(id > 0);
          
                  // Look for an active order.
                  while (pos != 0 && !isActive(pos)) {
                      pos = _rank[pos].prev;
                  }
          
                  if (pos == 0) {
                      //if we got to the end of list without a single active offer
                      return _find(id);
          
                  } else {
                      // if we did find a nearby active offer
                      // Walk the order book down from there...
                      if(_isPricedLtOrEq(id, pos)) {
                          uint old_pos;
          
                          // Guaranteed to run at least once because of
                          // the prior if statements.
                          while (pos != 0 && _isPricedLtOrEq(id, pos)) {
                              old_pos = pos;
                              pos = _rank[pos].prev;
                          }
                          return old_pos;
          
                      // ...or walk it up.
                      } else {
                          while (pos != 0 && !_isPricedLtOrEq(id, pos)) {
                              pos = _rank[pos].next;
                          }
                          return pos;
                      }
                  }
              }
          
              //return true if offers[low] priced less than or equal to offers[high]
              function _isPricedLtOrEq(
                  uint low,   //lower priced offer's id
                  uint high   //higher priced offer's id
              )
                  internal
                  view
                  returns (bool)
              {
                  return mul(offers[low].buy_amt, offers[high].pay_amt)
                    >= mul(offers[high].buy_amt, offers[low].pay_amt);
              }
          
              //these variables are global only because of solidity local variable limit
          
              //match offers with taker offer, and execute token transactions
              function _matcho(
                  uint t_pay_amt,    //taker sell how much
                  ERC20 t_pay_gem,   //taker sell which token
                  uint t_buy_amt,    //taker buy how much
                  ERC20 t_buy_gem,   //taker buy which token
                  uint pos,          //position id
                  bool rounding      //match "close enough" orders?
              )
                  internal
                  returns (uint id)
              {
                  uint best_maker_id;    //highest maker id
                  uint t_buy_amt_old;    //taker buy how much saved
                  uint m_buy_amt;        //maker offer wants to buy this much token
                  uint m_pay_amt;        //maker offer wants to sell this much token
          
                  // there is at least one offer stored for token pair
                  while (_best[t_buy_gem][t_pay_gem] > 0) {
                      best_maker_id = _best[t_buy_gem][t_pay_gem];
                      m_buy_amt = offers[best_maker_id].buy_amt;
                      m_pay_amt = offers[best_maker_id].pay_amt;
          
                      // Ugly hack to work around rounding errors. Based on the idea that
                      // the furthest the amounts can stray from their "true" values is 1.
                      // Ergo the worst case has t_pay_amt and m_pay_amt at +1 away from
                      // their "correct" values and m_buy_amt and t_buy_amt at -1.
                      // Since (c - 1) * (d - 1) > (a + 1) * (b + 1) is equivalent to
                      // c * d > a * b + a + b + c + d, we write...
                      if (mul(m_buy_amt, t_buy_amt) > mul(t_pay_amt, m_pay_amt) +
                          (rounding ? m_buy_amt + t_buy_amt + t_pay_amt + m_pay_amt : 0))
                      {
                          break;
                      }
                      // ^ The `rounding` parameter is a compromise borne of a couple days
                      // of discussion.
                      buy(best_maker_id, min(m_pay_amt, t_buy_amt));
                      t_buy_amt_old = t_buy_amt;
                      t_buy_amt = sub(t_buy_amt, min(m_pay_amt, t_buy_amt));
                      t_pay_amt = mul(t_buy_amt, t_pay_amt) / t_buy_amt_old;
          
                      if (t_pay_amt == 0 || t_buy_amt == 0) {
                          break;
                      }
                  }
          
                  if (t_buy_amt > 0 && t_pay_amt > 0 && t_pay_amt >= _dust[t_pay_gem]) {
                      //new offer should be created
                      id = super.offer(t_pay_amt, t_pay_gem, t_buy_amt, t_buy_gem);
                      //insert offer into the sorted list
                      _sort(id, pos);
                  }
              }
          
              // Make a new offer without putting it in the sorted list.
              // Takes funds from the caller into market escrow.
              // ****Available to authorized contracts only!**********
              // Keepers should call insert(id,pos) to put offer in the sorted list.
              function _offeru(
                  uint pay_amt,      //maker (ask) sell how much
                  ERC20 pay_gem,     //maker (ask) sell which token
                  uint buy_amt,      //maker (ask) buy how much
                  ERC20 buy_gem      //maker (ask) buy which token
              )
                  internal
                  returns (uint id)
              {
                  require(_dust[pay_gem] <= pay_amt);
                  id = super.offer(pay_amt, pay_gem, buy_amt, buy_gem);
                  _near[id] = _head;
                  _head = id;
                  LogUnsortedOffer(id);
              }
          
              //put offer into the sorted list
              function _sort(
                  uint id,    //maker (ask) id
                  uint pos    //position to insert into
              )
                  internal
              {
                  require(isActive(id));
          
                  address buy_gem = address(offers[id].buy_gem);
                  address pay_gem = address(offers[id].pay_gem);
                  uint prev_id;                                      //maker (ask) id
          
                  pos = pos == 0 || offers[pos].pay_gem != pay_gem || offers[pos].buy_gem != buy_gem || !isOfferSorted(pos)
                  ?
                      _find(id)
                  :
                      _findpos(id, pos);
          
                  if (pos != 0) {                                    //offers[id] is not the highest offer
                      //requirement below is satisfied by statements above
                      //require(_isPricedLtOrEq(id, pos));
                      prev_id = _rank[pos].prev;
                      _rank[pos].prev = id;
                      _rank[id].next = pos;
                  } else {                                           //offers[id] is the highest offer
                      prev_id = _best[pay_gem][buy_gem];
                      _best[pay_gem][buy_gem] = id;
                  }
          
                  if (prev_id != 0) {                               //if lower offer does exist
                      //requirement below is satisfied by statements above
                      //require(!_isPricedLtOrEq(id, prev_id));
                      _rank[prev_id].next = id;
                      _rank[id].prev = prev_id;
                  }
          
                  _span[pay_gem][buy_gem]++;
                  LogSortedOffer(id);
              }
          
              // Remove offer from the sorted list (does not cancel offer)
              function _unsort(
                  uint id    //id of maker (ask) offer to remove from sorted list
              )
                  internal
                  returns (bool)
              {
                  address buy_gem = address(offers[id].buy_gem);
                  address pay_gem = address(offers[id].pay_gem);
                  require(_span[pay_gem][buy_gem] > 0);
          
                  require(_rank[id].delb == 0 &&                    //assert id is in the sorted list
                           isOfferSorted(id));
          
                  if (id != _best[pay_gem][buy_gem]) {              // offers[id] is not the highest offer
                      require(_rank[_rank[id].next].prev == id);
                      _rank[_rank[id].next].prev = _rank[id].prev;
                  } else {                                          //offers[id] is the highest offer
                      _best[pay_gem][buy_gem] = _rank[id].prev;
                  }
          
                  if (_rank[id].prev != 0) {                        //offers[id] is not the lowest offer
                      require(_rank[_rank[id].prev].next == id);
                      _rank[_rank[id].prev].next = _rank[id].next;
                  }
          
                  _span[pay_gem][buy_gem]--;
                  _rank[id].delb = block.number;                    //mark _rank[id] for deletion
                  return true;
              }
          
              //Hide offer from the unsorted order book (does not cancel offer)
              function _hide(
                  uint id     //id of maker offer to remove from unsorted list
              )
                  internal
                  returns (bool)
              {
                  uint uid = _head;               //id of an offer in unsorted offers list
                  uint pre = uid;                 //id of previous offer in unsorted offers list
          
                  require(!isOfferSorted(id));    //make sure offer id is not in sorted offers list
          
                  if (_head == id) {              //check if offer is first offer in unsorted offers list
                      _head = _near[id];          //set head to new first unsorted offer
                      _near[id] = 0;              //delete order from unsorted order list
                      return true;
                  }
                  while (uid > 0 && uid != id) {  //find offer in unsorted order list
                      pre = uid;
                      uid = _near[uid];
                  }
                  if (uid != id) {                //did not find offer id in unsorted offers list
                      return false;
                  }
                  _near[pre] = _near[id];         //set previous unsorted offer to point to offer after offer id
                  _near[id] = 0;                  //delete order from unsorted order list
                  return true;
              }
          }