0

I am trying to identify token swaps from Uniswap (from WETH, USDT or USDC) through transaction hashes.

Right now I use it like:

infura_url = 'https://mainnet.infura.io/v3/xxx'
web3 = Web3(Web3.HTTPProvider(infura_url))
tx_hash = '0x0...'
receipt = web3.eth.get_transaction_receipt(tx_hash)

TRANSFER_EVENT_SIGNATURE_HASH = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"

 for log in receipt.logs:
     if log['topics'][0].hex() == TRANSFER_EVENT_SIGNATURE_HASH:
     (....)

And then at the end I filter out all transactions with WETH, USDC and USDT which then leaves me with only the actual swap. But this is using a lot of calls and resources. Is there any better way to do this? On Etherscan these swaps are identified as "Execute".

1 Answer 1

0

You could filter for the Swap events on the the specific pool contracts that you want to monitor, similar to what you are doing now but more targeted:

POOL_ADDRESS = '0x0d4a11d5EEaaC28EC3F61d100daF4d40471f1852'
POOL_ABI = '...'
pool = w3.eth.contract(address=POOL_ADDRESS, abi=POOL_ABI)

SWAP_TOPIC = w3.solidity_keccak(['string'],['Swap(address,uint256,uint256,uint256,uint256,address)']).hex()
logs = w3.eth.get_logs({'fromBlock': 19685780, 'toBlock': 'latest', 'address': pool.address})

for log in logs:
  for topic in log['topics']:
    if SWAP_TOPIC == topic.hex():
      swap = pool.events.Swap().process_log(log)['args']
      # process swap volume
      amount0In, amount1In, amount0Out, amount1Out = swap['amount0In'], swap['amount1In'], swap['amount0Out'], swap['amount1Out']
      print(f'[+] OUTPUT: amount0In -> {amount0In}, amount1In -> {amount1In}, amount0Out -> {amount0Out}, amount1Out -> {amount1Out}')

example output:

[+] OUTPUT: amount0In -> 139000000000000000, amount1In -> 0, amount0Out -> 0, amount1Out -> 425230323
[+] OUTPUT: amount0In -> 0, amount1In -> 50000000, amount0Out -> 16246253920760211, amount1Out -> 0
[+] OUTPUT: amount0In -> 0, amount1In -> 1354788178, amount0Out -> 440195827163941991, amount1Out -> 0
[+] OUTPUT: amount0In -> 148950000000000000, amount1In -> 0, amount0Out -> 0, amount1Out -> 455682018
[+] OUTPUT: amount0In -> 0, amount1In -> 553992857, amount0Out -> 180000000083162911, amount1Out -> 0
[+] OUTPUT: amount0In -> 0, amount1In -> 10650000, amount0Out -> 3460305454802189, amount1Out -> 0
[+] OUTPUT: amount0In -> 29227747539203983, amount1In -> 0, amount0Out -> 0, amount1Out -> 89417051
...

You could look at searching the bloom filters for topics. If 3 bits in the bloom are set then the topic might be in the block, if any of the 3 bits are not set then it definitely isn't. Narrow down from there.

from net import con; w3 = con('MAINNET') # just for keccak
from hexbytes import HexBytes
keccak256 = w3.solidity_keccak

def get_num(hex_str: str, index: int, bytes_count: int = 1) -> int:
  hex_str = HexBytes(hex_str).hex()[2:]
  ost = index * 2
  return int(hex_str[ost:ost + 2 * bytes_count], 16)

def fetch_mask(topic: str) -> list:
  buf = keccak256(['bytes'], [topic])
  mask_1 = 1 << (get_num(buf, 1) & 0x7)
  mask_2 = 1 << (get_num(buf, 3) & 0x7)
  mask_3 = 1 << (get_num(buf, 5) & 0x7)

  bloom_len = 256
  pos_1 = bloom_len - ((get_num(buf, 0, 2) & 0x7FF) >> 3) - 1
  pos_2 = bloom_len - ((get_num(buf, 2, 2) & 0x7FF) >> 3) - 1
  pos_3 = bloom_len - ((get_num(buf, 4, 2) & 0x7FF) >> 3) - 1

  return [
    { 'index': pos_1, 'mask': mask_1 },
    { 'index': pos_2, 'mask': mask_2 },
    { 'index': pos_3, 'mask': mask_3 },
  ]

def maybe_topic(bloom: str, masks: list) -> bool:

  def aux(x) -> bool:
    b = get_num(bloom, x['index'])
    return (b | x['mask'] == b)

  bits_set = list(filter(lambda b : b == True, map(lambda x: aux(x), masks) ))
  return len(bits_set) == 3

example output:

>>>  topic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
>>> logsBloom = '0x0100000000000004000000000000100000000000000000000000000000200000000000000000000000000000000001000000000000000000080008000000000040000000002000000000040800000000000000000000200000000000004800000040000002000000000000000000081000000000000040000000001080020000200001000000000000000000040000004000000000000000000000000010000000008000000000000800008000000800000000008000000200000800000000000000200a000000200000000000100002004000000000000000000000000020000000000080000020000000800000000000004000000000000000080000004000'
>>> print(maybe_topic(logsBloom, fetch_mask(topic)))
True
1
  • 1
    Thank you very much. I will try that :)
    – ftmbl
    Apr 21 at 8:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.