Skip to main content
Monitor the native USDC Transfer event from the system address 0xffff…fffe to capture every native USDC movement in one stream (Arc’s EIP-7708 implementation). Index the ERC-20 USDC contract, Memo, blocklist, and CCTP events from their respective contracts for full transaction coverage. Skip reorg-handling logic entirely; Arc’s deterministic finality means every block is permanent. Use block number (not timestamp) as your ordering key because sub-second blocks can share the same block.timestamp.

Prerequisites

Before you begin:
  • Access to an Arc RPC endpoint (https://rpc.testnet.arc.io) or WebSocket (wss://rpc.testnet.arc.io)
  • Familiarity with Ethereum JSON-RPC methods (eth_getLogs, eth_subscribe)
  • A database or indexing pipeline that supports high-throughput block ingestion
  • TypeScript environment with ethers or viem installed

Steps

Step 1. Connect to the block stream

Use eth_subscribe("newHeads") over WebSocket for real-time block notifications. For historical back-fills, use eth_getLogs with fromBlock/toBlock ranges.
Arc produces sub-second blocks. Your ingestion pipeline must handle bursts of many blocks per second without falling behind.

Step 2. Index native USDC Transfer events (EIP-7708)

Every native USDC movement emits a standard ERC-20 Transfer log from the system address 0xfffffffffffffffffffffffffffffffffffffffe (Arc’s EIP-7708 implementation). This single stream covers native sends, the native leg of ERC-20 transfers, and mint and burn, with values in 18 decimals. It is the single source of truth for USDC balance changes. The ERC-20 USDC contract at 0x3600…0000 also emits its own Transfer (6 decimals) for ERC-20-interface activity, so an ERC-20 transfer() produces a log from both emitters. Distinguish them by emitter address and never count the same movement twice. For the complete event matrix, the mint and burn mapping, and the historical pre-Zero5 events, see USDC system events.
Filtering the native system emitter (0xffff…fffe) captures all native USDC movements in one stream: native sends, the native leg of ERC-20 transfers, and mint and burn. If you also index the ERC-20 USDC contract (0x3600…0000) for its 6-decimal Transfer events, match on the emitter address so you do not count ERC-20 transfers twice.
To backfill history across the Zero5 hard fork, read the historical NativeCoin* events from 0x1800…0000 for blocks before activation and Transfer from 0xffff…fffe at and after it. See USDC system events for signatures and the activation reference.

Step 3. Index EURC and other ERC-20 token transfers

EURC and other ERC-20 tokens emit standard Transfer events from their own contract addresses. Index these separately from USDC.

Step 4. Index Memo contract events

The Memo contract lets an account attach an arbitrary memo to a forwarded call (for example, a USDC transfer) for correlation and reconciliation. It executes the call through the CallFrom precompile, preserving the original msg.sender, and emits a Memo event with the metadata. Index Memo events to store memo payloads alongside the calls they annotate.
The Memo contract is available on Arc testnet as of June 18, 2026.
Correlate Memo events with the Transfer (or other target-contract) events they annotate by matching on transactionHash, or use the caller-supplied memoId. To match a specific target call, compare callDataHash with the hash of the calldata your application submitted. The memo provides context (such as an invoice ID or payment reference) for the forwarded call.

Step 5. Index blocklist events

The USDC contract emits Blocklisted and UnBlocklisted events when addresses are added to or removed from the blocklist. Track these to maintain an accurate set of restricted addresses.

Step 6. Index CCTP crosschain events

The Cross-Chain Transfer Protocol (CCTP) uses two contracts on Arc: TokenMessengerV2 for outbound burns and MessageTransmitterV2 for inbound mints.

Step 7. Use block number as your ordering key

Multiple blocks can share the same block.timestamp because Arc produces sub-second blocks that fall in the same wall-clock second. Always use blockNumber (and logIndex in a block) as your canonical ordering key.
Do not use block.timestamp for ordering. Two consecutive blocks (for example, block 100 and block 101) may both have timestamp = 1700000000. Sorting by timestamp produces ambiguous ordering.

Step 8. Simplify your pipeline—no reorg handling required

Arc provides deterministic finality. Once a block appears, it is permanent. You can remove the following from your indexing pipeline:
  • Reorg detection and rollback logic
  • Confirmation-depth delays (no need to wait for N confirmations)
  • Uncle/ommer block handling
  • Chain reorganization event listeners
If your indexer restarts, resume from the last processed block number. You do not need to re-validate previously indexed blocks because they cannot be reverted.

Event reference

See also