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.
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.
| Token | Address | Polymarket accepts? |
|---|---|---|
| USDC.e (bridged, PoS) | 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174 | YES |
| Native USDC (Circle CCTP) | 0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359 | NO |
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
The Failure Mode
Here is the exact mistake Hermes almost made during funding:
- Bot config specifies wallet
0xCF56...0aD. - Engineer bridges USDC from Ethereum to Polygon using the latest Circle CCTP bridge.
- Native USDC arrives at
0xCF56...0aDon Polygon. - Engineer checks the wallet: "$14.99 USDC. Funded."
- Hermes queries Polymarket
/balance-allowance: $0.00. - 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.