ETH Price: $2,003.44 (+0.27%)

Transaction Decoder

Block:
8754976 at Oct-16-2019 10:49:14 PM +UTC
Transaction Fee:
0.00134406 ETH $2.69
Gas Used:
44,802 Gas / 30 Gwei

Account State Difference:

  Address   Before After State Difference Code
0x4E0c6603...2655e5906
3.724594488394247881 Eth
Nonce: 11559
3.723250428394247881 Eth
Nonce: 11560
0.00134406
(Spark Pool)
60.32549895469109974 Eth60.32684301469109974 Eth0.00134406

Execution Trace

0x1200563cfaeb481e1f0542a246f0cefcde4c4753.689c49c0( )
  • Forwarder.cd7724c3( )
    • Vyper_contract.getEthToTokenInputPrice( eth_sold=2883207678347402109 ) => ( out=1045880662187136993 )
      • DSToken.balanceOf( src=0x2C4Bd064b998838076fa341A83d007FC2FA50957 ) => ( 3439543523851156519969 )
      • MatchingMarket.getOffer( id=521195 ) => ( 10999999999999997, 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, 4011688345654767, 0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2 )
        File 1 of 4: Forwarder
        pragma solidity ^0.4.14;
        
        /**
         * Contract that exposes the needed erc20 token functions
         */
        
        contract ERC20Interface {
          // Send _value amount of tokens to address _to
          function transfer(address _to, uint256 _value) returns (bool success);
          // Get the account balance of another account with address _owner
          function balanceOf(address _owner) constant returns (uint256 balance);
        }
        
        /**
         * Contract that will forward any incoming Ether to its creator
         */
        contract Forwarder {
          // Address to which any funds sent to this contract will be forwarded
          address public parentAddress;
          event ForwarderDeposited(address from, uint value, bytes data);
        
          event TokensFlushed(
            address tokenContractAddress, // The contract address of the token
            uint value // Amount of token sent
          );
        
          /**
           * Create the contract, and set the destination address to that of the creator
           */
          function Forwarder() {
            parentAddress = msg.sender;
          }
        
          /**
           * Modifier that will execute internal code block only if the sender is a parent of the forwarder contract
           */
          modifier onlyParent {
            if (msg.sender != parentAddress) {
              throw;
            }
            _;
          }
        
          /**
           * Default function; Gets called when Ether is deposited, and forwards it to the destination address
           */
          function() payable {
            if (!parentAddress.call.value(msg.value)(msg.data))
              throw;
            // Fire off the deposited event if we can forward it  
            ForwarderDeposited(msg.sender, msg.value, msg.data);
          }
        
          /**
           * Execute a token transfer of the full balance from the forwarder token to the main wallet contract
           * @param tokenContractAddress the address of the erc20 token contract
           */
          function flushTokens(address tokenContractAddress) onlyParent {
            ERC20Interface instance = ERC20Interface(tokenContractAddress);
            var forwarderAddress = address(this);
            var forwarderBalance = instance.balanceOf(forwarderAddress);
            if (forwarderBalance == 0) {
              return;
            }
            if (!instance.transfer(parentAddress, forwarderBalance)) {
              throw;
            }
            TokensFlushed(tokenContractAddress, forwarderBalance);
          }
        
          /**
           * It is possible that funds were sent to this address before the contract was deployed.
           * We can flush those funds to the destination address.
           */
          function flush() {
            if (!parentAddress.call.value(this.balance)())
              throw;
          }
        }
        
        /**
         * Basic multi-signer wallet designed for use in a co-signing environment where 2 signatures are required to move funds.
         * Typically used in a 2-of-3 signing configuration. Uses ecrecover to allow for 2 signatures in a single transaction.
         */
        contract WalletSimple {
          // Events
          event Deposited(address from, uint value, bytes data);
          event SafeModeActivated(address msgSender);
          event Transacted(
            address msgSender, // Address of the sender of the message initiating the transaction
            address otherSigner, // Address of the signer (second signature) used to initiate the transaction
            bytes32 operation, // Operation hash (sha3 of toAddress, value, data, expireTime, sequenceId)
            address toAddress, // The address the transaction was sent to
            uint value, // Amount of Wei sent to the address
            bytes data // Data sent when invoking the transaction
          );
          event TokenTransacted(
            address msgSender, // Address of the sender of the message initiating the transaction
            address otherSigner, // Address of the signer (second signature) used to initiate the transaction
            bytes32 operation, // Operation hash (sha3 of toAddress, value, tokenContractAddress, expireTime, sequenceId)
            address toAddress, // The address the transaction was sent to
            uint value, // Amount of token sent
            address tokenContractAddress // The contract address of the token
          );
        
          // Public fields
          address[] public signers; // The addresses that can co-sign transactions on the wallet
          bool public safeMode = false; // When active, wallet may only send to signer addresses
        
          // Internal fields
          uint constant SEQUENCE_ID_WINDOW_SIZE = 10;
          uint[10] recentSequenceIds;
        
          /**
           * Modifier that will execute internal code block only if the sender is an authorized signer on this wallet
           */
          modifier onlysigner {
            if (!isSigner(msg.sender)) {
              throw;
            }
            _;
          }
        
          /**
           * Set up a simple multi-sig wallet by specifying the signers allowed to be used on this wallet.
           * 2 signers will be required to send a transaction from this wallet.
           * Note: The sender is NOT automatically added to the list of signers.
           * Signers CANNOT be changed once they are set
           *
           * @param allowedSigners An array of signers on the wallet
           */
          function WalletSimple(address[] allowedSigners) {
            if (allowedSigners.length != 3) {
              // Invalid number of signers
              throw;
            }
            signers = allowedSigners;
          }
        
          /**
           * Gets called when a transaction is received without calling a method
           */
          function() payable {
            if (msg.value > 0) {
              // Fire deposited event if we are receiving funds
              Deposited(msg.sender, msg.value, msg.data);
            }
          }
        
          /**
           * Create a new contract (and also address) that forwards funds to this contract
           * returns address of newly created forwarder address
           */
          function createForwarder() onlysigner returns (address) {
            return new Forwarder();
          }
        
          /**
           * Execute a multi-signature transaction from this wallet using 2 signers: one from msg.sender and the other from ecrecover.
           * The signature is a signed form (using eth.sign) of tightly packed toAddress, value, data, expireTime and sequenceId
           * Sequence IDs are numbers starting from 1. They are used to prevent replay attacks and may not be repeated.
           *
           * @param toAddress the destination address to send an outgoing transaction
           * @param value the amount in Wei to be sent
           * @param data the data to send to the toAddress when invoking the transaction
           * @param expireTime the number of seconds since 1970 for which this transaction is valid
           * @param sequenceId the unique sequence id obtainable from getNextSequenceId
           * @param signature the result of eth.sign on the operationHash sha3(toAddress, value, data, expireTime, sequenceId)
           */
          function sendMultiSig(address toAddress, uint value, bytes data, uint expireTime, uint sequenceId, bytes signature) onlysigner {
            // Verify the other signer
            var operationHash = sha3("ETHER", toAddress, value, data, expireTime, sequenceId);
            
            var otherSigner = verifyMultiSig(toAddress, operationHash, signature, expireTime, sequenceId);
        
            // Success, send the transaction
            if (!(toAddress.call.value(value)(data))) {
              // Failed executing transaction
              throw;
            }
            Transacted(msg.sender, otherSigner, operationHash, toAddress, value, data);
          }
          
          /**
           * Execute a multi-signature token transfer from this wallet using 2 signers: one from msg.sender and the other from ecrecover.
           * The signature is a signed form (using eth.sign) of tightly packed toAddress, value, tokenContractAddress, expireTime and sequenceId
           * Sequence IDs are numbers starting from 1. They are used to prevent replay attacks and may not be repeated.
           *
           * @param toAddress the destination address to send an outgoing transaction
           * @param value the amount in tokens to be sent
           * @param tokenContractAddress the address of the erc20 token contract
           * @param expireTime the number of seconds since 1970 for which this transaction is valid
           * @param sequenceId the unique sequence id obtainable from getNextSequenceId
           * @param signature the result of eth.sign on the operationHash sha3(toAddress, value, tokenContractAddress, expireTime, sequenceId)
           */
          function sendMultiSigToken(address toAddress, uint value, address tokenContractAddress, uint expireTime, uint sequenceId, bytes signature) onlysigner {
            // Verify the other signer
            var operationHash = sha3("ERC20", toAddress, value, tokenContractAddress, expireTime, sequenceId);
            
            var otherSigner = verifyMultiSig(toAddress, operationHash, signature, expireTime, sequenceId);
            
            ERC20Interface instance = ERC20Interface(tokenContractAddress);
            if (!instance.transfer(toAddress, value)) {
                throw;
            }
            TokenTransacted(msg.sender, otherSigner, operationHash, toAddress, value, tokenContractAddress);
          }
        
          /**
           * Execute a token flush from one of the forwarder addresses. This transfer needs only a single signature and can be done by any signer
           *
           * @param forwarderAddress the address of the forwarder address to flush the tokens from
           * @param tokenContractAddress the address of the erc20 token contract
           */
          function flushForwarderTokens(address forwarderAddress, address tokenContractAddress) onlysigner {    
            Forwarder forwarder = Forwarder(forwarderAddress);
            forwarder.flushTokens(tokenContractAddress);
          }  
          
          /**
           * Do common multisig verification for both eth sends and erc20token transfers
           *
           * @param toAddress the destination address to send an outgoing transaction
           * @param operationHash the sha3 of the toAddress, value, data/tokenContractAddress and expireTime
           * @param signature the tightly packed signature of r, s, and v as an array of 65 bytes (returned by eth.sign)
           * @param expireTime the number of seconds since 1970 for which this transaction is valid
           * @param sequenceId the unique sequence id obtainable from getNextSequenceId
           * returns address of the address to send tokens or eth to
           */
          function verifyMultiSig(address toAddress, bytes32 operationHash, bytes signature, uint expireTime, uint sequenceId) private returns (address) {
        
            var otherSigner = recoverAddressFromSignature(operationHash, signature);
        
            // Verify if we are in safe mode. In safe mode, the wallet can only send to signers
            if (safeMode && !isSigner(toAddress)) {
              // We are in safe mode and the toAddress is not a signer. Disallow!
              throw;
            }
            // Verify that the transaction has not expired
            if (expireTime < block.timestamp) {
              // Transaction expired
              throw;
            }
        
            // Try to insert the sequence ID. Will throw if the sequence id was invalid
            tryInsertSequenceId(sequenceId);
        
            if (!isSigner(otherSigner)) {
              // Other signer not on this wallet or operation does not match arguments
              throw;
            }
            if (otherSigner == msg.sender) {
              // Cannot approve own transaction
              throw;
            }
        
            return otherSigner;
          }
        
          /**
           * Irrevocably puts contract into safe mode. When in this mode, transactions may only be sent to signing addresses.
           */
          function activateSafeMode() onlysigner {
            safeMode = true;
            SafeModeActivated(msg.sender);
          }
        
          /**
           * Determine if an address is a signer on this wallet
           * @param signer address to check
           * returns boolean indicating whether address is signer or not
           */
          function isSigner(address signer) returns (bool) {
            // Iterate through all signers on the wallet and
            for (uint i = 0; i < signers.length; i++) {
              if (signers[i] == signer) {
                return true;
              }
            }
            return false;
          }
        
          /**
           * Gets the second signer's address using ecrecover
           * @param operationHash the sha3 of the toAddress, value, data/tokenContractAddress and expireTime
           * @param signature the tightly packed signature of r, s, and v as an array of 65 bytes (returned by eth.sign)
           * returns address recovered from the signature
           */
          function recoverAddressFromSignature(bytes32 operationHash, bytes signature) private returns (address) {
            if (signature.length != 65) {
              throw;
            }
            // We need to unpack the signature, which is given as an array of 65 bytes (from eth.sign)
            bytes32 r;
            bytes32 s;
            uint8 v;
            assembly {
              r := mload(add(signature, 32))
              s := mload(add(signature, 64))
              v := and(mload(add(signature, 65)), 255)
            }
            if (v < 27) {
              v += 27; // Ethereum versions are 27 or 28 as opposed to 0 or 1 which is submitted by some signing libs
            }
            return ecrecover(operationHash, v, r, s);
          }
        
          /**
           * Verify that the sequence id has not been used before and inserts it. Throws if the sequence ID was not accepted.
           * We collect a window of up to 10 recent sequence ids, and allow any sequence id that is not in the window and
           * greater than the minimum element in the window.
           * @param sequenceId to insert into array of stored ids
           */
          function tryInsertSequenceId(uint sequenceId) onlysigner private {
            // Keep a pointer to the lowest value element in the window
            uint lowestValueIndex = 0;
            for (uint i = 0; i < SEQUENCE_ID_WINDOW_SIZE; i++) {
              if (recentSequenceIds[i] == sequenceId) {
                // This sequence ID has been used before. Disallow!
                throw;
              }
              if (recentSequenceIds[i] < recentSequenceIds[lowestValueIndex]) {
                lowestValueIndex = i;
              }
            }
            if (sequenceId < recentSequenceIds[lowestValueIndex]) {
              // The sequence ID being used is lower than the lowest value in the window
              // so we cannot accept it as it may have been used before
              throw;
            }
            if (sequenceId > (recentSequenceIds[lowestValueIndex] + 10000)) {
              // Block sequence IDs which are much higher than the lowest value
              // This prevents people blocking the contract by using very large sequence IDs quickly
              throw;
            }
            recentSequenceIds[lowestValueIndex] = sequenceId;
          }
        
          /**
           * Gets the next available sequence ID for signing when using executeAndConfirm
           * returns the sequenceId one higher than the highest currently stored
           */
          function getNextSequenceId() returns (uint) {
            uint highestSequenceId = 0;
            for (uint i = 0; i < SEQUENCE_ID_WINDOW_SIZE; i++) {
              if (recentSequenceIds[i] > highestSequenceId) {
                highestSequenceId = recentSequenceIds[i];
              }
            }
            return highestSequenceId + 1;
          }
        }

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

        File 3 of 4: DSToken
        // MKR Token
        
        // hevm: flattened sources of src/mkr-499.sol
        pragma solidity ^0.4.15;
        
        ////// lib/ds-roles/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-thing/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-thing/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-thing/src/thing.sol
        // thing.sol - `auth` with handy mixins. your things should be DSThings
        
        // 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'; */
        /* import 'ds-math/math.sol'; */
        
        contract DSThing is DSAuth, DSNote, DSMath {
        }
        
        ////// lib/ds-token/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/ds-token/lib/erc20/src/erc20.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.8; */
        
        // Token standard API
        // https://github.com/ethereum/EIPs/issues/20
        
        contract ERC20 {
            function totalSupply() public view returns (uint supply);
            function balanceOf( address who ) public view returns (uint value);
            function allowance( address owner, address spender ) public view returns (uint _allowance);
        
            function transfer( address to, uint value) public returns (bool ok);
            function transferFrom( address from, address to, uint value) public returns (bool ok);
            function approve( address spender, uint value ) public returns (bool ok);
        
            event Transfer( address indexed from, address indexed to, uint value);
            event Approval( address indexed owner, address indexed spender, uint value);
        }
        
        ////// lib/ds-token/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;
            }
        }
        
        ////// lib/ds-token/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 4: 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;
            }
        }