Data access · 10 min read
Stablecoin data: tracking cross-chain flows and peg health
A stablecoin is rarely one thing on one chain. The same brand is issued natively across many networks, and the interesting questions follow the money between them. This guide is about the data underneath that: transfers as the substrate of flow tracking, the per-chain address problem, lining up a window across chains by timestamp, and reading peg health from where stablecoins actually trade. Every query below is run against SQD's Portal, and every number is from a fixed block range so it stays reproducible.
1. Flow data, and where it stops
Three different questions get filed under stablecoin data, and keeping them apart saves a lot of confusion. Flows are movements of value between addresses: who paid whom, how funds reach an exchange, where a treasury sends money. Volume is the aggregate size of those movements over time. Supply is how much of the token exists, which has to be reconstructed from issuance.
This guide is about flows and the related question of peg health. Transfer volume, and reconstructing circulating supply from mint and burn events across chains, are their own topic with their own reconciliation problems, and they are covered in the onchain analytics guide. One boundary is worth stating plainly up front: the data here is transfers and trades, the events a chain actually emits. It is not a balance lookup. Nothing below reconstructs an account balance at a point in time, because that is a different exercise built on different data.
2. Transfers are the substrate
Every movement of an ERC-20 stablecoin emits a Transfer event, so the flow of funds is exactly the log of that event filtered to the token's contract. A query against SQD's Portal for USDC transfers on Ethereum is one filter on the token address and the Transfer topic:
POST https://portal.sqd.dev/datasets/ethereum-mainnet/stream
Accept: application/x-ndjson
{
"type": "evm",
"fromBlock": 21000000,
"toBlock": 21000100,
"logs": [{
"address": ["0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"],
"topic0": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
}],
"fields": {
"block": { "number": true, "timestamp": true },
"log": { "address": true, "topics": true, "data": true, "transactionHash": true }
}
}
The Transfer signature puts the sender in topic1, the recipient in topic2, and the amount in data. One real result, at block 21,000,100, is an issuance:
from: 0x0000000000000000000000000000000000000000
to: 0xa6227fffd0f7e46ee9f513ca65acfd52667f4974
value: 750.969553 USDC The sender is the zero address, which is what a mint looks like: new tokens entering circulation have to come from somewhere, and by convention that somewhere is address zero. A burn is the mirror image, a transfer to the zero address. Everything else in the stream is an ordinary movement between two real addresses. That single event type, read for one token, is the entire flow of funds for that stablecoin on that chain. Turning mints and burns into a supply series is the analytics step that lives in the onchain analytics guide; here the point is simply that issuance, redemption, and every payment are all the same queryable event.
3. Every chain, one query
A stablecoin does not live on one chain, and that is where flow tracking gets real. The same query shape runs on every network SQD indexes; the only change is the dataset name in the endpoint and the token's address on that chain. The catch is that the address is genuinely different each time. USDC is a separate native deployment per network:
Ethereum 0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
Base 0x833589fcd6edb6e08f4c7c32d4f71b54bda02913
Arbitrum 0xaf88d065e77c8cc2239327c5edb3a432268e5831
Run the identical Transfer query against each dataset with the right address and the transfers come back from all three: one query model, one event signature, every chain a stablecoin is deployed on, with the per-chain address map as the only thing you carry between them. A flow that crosses chains, say a treasury moving USDC from Ethereum to Base, is two transfers in two datasets that you join on the addresses and amounts. One thing has to be right before the per-chain numbers mean anything, and that is the window they cover.
4. Aligning time across chains
Block numbers do not line up across chains. Block 22,606,143 on Ethereum and block 22,606,143 on Base are different moments, because the chains produce blocks at different rates and started at different times. So to compare the same window everywhere at once, you cannot reuse a block number. You translate the instant you care about into each chain's block first.
Portal resolves a timestamp to a block with a single GET against the dataset, passing the instant as a Unix timestamp in seconds. It returns the first block at or after that time:
GET https://portal.sqd.dev/datasets/base-mainnet/timestamps/1748736000/block
{ "block_number": 30973327 } Change the dataset in the path and the same lookup works on every chain, returning an EVM block number or a Solana slot. Take a fixed ten-minute window, 2025-06-01 00:00:00 to 00:10:00 UTC (Unix 1748736000 to 1748736600), and resolve both ends on each chain:
00:00:00 block 00:10:00 block blocks in window
Ethereum 22,606,143 22,606,192 49
Base 30,973,327 30,973,627 300
Arbitrum 342,607,969 342,610,336 2,367
The same ten minutes is 49 blocks on Ethereum and 2,367 on Arbitrum, which is exactly why a shared block number is meaningless across chains. One boundary detail matters: the lookup returns the first block at or after the instant, and Portal's toBlock is inclusive, so each window runs fromBlock the 00:00:00 block to toBlock the 00:10:00 block minus one. That last block already belongs to the next window. Over those 49, 300, and 2,367 blocks the USDC transfer counts compare directly:
USDC transfers, 2025-06-01 00:00:00 to 00:10:00 UTC
Ethereum 1,304
Base 10,483
Arbitrum 3,649
In that window Base alone moved USDC close to eight times as often as Ethereum, and Arbitrum more than twice as often, the kind of comparison that holds only because the windows are time-aligned rather than block-aligned. The lookup works the same way across EVM, Solana, Bitcoin, and Substrate datasets. The alternative, with only an RPC endpoint, is a binary search over block timestamps, roughly twenty eth_getBlockByNumber calls per bound, per chain. Time alignment is the unglamorous part of cross-chain flow data, and it is most of the work.
5. Peg health from DEX trades
A stablecoin's peg is a market fact, not a contract field. The token does not report that it is worth a dollar; the market decides that by what people pay for it, and onchain that happens in DEX pools. So peg health is read from swap events: the executed price of the stablecoin against a reference asset, usually another stablecoin or the chain's wrapped native token, on its deepest pools. Compare that price to 1.00 and a sustained gap is a depeg signal.
The mechanics are the same as any DEX price work: filter the pool's Swap events, derive the executed price from the amounts, and bucket it over time. This is a reading of trade data, not an oracle feed and not a substitute for one; it tells you where the stablecoin changed hands, which is the right signal for monitoring rather than for settlement. The same transfer and swap streams that drive flow tracking also drive this, so peg monitoring is another view over data you are already pulling, not a separate integration.
6. Stablecoin data with SQD
SQD provides the transfer and trade data this all runs on, decoded and typed, from the Portal. The same query shape covers every network a stablecoin reaches, including newer stablecoin-focused chains such as Plasma, which SQD indexes the same way it indexes Ethereum, so adding a chain to a flow view is a one-line dataset change rather than a new integration.
For a product you would stream this rather than query by hand. The Pipes SDK pulls the same transfer and swap events into your own store, where you keep per-chain flow tables and a peg series that update as blocks arrive, with the timestamp-to-block lookup handling cross-chain windows. For transfer-volume metrics and cross-chain supply reconstruction, see the onchain analytics guide; for how this feeds a monitoring product, see the stablecoins solution page.
Frequently asked questions
What data do you need to track stablecoin flows?
Why does the same stablecoin have a different contract address on each chain?
How do you compare stablecoin activity at the same moment across chains?
How do you measure a stablecoin's peg from onchain data?
How do you measure stablecoin supply onchain?
Related guides
Building stablecoin or payments tooling?
See how cross-chain flow tracking and peg monitoring run on SQD on the stablecoins solution page.