All packages
usdt_transfers

usdt_transfers

StreamingFast
v0.2.0/0 downloads/Repository

Package ref

usdt-transfers@v0.2.0

Run package

CLI

Run map_transfers from the command line.

substreams run usdt-transfers@v0.2.0 map_transfers -e mainnet --start-block -1
Authenticate by running substreams auth or directly on thegraph.market (see docs).

README

usdt_transfers

One map module, map_transfers, emitting every USDT Transfer event on the streamed network as erc20.v1.Transfers (from, to, contract, amount, trx_hash and log_index).

amount is the raw uint256 as a decimal string — apply the token's decimals yourself (6 everywhere except BNB Chain, which uses 18).

Networks

The contract address is a module param and the deployment block is the module's initialBlock; both are set per network in substreams.yaml.

--networkTokenContractinitialBlock
mainnetUSDT0xdac17f958d2ee523a2206206994597c13d831ec74634748
bscUSDT (18 decimals)0x55d398326f99059ff775485246999027b31979554208826
baseUSDT0xfde4c96c8593536e31f229ea8f37b2ada2699bb212044917
polygonUSDT00xc2132d05d31c914a87c6611c10748aeb04b58e8f4196335
arbitrumUSD₮00xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9228105
optimismBridged USDT0x94b008aa00579c1307b0ef2c499ad98a8ce58e580
avalancheUSDt0x9702230a8ea53601f5cd2dc00fdbc13d4df4a8c74663628
unichainUSD₮00x9151434b16b9763660705744891fa906f660ecc511236573
tron-evm-mainnetUSDT (TRC-20)0xa614f803b6fd780986a42c78ec9c7f77e6ded13c8418292

Tron carries the most USDT transfers of any network. Use tron-evm-mainnet, which serves Tron in the Ethereum block format — the plain tron endpoint serves sf.tron.type.v1.Block, which this module cannot read, and streaming it produces no output and no error.

Tron addresses come out in EVM form. To get the base58 address Tron tooling expects, base58check-encode 0x41 followed by the 20 bytes: 0xa614f803… is TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t.

Optimism's USDT predates the chain's regenesis, so it exists at block 0. Arbitrum's predates Nitro; Firehose serves those blocks.

Avalanche's USDT.e (the bridged variant, 0xc7198437980c041c805a1edcba50c1ce5db95118) is a separate token — pass it with -p if you want it instead.

Benchmark

cargo bench

Times the mapping loop against eight real mainnet blocks committed under testdata/blocks (1909 transactions, 246 USDT transfers). No endpoint, no network. The bench asserts every variant emits identical transfers before timing them, so a change that alters the output fails rather than posting a fast number.

Three groups, on an M-series laptop:

groupv0.1.0v0.2.0
map_only — the mapping loop466 µs70 µs
decode_only — prost block decode, unchanged by either14.44 ms14.44 ms
decode_map_encode — the whole handler14.56 ms14.36 ms

The decode is 97% of the handler, so a mapping-loop win lands as roughly 1.5% end to end. Judge a change by decode_map_encode; use map_only to see whether the mapping change itself did anything.

map_only also carries two diagnostic variants: lazy_hash_only (142 µs) isolates the lazy transaction-hash encoding from the hand-rolled hex writer, and v0.2.0_minus_amount (48 µs) sizes the uint256 to decimal conversion, which is a third of what v0.2.0 still spends.

criterion stores a baseline, so a second cargo bench reports the delta against the previous run. To compare against a specific revision, run it on that revision first:

git stash && cargo bench && git stash pop && cargo bench

Fixtures are gzipped sf.ethereum.type.v2.Block messages. testdata/dumper is the package that captured them; its README covers pulling a different range or chain.

Run

cargo build --target wasm32-unknown-unknown --release
substreams run substreams.yaml map_transfers --network tron-evm-mainnet -s 60000000 -t +10 -o jsonl

Any other network, or a different ERC-20 entirely, works without a rebuild:

substreams run substreams.yaml map_transfers --network mainnet \
  -p map_transfers=0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48 -s 25800000 -t +10 -o jsonl

Modules

Execution graph

1 modules