Stream the activity of one Ethereum contract, USDC, as the input to a transfer index or activity
feed. This guide passes the contract address to a package module as a parameter, then shows how to
pick Transfer events out of the output.
The pinned module returns event addresses, topics, and transaction hashes, not decoded amounts. Your consumer filters and stores the transfer records, then commits them with the block's cursor.
Prerequisites
- A Substreams API key.
- A consumer that can decode the module's
eth.event.v1.Eventsoutput type. - A durable store for transfer rows and cursors.
Stream the contract's events
USDC on Ethereum mainnet:
0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48--params passes the address to map_contract_events, which then returns only that contract's
events. The CLI tab is a complete command over 100 blocks. The Go, Rust, and TypeScript tabs show
the values a standard Substreams client library needs, including the module parameter.
export SUBSTREAMS_API_KEY="<GRAPHOPS_API_KEY>"
substreams run \
-e "https://1.evm.substreams.graphops.xyz" \
"https://github.com/streamingfast/substreams-explorers/releases/download/v1.0.1/ethereum-explorer-v0.1.2.spkg" \
"map_contract_events" \
--start-block 18000000 \
--stop-block +100 \
--params "map_contract_events=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" \
-o jsonconst endpoint = "https://1.evm.substreams.graphops.xyz"
const packageRef = "https://github.com/streamingfast/substreams-explorers/releases/download/v1.0.1/ethereum-explorer-v0.1.2.spkg"
const moduleName = "map_contract_events"
const usdc = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
func main() {
headers := map[string]string{
"x-api-key": "<GRAPHOPS_API_KEY>",
}
params := map[string]string{
moduleName: usdc,
}
// Use a standard Substreams Go client, decode the event output,
// filter Transfer events, commit rows, then persist the cursor.
_, _ = headers, params
}const ENDPOINT: &str = "https://1.evm.substreams.graphops.xyz";
const PACKAGE_REF: &str = "https://github.com/streamingfast/substreams-explorers/releases/download/v1.0.1/ethereum-explorer-v0.1.2.spkg";
const MODULE_NAME: &str = "map_contract_events";
const USDC: &str = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
const X_API_KEY: &str = "<GRAPHOPS_API_KEY>";
// Use a standard Substreams Rust client, pass USDC as the module parameter,
// decode events, filter Transfer events, and persist cursors after commits.const streamConfig = {
endpoint: "https://1.evm.substreams.graphops.xyz",
packageRef: "https://github.com/streamingfast/substreams-explorers/releases/download/v1.0.1/ethereum-explorer-v0.1.2.spkg",
moduleName: "map_contract_events",
startBlock: 18_000_000,
stopBlock: "+100",
params: {
map_contract_events: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
},
headers: {
"x-api-key": "<GRAPHOPS_API_KEY>",
},
};
// Use a standard Substreams TypeScript client, decode events,
// filter Transfer events, and persist cursors after durable commits.
console.log(streamConfig);Read the output
CLI JSON places the events inside @data and identifies the output type with @type. Addresses,
topics, and transaction hashes are lowercase hexadecimal strings without a 0x prefix. The values
below are illustrative.
{
"@module": "map_contract_events",
"@block": 18000000,
"@type": "eth.event.v1.Events",
"@data": {
"events": [
{
"address": "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"topics": [
"ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0000000000000000000000001111111111111111111111111111111111111111",
"0000000000000000000000002222222222222222222222222222222222222222"
],
"txHash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
}
]
}
}topics[0]identifies the event.ddf252ad…is the ERC-20Transfersignature; keep events that match it.topics[1]andtopics[2]are the sender and recipient, left-padded to 32 bytes. The last 40 hex characters are the address.txHashlinks the event to its transaction.
This module does not include the log data field, so transfer amounts are not available. Use a package that emits full logs or decoded transfers when your consumer needs amounts.
Before production
- Commit a block's transfer rows and its cursor together.
- Handle undo signals if you follow blocks near the chain head; see Cursors and reorgs.
- Backfill history with finite ranges, then continue live; see Backfill a range.
- Watch bytes out. Event-heavy modules can produce large responses.