USDC transfer feed

The USDC ledger Arc doesn't hand you.

On Arc, USDC moves through two emitters at two decimal scales, and an ERC-20 transfer is reported twice. The transfer feed reconciles all of it into one record per movement, with the Memo, the blocklist state and the CCTP leg already attached.

tx 0x9f2c...a41e · block 4,210,887

system emitter18 dec

0xffffFFFf...FFfE

value=1250000000000000000000

USDC contract6 dec

0x36000000...0000

value=1250000000

one record
transfer_id
4210887:3:0
amount_usdc
1250.000000
kind
ERC20_TRANSFER
memo
INV-2026-0914

Index both logs and this transfer is booked twice. Index only the contract and every native send is missed. Both errors balance to a number that looks plausible.

What Arc emits

Five emitters. One ledger.

The system Transfer is always emitted first in a transaction, zero-value and self-transfers emit nothing, and gas deductions emit no log at all. A correct ledger has to know all of that.

EmitterAddressEventsDecimals
System emitter (EIP-7708)0xffffFFFfFFffffffffffffffFfFFFfffFFFfFFfETransfer for every explicit USDC movement, including the native leg of ERC-20 transfers, mints, burns and SELFDESTRUCT moves18
USDC contract0x3600000000000000000000000000000000000000Transfer, Blocklisted, UnBlocklisted6
Denylist0x3600000000000000000000000000000000000004Denylisted and UnDenylisted, the protocol-level compliance list checked before a transaction reaches the pooln/a
Memo0x5294E9927c3306DcBaDb03fe70b92e01cCede505Memo, the payment reference attached to a forwarded calln/a
CCTP TokenMessenger0x28b5a0e9C621a5BadaA536219b3a228C8168cf5dDepositForBurn, USDC leaving Arcn/a
CCTP MessageTransmitter0x81D40F21F12A8F0E3252Bccb954D722d4c464B64MessageReceived, USDC arriving on Arcn/a
The record

Both decimal conventions, every time.

Backfill and live through one stream: supply a from_block and omit to_block to replay history then tail indefinitely. Resumable by cursor. Ordered by block number and log index, never by timestamp, because sub-second blocks share one.

FieldTypeDescription
transfer_idstringDeterministic id: block number, transaction index and leg.The same transfer arriving from both emitters produces one id, which is what stops a naive indexer double counting.
fromaddressSender.
toaddressRecipient.
amount_nativedecimal stringAmount at 18 decimals, the native representation.
amount_usdcdecimal stringThe same amount at 6 decimals, the ERC-20 view.Both representations, always. Publishing one guarantees a class of errors worth 10^12.
kindenumNATIVE_SEND, ERC20_TRANSFER, MINT, BURN, ENDOWMENT, SELFDESTRUCT.
memostring?Payment reference from the Memo contract, decoded, when present.
cctp_legobject?Correlated DepositForBurn or MessageReceived when the transfer crosses chains.
Already attached

The joins you would otherwise write yourself.

01

Memo, decoded

The payment reference from the Memo contract, on the transfer it belongs to, so a receipt matches an invoice without a lookup table.

02

Blocklist state

Blocklisted and UnBlocklisted changes as a side channel, so a router knows before a customer's transfer reverts and burns gas.

03

CCTP legs

DepositForBurn and MessageReceived matched on the CCTP nonce, so a crossing is one movement instead of two unrelated events.

04

Across Zero5

Testnet history before the Zero5 fork used NativeCoinTransferred from 0x1800...0000. Backfills switch emitters at the boundary.

05

Ordered exactly

Sub-second blocks share timestamps, so records carry block number and log index. Ordering is exact, never inferred from time.

06

Never retracted

Arc finalises on commit. A transfer we deliver is final, and the certificate that finalised it is one call away.

Access

Backfill, stream or push.

The same record over three transports. Query a range, subscribe and tail, or have each transfer posted to your endpoint as its block commits.

  1. Backfill a range

    Any address, any block range, from genesis. Paginated and resumable.

  2. Stream it live

    gRPC on Scale: set from_block, omit to_block, and history flows into live without a gap.

  3. Or take webhooks

    Signed deliveries with a replayable log, safe at one confirmation. 10 endpoints on Pro, unlimited on Scale.

Timed from key creation to the first message on the wire.

Transfer feed docs

# every transfer touching an address, over a block rangecurl 'https://rpc.arcrpc.com/v1/YOUR_KEY/usdc/transfers?address=0x2d91...c3f0&from_block=4200000&to_block=4210887'

Stop reconciling emitters by hand.

One record per USDC movement, from genesis to the block that just committed. On every plan.

FAQ

Common questions

Settlement you can prove: every block carries a BFT commit certificate signed by the validator set, so a payment can be shown final offline. Webhooks that act on the first block, because the finalized tag is the latest block. Payment references joined to transfers through Arc's Memo contract. Compliance events from the USDC blocklist and the protocol Denylist. And no public mempool, so nobody can watch or front-run a transaction before it lands.

CCTP v2 legs on domain 26 are in the transfer feed today. Gateway deposits and StableFX FxEscrow settlements are on the roadmap. Both contracts are live on mainnet, but Circle has not documented the FxEscrow events, so we will not ship that index until it is validated against real trades.

Arc keeps one USDC balance behind two interfaces. Native USDC, which eth_getBalance returns, has 18 decimals. The ERC-20 interface at 0x3600...0000 has 6. Mixing them is off by 10^12. arcrpc_getUsdcBalance returns both plus the display value so nobody has to remember which is which.

An ERC-20 transfer on Arc emits two Transfer logs: one from the EIP-7708 system emitter at 18 decimals, one from the USDC contract at 6. The unified transfer feed merges them into one record. If you index raw logs, filter by emitter.

To genesis on mainnet and testnet. Testnet backfills switch emitters at the Zero5 hard fork, where NativeCoinTransferred gave way to EIP-7708 Transfer events; the transfer feed handles that boundary for you.