Output
erc20.v1.TransfersInputs
Package ref
usdt-transfers@v0.2.0Run package
CLI
Run map_transfers from the command line.
substreams run usdt-transfers@v0.2.0 map_transfers -e mainnet --start-block -1substreams auth or directly on thegraph.market (see docs).README
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).
The contract address is a module param and the deployment block is the module's
initialBlock; both are set per network in substreams.yaml.
--network | Token | Contract | initialBlock |
|---|---|---|---|
mainnet | USDT | 0xdac17f958d2ee523a2206206994597c13d831ec7 | 4634748 |
bsc | USDT (18 decimals) | 0x55d398326f99059ff775485246999027b3197955 | 4208826 |
base | USDT | 0xfde4c96c8593536e31f229ea8f37b2ada2699bb2 | 12044917 |
polygon | USDT0 | 0xc2132d05d31c914a87c6611c10748aeb04b58e8f | 4196335 |
arbitrum | USD₮0 | 0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9 | 228105 |
optimism | Bridged USDT | 0x94b008aa00579c1307b0ef2c499ad98a8ce58e58 | 0 |
avalanche | USDt | 0x9702230a8ea53601f5cd2dc00fdbc13d4df4a8c7 | 4663628 |
unichain | USD₮0 | 0x9151434b16b9763660705744891fa906f660ecc5 | 11236573 |
tron-evm-mainnet | USDT (TRC-20) | 0xa614f803b6fd780986a42c78ec9c7f77e6ded13c | 8418292 |
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.
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:
| group | v0.1.0 | v0.2.0 |
|---|---|---|
map_only — the mapping loop | 466 µs | 70 µs |
decode_only — prost block decode, unchanged by either | 14.44 ms | 14.44 ms |
decode_map_encode — the whole handler | 14.56 ms | 14.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.
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
Output
erc20.v1.TransfersInputs