Skip to main content
Detect USDC deposits on Arc by generating addresses, monitoring the native USDC Transfer event from the system emitter 0xffff…fffe (Arc’s EIP-7708 implementation), crediting after a single block confirmation (deterministic finality guarantees no reorgs), and sweeping funds into a hot wallet.

Prerequisites

Before you begin, ensure that you’ve:
  • Obtained access to an Arc RPC endpoint (https://rpc.testnet.arc.io) or WebSocket (wss://rpc.testnet.arc.io)
  • Installed an HD wallet library for generating deposit addresses (for example, ethers or viem)
  • Familiarized yourself with Ethereum JSON-RPC methods and event log filtering
  • Set up a database to track processed deposits and prevent double-crediting

Steps

Step 1. Generate deposit addresses

Arc uses standard Ethereum addresses (0x-prefixed, 20 bytes, EIP-55 checksum). Derive deposit addresses using the same HD wallet approach as Ethereum—one unique address per user.
Store the mapping between user IDs and their derived address index. Never expose the mnemonic or private keys in client-side code.

Step 2. Subscribe to new blocks

Use eth_subscribe("newHeads") over WebSocket for real-time block notifications, or poll eth_blockNumber over HTTP as a fallback.

Step 3. Detect incoming transfers with the native USDC Transfer event

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 plain native sends and the native leg of ERC-20 transfers, with values in 18 decimals. Filter it to catch every deposit, including native sends that emit no event on the ERC-20 contract.
Do not filter the ERC-20 USDC contract (0x3600…0000) for deposit detection. Its Transfer events cover only ERC-20-interface activity, so a plain native send produces no log there and the deposit is missed. Filter the system emitter (0xffff…fffe) instead. See USDC system events for the full event matrix.
Event signature:
Topic0: 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef Native USDC system emitter: 0xffffFFFfFFffffffffffffffFfFFFfffFFFfFFfE Use eth_getLogs to filter for transfers to your deposit addresses:
Do not credit the same deposit twice. An ERC-20 transfer() emits a log from both the system emitter (0xffff…fffe, 18 decimals) and the ERC-20 contract (0x3600…0000, 6 decimals); filter only the system emitter. Likewise, do not reconcile eth_getBalance against Transfer events—they represent the same balance.

Step 4. Confirm the deposit

Arc provides deterministic finality—once a transaction is included in a block, it is final with no possibility of reorg. You can safely credit deposits after 1 confirmation.
Since Arc has deterministic finality, you do not need to wait for multiple confirmations. Credit the user once the block containing their transaction is produced.

Step 5. Handle decimal conversion

The native system Transfer event emits values with 18 decimals. Convert to 6-decimal USDC (divide by 10^12) before crediting user balances.
The native system Transfer event and eth_getBalance both use 18 decimals; the ERC-20 contract’s Transfer and balanceOf use 6. Never mix representations—convert 18-decimal values by dividing by 10^12 before displaying or crediting.

Step 6. Sweep deposits to a hot wallet

Consolidate deposited funds from individual user addresses into your hot wallet. Use EIP-1559 transactions with Arc’s minimum base fee of 20 Gwei.
When sweeping using a native USDC send, convert from 6-decimal amounts to 18-decimal value fields. Alternatively, call the ERC-20 transfer function on the USDC contract, which accepts 6-decimal amounts directly.

Common mistakes

Avoid these pitfalls when implementing deposits.
  • Filtering the wrong emitter: Plain native USDC sends emit no Transfer on the ERC-20 contract (0x3600…0000). Filter the system emitter (0xffff…fffe) so you do not miss native deposits.
  • Double-counting: An ERC-20 transfer() logs from both emitters. Filter only the system emitter, and do not reconcile eth_getBalance against Transfer events for the same address.
  • Decimal mismatch: The system emitter’s values use 18 decimals. Treating them as 6-decimal amounts inflates credited balances by 10^12—divide by 10^12 first.
  • Waiting for multiple confirmations: Arc has deterministic finality. Waiting for 6+ confirmations adds unnecessary latency with no security benefit.
  • Hardcoded keys: Never embed private keys in source code. Use environment variables or a secrets manager for sweep wallet credentials.
  • Meta-transaction sweep of a brand-new address: If you sweep deposit addresses using a transferWithAuthorization (EIP-3009) meta-transaction rather than a direct native send, be aware that the relayer’s transaction does not increment the deposit address’s nonce. A meta-transaction that fully drains an address with zero nonce reverts; the post-drain state (zero nonce, zero balance, no code) is blocked by Arc. Initialize the deposit address with any prior transaction to set a non-zero nonce before sweeping using a meta-transaction. A protocol fix is planned for a future release. See Known limitation: draining an empty account.