Prenons l'exemple d'un transfert sur le réseau Avalanche Mainnet via le routeur Trader Joe.
Comment savoir le nombre exact de tokens qui ont été transférés (envoyés et reçus) ?

Voici une solution, en prenant pour l'exemple cette transaction (⚠ ceci n'est pas mon portefeuille).

Imports et constantes :

from web3 import HTTPProvider, Web3
from web3.logs import DISCARD

# Note: I picked a random transaction, this is not my wallet!
MY_ADDRESS = "0xDA9f28a1b0CE18b011A404f3372d98C3E3143569"
TX_HASH = "0x5270f284b2a5432e264be7a173fd5f187983a86d80e7f65cf2e9125b7fde1e51"

AVALANCHE_MAINNET_RPC_URL = "https://api.avax.network/ext/bc/C/rpc"
TRADER_JOE_ROUTER_ADDRESS = "0x60aE616a2155Ee3d9A68541Ba4544862310933d4"

# We will check both wallet, and router, addresses to catch coins transfers too (USDC.e -> AVAX for example)
ADDRESSES = {MY_ADDRESS, TRADER_JOE_ROUTER_ADDRESS}

MINIMAL_ABI = [
    # Transfer event
    {
        "name": "Transfer",
        "type": "event",
        "anonymous": False,
        "inputs": [
            {
                "indexed": True,
                "name": "from",
                "type": "address",
            },
            {
                "indexed": True,
                "name": "to",
                "type": "address",
            },
            {
                "indexed": False,
                "name": "value",
                "type": "uint256",
            },
        ],
    },
]

Récupération et décodage des logs :

# Initiate the machine
w3 = Web3(HTTPProvider(AVALANCHE_MAINNET_RPC_URL))

# Instantiate the router
router = w3.eth.contract(address=TRADER_JOE_ROUTER_ADDRESS, abi=MINIMAL_ABI)

# Get the transaction receipt
receipt = w3.eth.get_transaction_receipt(TX_HASH)

# Decode logs, without displaying warnings about discarded logs
logs = router.events.Transfer().processReceipt(receipt, errors=DISCARD)

Enfin, quelques idées de traitement des logs :

# 1. Access details as simply as:
print("Details:")
for idx, log in enumerate(logs, start=1):
    args = log.args
    print(f"  {idx}. From {args['from']} to {args['to']} for {args['value']} tokens")

# 2. Or compute amounts
tokens_sent = sum(log.args["value"] for log in logs if log.args["from"] in ADDRESSES)
tokens_received = sum(log.args["value"] for log in logs if log.args["to"] in ADDRESSES)

# You will need to adapt "10**N" with the real token decimals
print("\nAmounts:")
print(f"  - Tokens sent    : {tokens_sent / 10**18:.5f} $WETH.e")
print(f"  - Tokens received: {tokens_received / 10**9:.5f} $TIME")

Pour la démonstration, voici ce qui sera affiché :

Details:
  1. From 0xDA9f28a1b0CE18b011A404f3372d98C3E3143569 to 0xFE15c2695F1F920da45C30AAE47d11dE51007AF9 for 984378414134985971 tokens
  2. From 0xFE15c2695F1F920da45C30AAE47d11dE51007AF9 to 0xf64e1c5B6E17031f5504481Ac8145F4c3eab4917 for 59247645014580674712 tokens
  3. From 0xf64e1c5B6E17031f5504481Ac8145F4c3eab4917 to 0xDA9f28a1b0CE18b011A404f3372d98C3E3143569 for 480492703 tokens

Amounts:
  - Tokens sent    : 0.98438 $WETH.e
  - Tokens received: 0.48049 $TIME

Et voilà !

PS : J'avais partagé ces infos sur cette issue en anglais.


Historique

  • 2022-03-07 : simplification du code : utilisation de web3.logs.DISCARD plutôt que de filtrer les avertissements.
  • 2022-03-01 : aération du code monolithique.
  • 2022-02-25 : prise en compte des transferts utilisant le coin de la blockchain (AVAX sur Avalanche, FTM sur Fantom, etc.).