github.com/pdaian/flashboys2@v0.0.0-20190718175736-b101c35361f0/exchanges.py (about)

     1  import json
     2  from persistence import persist_to_file
     3  import web3
     4  from web3 import Web3
     5  from eth_abi import decode_abi
     6  
     7  ERC20_ABI = json.loads('[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_spender","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Approval","type":"event"}]')  # noqa: 501
     8  UNISWAP_ABI = json.loads('[{"name": "TokenPurchase", "inputs": [{"type": "address", "name": "buyer", "indexed": true}, {"type": "uint256", "name": "eth_sold", "indexed": true}, {"type": "uint256", "name": "tokens_bought", "indexed": true}], "anonymous": false, "type": "event"}, {"name": "EthPurchase", "inputs": [{"type": "address", "name": "buyer", "indexed": true}, {"type": "uint256", "name": "tokens_sold", "indexed": true}, {"type": "uint256", "name": "eth_bought", "indexed": true}], "anonymous": false, "type": "event"}, {"name": "AddLiquidity", "inputs": [{"type": "address", "name": "provider", "indexed": true}, {"type": "uint256", "name": "eth_amount", "indexed": true}, {"type": "uint256", "name": "token_amount", "indexed": true}], "anonymous": false, "type": "event"}, {"name": "RemoveLiquidity", "inputs": [{"type": "address", "name": "provider", "indexed": true}, {"type": "uint256", "name": "eth_amount", "indexed": true}, {"type": "uint256", "name": "token_amount", "indexed": true}], "anonymous": false, "type": "event"}, {"name": "Transfer", "inputs": [{"type": "address", "name": "_from", "indexed": true}, {"type": "address", "name": "_to", "indexed": true}, {"type": "uint256", "name": "_value", "indexed": false}], "anonymous": false, "type": "event"}, {"name": "Approval", "inputs": [{"type": "address", "name": "_owner", "indexed": true}, {"type": "address", "name": "_spender", "indexed": true}, {"type": "uint256", "name": "_value", "indexed": false}], "anonymous": false, "type": "event"}, {"name": "setup", "outputs": [], "inputs": [{"type": "address", "name": "token_addr"}], "constant": false, "payable": false, "type": "function", "gas": 175875}, {"name": "addLiquidity", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "min_liquidity"}, {"type": "uint256", "name": "max_tokens"}, {"type": "uint256", "name": "deadline"}], "constant": false, "payable": true, "type": "function", "gas": 82616}, {"name": "removeLiquidity", "outputs": [{"type": "uint256", "name": "out"}, {"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "amount"}, {"type": "uint256", "name": "min_eth"}, {"type": "uint256", "name": "min_tokens"}, {"type": "uint256", "name": "deadline"}], "constant": false, "payable": false, "type": "function", "gas": 116814}, {"name": "__default__", "outputs": [], "inputs": [], "constant": false, "payable": true, "type": "function"}, {"name": "ethToTokenSwapInput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "min_tokens"}, {"type": "uint256", "name": "deadline"}], "constant": false, "payable": true, "type": "function", "gas": 12757}, {"name": "ethToTokenTransferInput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "min_tokens"}, {"type": "uint256", "name": "deadline"}, {"type": "address", "name": "recipient"}], "constant": false, "payable": true, "type": "function", "gas": 12965}, {"name": "ethToTokenSwapOutput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "tokens_bought"}, {"type": "uint256", "name": "deadline"}], "constant": false, "payable": true, "type": "function", "gas": 50463}, {"name": "ethToTokenTransferOutput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "tokens_bought"}, {"type": "uint256", "name": "deadline"}, {"type": "address", "name": "recipient"}], "constant": false, "payable": true, "type": "function", "gas": 50671}, {"name": "tokenToEthSwapInput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "tokens_sold"}, {"type": "uint256", "name": "min_eth"}, {"type": "uint256", "name": "deadline"}], "constant": false, "payable": false, "type": "function", "gas": 47503}, {"name": "tokenToEthTransferInput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "tokens_sold"}, {"type": "uint256", "name": "min_eth"}, {"type": "uint256", "name": "deadline"}, {"type": "address", "name": "recipient"}], "constant": false, "payable": false, "type": "function", "gas": 47712}, {"name": "tokenToEthSwapOutput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "eth_bought"}, {"type": "uint256", "name": "max_tokens"}, {"type": "uint256", "name": "deadline"}], "constant": false, "payable": false, "type": "function", "gas": 50175}, {"name": "tokenToEthTransferOutput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "eth_bought"}, {"type": "uint256", "name": "max_tokens"}, {"type": "uint256", "name": "deadline"}, {"type": "address", "name": "recipient"}], "constant": false, "payable": false, "type": "function", "gas": 50384}, {"name": "tokenToTokenSwapInput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "tokens_sold"}, {"type": "uint256", "name": "min_tokens_bought"}, {"type": "uint256", "name": "min_eth_bought"}, {"type": "uint256", "name": "deadline"}, {"type": "address", "name": "token_addr"}], "constant": false, "payable": false, "type": "function", "gas": 51007}, {"name": "tokenToTokenTransferInput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "tokens_sold"}, {"type": "uint256", "name": "min_tokens_bought"}, {"type": "uint256", "name": "min_eth_bought"}, {"type": "uint256", "name": "deadline"}, {"type": "address", "name": "recipient"}, {"type": "address", "name": "token_addr"}], "constant": false, "payable": false, "type": "function", "gas": 51098}, {"name": "tokenToTokenSwapOutput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "tokens_bought"}, {"type": "uint256", "name": "max_tokens_sold"}, {"type": "uint256", "name": "max_eth_sold"}, {"type": "uint256", "name": "deadline"}, {"type": "address", "name": "token_addr"}], "constant": false, "payable": false, "type": "function", "gas": 54928}, {"name": "tokenToTokenTransferOutput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "tokens_bought"}, {"type": "uint256", "name": "max_tokens_sold"}, {"type": "uint256", "name": "max_eth_sold"}, {"type": "uint256", "name": "deadline"}, {"type": "address", "name": "recipient"}, {"type": "address", "name": "token_addr"}], "constant": false, "payable": false, "type": "function", "gas": 55019}, {"name": "tokenToExchangeSwapInput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "tokens_sold"}, {"type": "uint256", "name": "min_tokens_bought"}, {"type": "uint256", "name": "min_eth_bought"}, {"type": "uint256", "name": "deadline"}, {"type": "address", "name": "exchange_addr"}], "constant": false, "payable": false, "type": "function", "gas": 49342}, {"name": "tokenToExchangeTransferInput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "tokens_sold"}, {"type": "uint256", "name": "min_tokens_bought"}, {"type": "uint256", "name": "min_eth_bought"}, {"type": "uint256", "name": "deadline"}, {"type": "address", "name": "recipient"}, {"type": "address", "name": "exchange_addr"}], "constant": false, "payable": false, "type": "function", "gas": 49532}, {"name": "tokenToExchangeSwapOutput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "tokens_bought"}, {"type": "uint256", "name": "max_tokens_sold"}, {"type": "uint256", "name": "max_eth_sold"}, {"type": "uint256", "name": "deadline"}, {"type": "address", "name": "exchange_addr"}], "constant": false, "payable": false, "type": "function", "gas": 53233}, {"name": "tokenToExchangeTransferOutput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "tokens_bought"}, {"type": "uint256", "name": "max_tokens_sold"}, {"type": "uint256", "name": "max_eth_sold"}, {"type": "uint256", "name": "deadline"}, {"type": "address", "name": "recipient"}, {"type": "address", "name": "exchange_addr"}], "constant": false, "payable": false, "type": "function", "gas": 53423}, {"name": "getEthToTokenInputPrice", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "eth_sold"}], "constant": true, "payable": false, "type": "function", "gas": 5542}, {"name": "getEthToTokenOutputPrice", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "tokens_bought"}], "constant": true, "payable": false, "type": "function", "gas": 6872}, {"name": "getTokenToEthInputPrice", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "tokens_sold"}], "constant": true, "payable": false, "type": "function", "gas": 5637}, {"name": "getTokenToEthOutputPrice", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "eth_bought"}], "constant": true, "payable": false, "type": "function", "gas": 6897}, {"name": "tokenAddress", "outputs": [{"type": "address", "name": "out"}], "inputs": [], "constant": true, "payable": false, "type": "function", "gas": 1413}, {"name": "factoryAddress", "outputs": [{"type": "address", "name": "out"}], "inputs": [], "constant": true, "payable": false, "type": "function", "gas": 1443}, {"name": "balanceOf", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "address", "name": "_owner"}], "constant": true, "payable": false, "type": "function", "gas": 1645}, {"name": "transfer", "outputs": [{"type": "bool", "name": "out"}], "inputs": [{"type": "address", "name": "_to"}, {"type": "uint256", "name": "_value"}], "constant": false, "payable": false, "type": "function", "gas": 75034}, {"name": "transferFrom", "outputs": [{"type": "bool", "name": "out"}], "inputs": [{"type": "address", "name": "_from"}, {"type": "address", "name": "_to"}, {"type": "uint256", "name": "_value"}], "constant": false, "payable": false, "type": "function", "gas": 110907}, {"name": "approve", "outputs": [{"type": "bool", "name": "out"}], "inputs": [{"type": "address", "name": "_spender"}, {"type": "uint256", "name": "_value"}], "constant": false, "payable": false, "type": "function", "gas": 38769}, {"name": "allowance", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "address", "name": "_owner"}, {"type": "address", "name": "_spender"}], "constant": true, "payable": false, "type": "function", "gas": 1925}, {"name": "name", "outputs": [{"type": "bytes32", "name": "out"}], "inputs": [], "constant": true, "payable": false, "type": "function", "gas": 1623}, {"name": "symbol", "outputs": [{"type": "bytes32", "name": "out"}], "inputs": [], "constant": true, "payable": false, "type": "function", "gas": 1653}, {"name": "decimals", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [], "constant": true, "payable": false, "type": "function", "gas": 1683}, {"name": "totalSupply", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [], "constant": true, "payable": false, "type": "function", "gas": 1713}]')
     9  
    10  my_provider = Web3.HTTPProvider('https://mainnet.infura.io/v3/c534d76d934f40498f6d6113a46c6ab3')
    11  w3 = Web3(my_provider)
    12  
    13  
    14  """1     IDEX  57849   55.5056% 0x2a0c0dbecc7e4d658f48e01e3fa353f44050c208
    15  2     DEx.top   11628   11.1570% 0x7600977eb9effa627d6bd0da2e5be35e11566341
    16  3     Ether Delta   6596    6.3288% 0x8d12a197cb00d4747a1fe03395095ce2a5cc6819
    17  4     Bancor Network    6569    6.3029% ???
    18  5     DDEX  5146    4.9375% 0x12459c951127e0c374ff9105dda097662a027093
    19  6     Token Store   3750    3.5981% 0x1ce7ae555139c5ef5a57cc8d814a867ee6ee33d8
    20  7     Star Bit  3448    3.3083% 0x12459c951127e0c374ff9105dda097662a027093
    21  8     Kyber Network 2550    2.4467% ?????
    22  9     Joyso 2205    2.1157% 0x04f062809b244e37e7fdc21d9409469c989c2342
    23  10    Oasis Dex 1865    1.7894% 0x12459c951127e0c374ff9105dda097662a027093
    24  11    Radar Relay   1303    1.2502% 0x12459c951127e0c374ff9105dda097662a027093
    25  12    Paradex   820 0.7868% 0x12459c951127e0c374ff9105dda097662a027093
    26  13    Airswap   243 0.2332% 0x8fd3121013a07c57f0d69646e86e7a4880b467b7
    27  14    TokenJar  106 0.1017% 0x12459c951127e0c374ff9105dda097662a027093
    28  15    The Ocean 91  0.0873% 0x12459c951127e0c374ff9105dda097662a027093
    29  16    Erc dEX   25  0.0240% 0x12459c951127e0c374ff9105dda097662a027093
    30  17    Enclaves  22  0.0211% 0xed06d46ffb309128c4458a270c99c824dc127f5d
    31  18    Shark Relay   6   0.0058% 0x12459c951127e0c374ff9105dda097662a027093
    32  19    Bamboo Relay  0   0.0000%
    33  20    IDT Exchange  0   0.0000%
    34  21    Tokenlon  0   0.0000%
    35  
    36  Source: dexwatch Thus Sep 20 3:48PM EST
    37  """
    38  
    39  
    40  dex_list = ["0x2a0c0dbecc7e4d658f48e01e3fa353f44050c208", # IDEX
    41              "0x7600977eb9effa627d6bd0da2e5be35e11566341", # DEx.top
    42              "0x8d12a197cb00d4747a1fe03395095ce2a5cc6819", # Etherdelta (done)
    43              "0x12459c951127e0c374ff9105dda097662a027093", # 0x v1 (done)
    44              "0x4f833a24e1f95d70f028921e27040ca56e09ab0b", # 0x v2 (done)
    45              "0x1ce7ae555139c5ef5a57cc8d814a867ee6ee33d8", # Token Store (done)
    46              "0x04f062809b244e37e7fdc21d9409469c989c2342", # Joyso
    47              "0x8fd3121013a07c57f0d69646e86e7a4880b467b7", # Airswap
    48              "0xed06d46ffb309128c4458a270c99c824dc127f5d", # Enclaves
    49             ]
    50  
    51  
    52  bancor_relayers = open('data/bancor_relayers').read().strip().splitlines()
    53  kyber_relayers = open('data/kyber_relayers').read().strip().splitlines()
    54  uniswap_relayers = open('data/uniswap_relayers').read().strip().splitlines()
    55  
    56  dex_list = dex_list + bancor_relayers + kyber_relayers + uniswap_relayers
    57  
    58  def parse_address(raw_hex):
    59      """ Extract address from lowest hex bits, ignoring junk. """
    60      return raw_hex[-40:]
    61  
    62  def parse_amount(raw_hex):
    63      return int(raw_hex, 16)
    64  
    65  @persist_to_file('uniswap.dat')
    66  def get_uniswap_token(address):
    67      token_addr = address
    68      erc20 = w3.eth.contract(address=Web3.toChecksumAddress('0x' + address), abi=UNISWAP_ABI)
    69      try:
    70          token_addr = erc20.functions.tokenAddress().call()
    71      except web3.exceptions.BadFunctionCallOutput: # todo handle chainsync errors?
    72          pass
    73      return token_addr.lower().replace("0x", "")
    74  
    75  @persist_to_file('decimals.dat')
    76  def get_decimals_for(address):
    77      if int(address, 16) == 0 or int(address, 16) == 1364068194842176056990105843868530818345537040110:
    78          return 18
    79      erc20 = w3.eth.contract(address=Web3.toChecksumAddress('0x' + address), abi=ERC20_ABI)
    80      try:
    81          decimals = int(erc20.functions.decimals().call())
    82      except web3.exceptions.BadFunctionCallOutput: # todo handle chainsync errors?
    83          return 0
    84      return decimals
    85  
    86  @persist_to_file('labels.dat')
    87  def get_node_label_for(address):
    88      if int(address, 16) == 0 or int(address, 16) == 1364068194842176056990105843868530818345537040110 or address.lower() == "c0829421c1d260bd3cb3e0f06cfe2d52db2ce315" or address.lower() == "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" or address.lower() == "2956356cd2a2bf3202f771f50d3d14a367b48070":
    89          return ("ETH", "ETH")
    90      erc20 = w3.eth.contract(address=Web3.toChecksumAddress('0x' + address), abi=ERC20_ABI)
    91      name = "Unknown"
    92      symbol = "Unknown"
    93      try:
    94          name = erc20.functions.name().call()
    95      except OverflowError:
    96          pass
    97      except web3.exceptions.BadFunctionCallOutput:
    98          pass
    99      try:
   100          symbol = erc20.functions.symbol().call()
   101      except OverflowError:
   102          pass
   103      except web3.exceptions.BadFunctionCallOutput:
   104          pass
   105      return (symbol, "%s (%s)\n0x%s" % (name, symbol, address))
   106  
   107  def parse_bancor(topics, data, address):
   108      tokenget_addr = parse_address(topics[1])
   109      tokenget,tokenget_label = get_node_label_for(tokenget_addr)
   110      amountget = (parse_amount(data[0:64]) / (10 ** get_decimals_for(tokenget_addr)))
   111      tokengive_addr = parse_address(topics[2])
   112      tokengive,tokengive_label = get_node_label_for(tokengive_addr)
   113      amountgive = (parse_amount(data[64:128]) / (10 ** get_decimals_for(tokengive_addr)))
   114      assert(len(data) == 192)
   115      return (tokenget_addr, tokenget_label, tokenget, amountget, tokengive_addr, tokengive_label, tokengive, amountgive)
   116  
   117  def parse_etherdelta_clone(topics, data, address, data_length):
   118      tokenget_addr = parse_address(data[0:64])
   119      tokenget,tokenget_label = get_node_label_for(tokenget_addr)
   120      amountget = (parse_amount(data[64:128]) / (10 ** get_decimals_for(tokenget_addr)))
   121      tokengive_addr = parse_address(data[128:192])
   122      tokengive,tokengive_label = get_node_label_for(tokengive_addr)
   123      amountgive = (parse_amount(data[192:256]) / (10 ** get_decimals_for(tokengive_addr)))
   124      assert(len(data) == data_length)
   125      return (tokenget_addr, tokenget_label, tokenget, amountget, tokengive_addr, tokengive_label, tokengive, amountgive)
   126  
   127  parse_etherdelta = lambda topics, data, address : parse_etherdelta_clone(topics, data, address, 384)
   128  parse_tokenstore = lambda topics, data, address : parse_etherdelta_clone(topics, data, address, 448)
   129  
   130  def parse_0x(topics, data, address):
   131      tokenget_addr = parse_address(data[128:192])
   132      tokenget,tokenget_label = get_node_label_for(tokenget_addr)
   133      amountget = (parse_amount(data[256:320]) / (10 ** get_decimals_for(tokenget_addr)))
   134      tokengive_addr = parse_address(data[64:128])
   135      tokengive,tokengive_label = get_node_label_for(tokengive_addr)
   136      amountgive = (parse_amount(data[192:256]) / (10 ** get_decimals_for(tokengive_addr)))
   137      assert(len(data) == 512)
   138      return (tokenget_addr, tokenget_label, tokenget, amountget, tokengive_addr, tokengive_label, tokengive, amountgive)
   139  
   140  def parse_0x_v2(topics, data, address):
   141      abi_data = decode_abi(['address', 'address', 'uint256', 'uint256', 'uint256', 'uint256', 'bytes', 'bytes'], bytes(Web3.toBytes(hexstr=data)))
   142      tokenget_addr = parse_address(Web3.toHex(abi_data[-1])[2:])
   143      tokenget,tokenget_label = get_node_label_for(tokenget_addr)
   144      amountget = (parse_amount(data[192:256]) / (10 ** get_decimals_for(tokenget_addr)))
   145      tokengive_addr = parse_address(Web3.toHex(abi_data[-2])[2:])
   146      tokengive,tokengive_label = get_node_label_for(tokengive_addr)
   147      amountgive = (parse_amount(data[128:192]) / (10 ** get_decimals_for(tokengive_addr)))
   148      assert(len(data) >= 896)
   149      return (tokenget_addr, tokenget_label, tokenget, amountget, tokengive_addr, tokengive_label, tokengive, amountgive)
   150  
   151  def parse_kyber(topics, data, address):
   152      abi_data = decode_abi(['address', 'address', 'uint256', 'uint256', 'address', 'uint256', 'address', 'address', 'bytes'], bytes(Web3.toBytes(hexstr=data)))
   153      tokenget_addr = parse_address(abi_data[0])
   154      tokenget,tokenget_label = get_node_label_for(tokenget_addr)
   155      amountget = (abi_data[2] / (10 ** get_decimals_for(tokenget_addr)))
   156      tokengive_addr = parse_address(abi_data[1])
   157      tokengive,tokengive_label = get_node_label_for(tokengive_addr)
   158      amountgive = (abi_data[3] / (10 ** get_decimals_for(tokengive_addr)))
   159      assert(len(data) >= 512)
   160      return (tokenget_addr, tokenget_label, tokenget, amountget, tokengive_addr, tokengive_label, tokengive, amountgive)
   161  
   162  def parse_uniswap_tokenpurchase(topics, data, address):
   163      abi_data = decode_abi(['uint256', 'address', 'uint256', 'uint256'], Web3.toBytes(hexstr="".join([x.replace("0x", "") for x in topics])))
   164      tokenget_addr = get_uniswap_token(address[2:])
   165      tokenget,tokenget_label = get_node_label_for(tokenget_addr)
   166      amountget = (abi_data[-1] / (10 ** get_decimals_for(tokenget_addr)))
   167      tokengive_addr = "0"*40 # (eth given by definition)
   168      tokengive,tokengive_label = get_node_label_for(tokengive_addr)
   169      amountgive = (abi_data[-2] / (10 ** get_decimals_for(tokengive_addr)))
   170      assert(len(topics) == 4)
   171      return (tokenget_addr, tokenget_label, tokenget, amountget, tokengive_addr, tokengive_label, tokengive, amountgive)
   172  
   173  def parse_uniswap_ethpurchase(topics, data, address):
   174      abi_data = decode_abi(['uint256', 'address', 'uint256', 'uint256'], Web3.toBytes(hexstr="".join([x.replace("0x", "") for x in topics])))
   175      tokenget_addr = "0"*40 # (eth gotten by definition)
   176      tokenget,tokenget_label = get_node_label_for(tokenget_addr)
   177      amountget = (abi_data[-1] / (10 ** get_decimals_for(tokenget_addr)))
   178      tokengive_addr = get_uniswap_token(address[2:])
   179      tokengive,tokengive_label = get_node_label_for(tokengive_addr)
   180      amountgive = (abi_data[-2] / (10 ** get_decimals_for(tokengive_addr)))
   181      assert(len(topics) == 4)
   182      return (tokenget_addr, tokenget_label, tokenget, amountget, tokengive_addr, tokengive_label, tokengive, amountgive)
   183  
   184  
   185  def get_trade_data_from_log_item(topics, data, address):
   186      exchange = None
   187      parser = None
   188      if address == '0x8d12a197cb00d4747a1fe03395095ce2a5cc6819': # etherdelta
   189          # TODO handle clones
   190          if topics[0] == '0x6effdda786735d5033bfad5f53e5131abcced9e52be6c507b62d639685fbed6d': # trade log event
   191              exchange = "Etherdelta"
   192              parser = parse_etherdelta
   193      elif address == '0x1ce7ae555139c5ef5a57cc8d814a867ee6ee33d8': # Tokenstore
   194          if topics[0] == '0x3314c351c2a2a45771640a1442b843167a4da29bd543612311c031bbfb4ffa98':
   195              exchange = "Tokenstore"
   196              parser = parse_tokenstore
   197      elif address == '0x12459c951127e0c374ff9105dda097662a027093': # 0x v1
   198          if topics[0] == '0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3':
   199              exchange = "0x v1"
   200              parser = parse_0x
   201      elif address == '0x4f833a24e1f95d70f028921e27040ca56e09ab0b': # 0x v2
   202          if topics[0] == '0x0bcc4c97732e47d9946f229edb95f5b6323f601300e4690de719993f3c371129':
   203              exchange = "0x v2"
   204              parser = parse_0x_v2
   205      elif address in bancor_relayers:
   206          if topics[0] == '0x276856b36cbc45526a0ba64f44611557a2a8b68662c5388e9fe6d72e86e1c8cb':
   207              exchange = "Bancor"
   208              parser = parse_bancor
   209      elif address in kyber_relayers:
   210          if topics[0] == '0xd30ca399cb43507ecec6a629a35cf45eb98cda550c27696dcb0d8c4a3873ce6c':
   211              exchange = "Kyber"
   212              parser = parse_kyber
   213      elif address in uniswap_relayers:
   214          if topics[0] == '0x7f4091b46c33e918a0f3aa42307641d17bb67029427a5369e54b353984238705':
   215              exchange = "Uniswap"
   216              parser = parse_uniswap_ethpurchase
   217          if topics[0] == '0xcd60aa75dea3072fbc07ae6d7d856b5dc5f4eee88854f5b4abf7b680ef8bc50f' and len(topics) == 4: # (ZRXcoin has same event; eg https://etherscan.io/tx/0x3d774851984b665b6db16d8bbf7a138520c76db923599fc8929b29edd384db7b#eventlog)
   218              exchange = "Uniswap"
   219              parser = parse_uniswap_tokenpurchase
   220      else:
   221          # parsing failed
   222          return None
   223  
   224      if not parser:
   225          # no logs to parse
   226          return []
   227  
   228      # 1 log generated; return it
   229      (tokenget_addr, tokenget_label, tokenget, amountget, tokengive_addr, tokengive_label, tokengive, amountgive) = parser(topics, data, address)
   230      return [(tokenget_addr, tokenget_label, tokenget, amountget, tokengive_addr, tokengive_label, tokengive, amountgive, exchange)]