Track ERC-20 Transfers

In a smart contract, events serve as notifications of specific occurrences, like transactions, or changes in ownership. Each event is uniquely identified by its event signature, which is calculated using the keccak 256 hash of the event name and its input argument types.

For example, for an ERC-20 transfer event, the event signature is determined by taking the hash of Transfer(address,address,uint256). To compute this hash yourself, you can use an online keccak-256 converter and you’ll see that the hexadecimal representation of Transfer(address,address,uint256) is 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. For a full list of signatures check https://www.4byte.directory/event-signatures/.

Take into consideration that the Transfer event for ERC-20 and ERC-721 tokens is similar. Here is the Transfer event prototype on each standard:

  • ERC20: event Transfer(address indexed _from, address indexed _to, uint256 _value)
  • ERC721: event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);

These two signatures are indeed the same when you hash them to identifyTransfer events. The example below illustrates how to set up filtering to receive transfer events.

In this example, we will monitor all the USDT transfers on the C-chain. If we go to any block explorer, select a USDT transaction, and look at Topic 0 from the transfer event, we can get the signature.

With the event signature, we can create the webhook as follows:

curl --location '<https://glacier-api.avax.network/v1/webhooks'>  
--header 'x-glacier-api-key: <YOUR_API_KEY'  
--header 'Content-Type: application/json'  
--data '{  
   "url": "https://webhook.site/30eb3703-04f0-4a01-8903-fe3f5afb78bc",  
   "chainId": "43114",  
   "eventType": "address_activity",  
   "metadata": {  
       "addresses": ["0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7"],  
       "eventSignatures": [  
           "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"  
        ]  
   },  
   "name": "USDT",  
   "description": "USDT"  
}'

Once the the webhook is created we start receiving the events:

{
  "webhookId": "401da7d9-d6d7-46c8-b431-72ff1e1543f4",
  "eventType": "address_activity",
  "messageId": "bc9732db-2430-4296-afc3-c51267beb14a",
  "event": {
    "transaction": {
      "blockHash": "0x2a47bebed93db4a21cc8339980f004cc67f17d0dff4a368001e450e7be2edaa0",
      "blockNumber": "45396106",
      "from": "0x737F6b0b8A04e8462d0fC7076451298F0dA9a972",
      "gas": "80000",
      "gasPrice": "52000000000",
      "maxFeePerGas": "52000000000",
      "maxPriorityFeePerGas": "2000000000",
      "txHash": "0xfd91150d236ec5c3b1ee325781affad5b0b4d7eb0187c84c220ab115eaa563e8",
      "txStatus": "1",
      "input": "0xa9059cbb00000000000000000000000040e832c3df9562dfae5a86a4849f27f687a9b46b00000000000000000000000000000000000000000000000000000000c68b2a69",
      "nonce": "2",
      "to": "0x9702230a8ea53601f5cd2dc00fdbc13d4df4a8c7",
      "transactionIndex": 2,
      "value": "0",
      "type": 2,
      "chainId": "43114",
      "receiptCumulativeGasUsed": "668508",
      "receiptGasUsed": "44038",
      "receiptEffectiveGasPrice": "27000000000",
      "receiptRoot": "0xe5b018c29a77c8a92c4ea2f2d7e58820283041a52e14a0620d90d13b881e1ee3",
      "erc20Transfers": [
        {
          "transactionHash": "0xfd91150d236ec5c3b1ee325781affad5b0b4d7eb0187c84c220ab115eaa563e8",
          "type": "ERC20",
          "from": "0x737F6b0b8A04e8462d0fC7076451298F0dA9a972",
          "to": "0x40E832C3Df9562DfaE5A86A4849F27F687A9B46B",
          "value": "3331009129",
          "blockTimestamp": 1715621840,
          "logIndex": 0,
          "erc20Token": {
            "address": "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7",
            "name": "TetherToken",
            "symbol": "USDt",
            "decimals": 6,
            "valueWithDecimals": "3331.009129"
          }
        }
      ],
      "erc721Transfers": [],
      "erc1155Transfers": [],
      "internalTransactions": [
        {
          "from": "0x737F6b0b8A04e8462d0fC7076451298F0dA9a972",
          "to": "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7",
          "internalTxType": "CALL",
          "value": "0",
          "gasUsed": "44038",
          "gasLimit": "80000",
          "transactionHash": "0xfd91150d236ec5c3b1ee325781affad5b0b4d7eb0187c84c220ab115eaa563e8"
        },
        {
          "from": "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7",
          "to": "0xBA2a995Bd4ab9e605454cCEf88169352cd5F75A6",
          "internalTxType": "DELEGATECALL",
          "value": "0",
          "gasUsed": "15096",
          "gasLimit": "50301",
          "transactionHash": "0xfd91150d236ec5c3b1ee325781affad5b0b4d7eb0187c84c220ab115eaa563e8"
        }
      ],
      "blockTimestamp": 1715621840
    },
    "logs": [
      {
        "address": "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7",
        "topic0": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
        "topic1": "0x000000000000000000000000737f6b0b8a04e8462d0fc7076451298f0da9a972",
        "topic2": "0x00000000000000000000000040e832c3df9562dfae5a86a4849f27f687a9b46b",
        "topic3": null,
        "data": "0x00000000000000000000000000000000000000000000000000000000c68b2a69",
        "transactionIndex": 2,
        "logIndex": 19,
        "removed": false
      }
    ]
  }
}