ASK KNOX
beta
LESSON 265

USDC.e vs Native USDC on Polygon

Polymarket collateral is bridged USDC.e at 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174, NOT native USDC at 0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359. Sending the wrong one equals $0 balance forever. How to verify before funding.

7 min read

Two tokens. Both called USDC. Both worth a dollar. Only one is accepted as Polymarket collateral. Send the wrong one and your balance stays at zero forever.

The Addresses

Memorize these. Or better, store them in a config file that is read at startup and logged.

TokenAddressPolymarket accepts?
USDC.e (bridged, PoS)0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174YES
Native USDC (Circle CCTP)0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359NO

These two contracts are not interchangeable. A wallet can hold both at the same time with no automatic conversion between them. The Polymarket CLOB only looks at the USDC.e balance. Your wallet may show "$100 USDC" — which could be $100 of native, $100 of USDC.e, or $50 of each — and Polymarket will only credit the USDC.e portion.

Inline Diagram — Two Tokens, One Name

TWO USDC TOKENS ON POLYGONUSDC.e (BRIDGED)0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174bridged via Polygon PoSfrom Ethereum USDCPOLYMARKET COLLATERALUSDC (NATIVE CIRCLE)0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359minted natively by Circlevia CCTPNOT ACCEPTED BY POLYMARKET

The Failure Mode

Here is the exact mistake Hermes almost made during funding:

  1. Bot config specifies wallet 0xCF56...0aD.
  2. Engineer bridges USDC from Ethereum to Polygon using the latest Circle CCTP bridge.
  3. Native USDC arrives at 0xCF56...0aD on Polygon.
  4. Engineer checks the wallet: "$14.99 USDC. Funded."
  5. Hermes queries Polymarket /balance-allowance: $0.00.
  6. Two hours of debugging.

The bridge worked perfectly. The wallet received the tokens. The problem was that the tokens were the wrong flavor of USDC.

The fix was to either (a) bridge again using the older PoS bridge path to get USDC.e, or (b) swap the native USDC for USDC.e on a DEX. Option (b) is faster for small amounts. Neither is free.

The Prevention Pattern

At startup, every Polymarket bot should log and verify:

POLYMARKET_COLLATERAL = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174"
expected_balance = usdc_e_contract.balanceOf(wallet)  # check USDC.e specifically
polymarket_balance = clob.get_balance_allowance(asset=POLYMARKET_COLLATERAL)["balance"]
assert expected_balance > 0, f"USDC.e balance is zero on {wallet}"
assert polymarket_balance == expected_balance, "On-chain balance and Polymarket balance diverge"
logger.info(f"Wallet {wallet} funded with {expected_balance} USDC.e")

This check takes one RPC call plus one API call. It catches the wrong-token mistake before any trade is attempted.

The Rule

One address. One token. Verified at startup. The "two USDCs" problem will keep claiming engineer-hours until every bot logs its collateral contract address and checks it before claiming to be funded.