Pending Tokens – How to Know Token Transfer Status from Memepool Using Web3.js and Web3.py

txpoolweb3.pyweb3js

i'm getting list of data from web3.eth.filter('pending'), here i'm listening uniswap ETH router.

If i print one result of the list :

AttributeDict({'blockHash': None, 'blockNumber': None, 'from':
'0xxxxx0f7548e88708f7F212AfA58d27293fxxxx', 'gas': 180961,
'gasPrice': 165687947255, 'maxFeePerGas': 165687947255,
'maxPriorityFeePerGas': 94000000, 'hash':
HexBytes('0xa79d8760dd7e6dba5a0c1567bba3f3d5cd976aa1bb0478c878d0ac5f634d6cbc'),
'input':
'0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000006457ef7700000000000000000000000000000000000000000000000000000000000000020b080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000066fe4797754b1ebfb5b5f3c600000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000471a202f69d6e975da55e363dab1bdb2e86e0c0f',
'nonce': 551, 'to': '0xEf1c6E67703c7BD7107eed8303Fbe6EC2554BF6B',
'transactionIndex': None, 'value': 1000000000000000000, 'type': 2,
'accessList': [], 'chainId': 1, 'v': 0, 'r':
HexBytes('0xf00649dc54b04986577d93a1f89ac0f80002c366e42179f4e3872bb74e5b2486'),
's':
HexBytes('0x69f5375cb00778357013f34130aa1681feb7e8c259222c5675339fc2b32d543e')})

As it's pending i understand there is no blockHash/blockNumber. But my question here is how can i know wich token this users is transfering ? For example how can i know this users is swapping 1ETH for 1000$ ?

My guess would be to request last transactions of "from" and check what this address did last time. But i would be surprise that we don't already got this info somewhere here ?

Best Answer

You need to decode input params. You can do this if you know ABI of the smart-contract address. In your case it is not hard.

  1. Smart contract address: 0xEf1c6E67703c7BD7107eed8303Fbe6EC2554BF6B
  2. ABI (go to etherscan) to https://etherscan.io/address/0xEf1c6E67703c7BD7107eed8303Fbe6EC2554BF6B#code. Scroll down to 'Contract ABI' and copy it
  3. a little portion of code
In [1]: from web3 import Web3

In [2]: w3 = Web3(Web3.HTTPProvider('https://mainnet.infura.io/v3/<i use infura token>'))

In [3]: router_contract = w3.eth.contract(address='0xEf1c6E67703c7BD7107eed8303Fbe6EC2554BF6B', abi='<paste here abi>')

In [4]: input_params = '0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000
   ...: 0000000000000000000000000000006457ef7700000000000000000000000000000000000000000000000000000000000000020b0800000000000000000000000000000000000000000000000000000000000000000000000000
   ...: 00000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000
   ...: 0000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000de0b6
   ...: b3a764000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000
   ...: 0000000de0b6b3a7640000000000000000000000000000000000000000000066fe4797754b1ebfb5b5f3c600000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000
   ...: 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000
   ...: 000000471a202f69d6e975da55e363dab1bdb2e86e0c0f'
In [5]: router_contract.decode_function_input(input_params)
Out[5]: 
(<Function execute(bytes,bytes[],uint256)>,
 {'commands': b'\x0b\x08',
  'inputs': [b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\r\xe0\xb6\xb3\xa7d\x00\x00',
   b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\r\xe0\xb6\xb3\xa7d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00f\xfeG\x97uK\x1e\xbf\xb5\xb5\xf3\xc6\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0*\xaa9\xb2#\xfe\x8d\n\x0e\\O'\xea\xd9\x08<ul\xc2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00G\x1a /i\xd6\xe9u\xdaU\xe3c\xda\xb1\xbd\xb2\xe8n\x0c\x0f"],
  'deadline': 1683484535})

In [6]: next_params = router_contract.decode_function_input(input_params)[1]['inputs']

We are now understand that it is params to call an execute method from universal router (https://blog.uniswap.org/permit2-and-universal-router). Let's read more docs from github repo to understand what is command https://github.com/Uniswap/universal-router#how-the-command-byte-is-structured.

'commands': b'\x0b\x08' means V2_SWAP_EXACT_IN. Input params for each command covered in docs https://docs.uniswap.org/contracts/universal-router/technical-reference#command. V2_SWAP_EXACT_IN accepts address, uint256, uint256, address[], bool as input parameters.

Let's take a look on next_params content in hex.

In [7]: Web3.to_hex(next_params[0])
Out[7]: '0x00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000de0b6b3a7640000'

In [8]: Web3.to_hex(next_params[1])
Out[8]: '0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000066fe4797754b1ebfb5b5f3c600000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000471a202f69d6e975da55e363dab1bdb2e86e0c0f'

The second one looks prominent. Let's use eth_abi to decode.

In [9]: from eth_abi import decode

In [10]: decode(['address', 'uint256', 'uint256', 'address[]', 'bool'], next_params[1])
Out[10]: 
('0x0000000000000000000000000000000000000001',
 1000000000000000000,
 31874876241889834629682557894,
 ('0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
  '0x471a202f69d6e975da55e363dab1bdb2e86e0c0f'),
 False)

Voilà!

This transaction is an swap ecxact input value of Uniswap V2 liq pool.

Tokens:

Amounts:

  • Input amount: 1000000000000000000/10 ** 18 = 1 (WETH)

  • Minimum output: 31874876241889834629682557894/10 ** 18 = 31 874 876 241.889835 (Geke tokens)

About second input. Honestly, I don't find docs but running this gives at least understanding about input params structure.

In [48]: decode(['address', 'uint256'], next_params[0])
Out[48]: ('0x0000000000000000000000000000000000000002', 1000000000000000000)

Based on all list of commands from a full list: https://docs.uniswap.org/contracts/universal-router/technical-reference#wrap_eth

I bet that first command is the WRAP_ETH.

Using WRAP_ETH + V2_SWAP_EXACT_IN looks very logically when you sell ETH to Geke.