Use Arc’s ERC-20 USDC interface exclusively for all pool accounting, token
addresses, and reserve math. The native interface (address.balance,
msg.value) uses 18 decimals and is for gas operations only; mixing it with the
6-decimal ERC-20 interface silently breaks constant-product calculations by a
factor of 1012. The following steps apply this principle and address
five other changes required before deploying to Arc.
Prerequisites
Before you begin, ensure that you’ve:
- Read the stablecoin-native model
concept page to understand Arc’s dual USDC interface
- Reviewed the EVM differences reference for
a complete list of protocol divergences
- Obtained Arc testnet RPC access at
https://rpc.testnet.arc.io (chain ID
5042002)
Steps
Step 1. Set the pool token to the ERC-20 USDC address
Use 0x3600000000000000000000000000000000000000 as the USDC pair address in all
pool and router configuration.
On other EVM blockchains, protocols use a WETH-style adapter contract to give
the native asset an ERC-20 interface. Arc’s native USDC already has a built-in
ERC-20 interface, so no adapter is needed. The address
0x3600000000000000000000000000000000000000 is that interface. It already
implements transfer, approve, and transferFrom over the native balance.
Do not pair native USDC against the ERC-20 USDC interface as two separate pool
tokens. Both interfaces draw from the same underlying balance, so pairing them
is equivalent to pairing an asset with itself. A pool configured this way is
immediately insolvent.
Two additional mistakes to avoid:
- The zero address (
0x0000000000000000000000000000000000000000) is not USDC.
Do not use it as a token address. Value-bearing transfers to it revert on Arc;
see
Value transfer rules.
- Any address other than
0x3600000000000000000000000000000000000000 used to
represent USDC creates a separate, unrelated token that does not share the
native balance.
Update your pool factory, router configuration, and any initialization scripts
to reference 0x3600000000000000000000000000000000000000 directly.
Arc’s native USDC has 18 decimals (address.balance, eth_getBalance,
msg.value) and the ERC-20 interface has 6 decimals (balanceOf, transfer).
The conversion factor between them is 1012.
Pool contracts that follow the ERC-20 prescription do not encounter this offset:
deposits go through transferFrom, reserves are read from balanceOf, and
nonpayable functions block native USDC from entering pool logic. The offset
matters in offchain code: monitoring scripts, indexers, and SDK integrations
that read eth_getBalance or address.balance must convert to 6-decimal units
before comparing or displaying values.
Use these constants in your offchain scripts and SDK integrations:
Step 3. Remove WETH-style wrap/unwrap code paths
On most EVM blockchains, the native asset has no ERC-20 interface, so pools use
a wrapper contract (WETH, WMATIC, and so on) with deposit() and withdraw()
functions. On Arc, native USDC already has a built-in ERC-20 interface. Any
WETH-style wrap/unwrap code paths are unnecessary and create double-accounting
bugs by crediting balances twice.
Replace the wrapped native address constant and remove all deposit() and
withdraw() call sites:
Note that internal-only wrapping that is not exposed to users (for example, an
internal accounting abstraction similar to what Chainlink uses) is acceptable.
User-facing WUSDC wrapper contracts are not.
Step 4. Audit allowance-dependent sweep and security logic
ERC-20 allowances control transfers initiated through transferFrom. They do
not gate direct native transfers. A contract holding an approve from a user
can still have its native USDC balance moved by a direct native send
(call{value: ...}) without any allowance check.
Any sweep mechanism or authorization boundary that relies on ERC-20 allowance
limits to protect the contract’s native USDC balance is ineffective. Audit your
pool’s security logic for this pattern and apply one of the following
approaches:
- Mark the contract
nonpayable on functions that should not receive native
USDC, preventing accidental native deposits.
- Add an explicit
receive() function that either reverts or records the
incoming balance, so the contract behaves predictably on both the native and
ERC-20 paths.
Step 5. Replace onchain randomness and audit emergency-shutdown patterns
Two EVM opcodes behave differently on Arc and require review before deployment.
PREVRANDAO always returns 0 on Arc. Any protocol that uses
block.difficulty or prevrandao for randomness receives a constant value,
making lottery mechanics, shuffle algorithms, and similar constructs
predictable and exploitable. Replace these with a verifiable random function
(VRF) or oracle-based randomness. See Oracles for
available providers on Arc.
Emergency-shutdown and escape-hatch patterns that use SELFDESTRUCT also
require review. On Arc, a contract’s USDC balance is its native balance, so
SELFDESTRUCT transfers that USDC to the beneficiary address. On Ethereum, the
ERC-20 USDC balance lives in the token contract and is unaffected by
SELFDESTRUCT.
SELFDESTRUCT moves native USDC on Arc. Protocols with emergency-shutdown
patterns that call SELFDESTRUCT will transfer the contract’s entire USDC
balance to the beneficiary. Audit every code path that calls SELFDESTRUCT
and verify that the USDC transfer is intentional.
Step 6. Test against an Arc RPC endpoint, not a local Anvil fork
Local EVM simulators run a standard EVM and cannot reproduce the following
Arc-specific behaviors:
- Native-coin precompile behavior
- EIP-7708 Transfer events on native USDC movements
- Blocklist enforcement on value transfers
- Arc’s native USDC value transfer rules
Run all pool invariant math and value transfer tests against
https://rpc.testnet.arc.io (chain ID 5042002). For a complete porting
walkthrough covering contract deployment and verification, see
Porting contracts to Arc.