How it works
An EVM address is derived from a public key through hierarchical deterministic (HD) derivation rooted in a user’s identity. Two wallets derived under the same identity produce the same public key, and therefore the same address on every EVM blockchain. Circle groups wallets by identity so they share a key: through a JWT for user-controlled wallets, and through a wallet set plus arefId for developer-controlled wallets.
User-controlled wallets
CallPOST /user/wallets
with the user’s JWT and an array of the EVM blockchains you want wallets on:
Developer-controlled wallets
Group wallets under a sharedwalletSetId and refId. The refId typically
maps to a user ID in your system.
Create wallets with a shared address
Call POST /wallets with awalletSetId, an array of EVM blockchains, and an array of refIds:
refId produces the same address.
Retrieve the wallets with
GET /wallets.
Add a supported blockchain
To add a new EVM blockchain to a user’s existing set of wallets, callPUT /wallets/{id}/blockchains/{blockchain}
with the walletId of any of the user’s existing EVM wallets. Circle derives a
wallet on the new blockchain at the same address.
Recover tokens from unsupported blockchains
If a user accidentally deposits tokens to an EVM blockchain Circle doesn’t support, you can still access the funds by signing a raw transaction with the genericEVM blockchain. For background on how Circle’s signing APIs work, see
Signing APIs.
1
Create a wallet on the generic EVM blockchain
Create a wallet with
blockchain: "EVM" under the same walletSetId and
refId. Circle derives a wallet at the user’s address.2
Sign a raw transaction to move the assets
Use Sign
transaction
to sign a raw transaction that moves the assets.
3
Broadcast the signed transaction
Broadcast the signed transaction to the blockchain yourself. Circle doesn’t
broadcast on unsupported blockchains.
Address indexes
Wallets in a wallet set are derived by index. On EVM, a single index maps to the same address on every EVM blockchain the wallet is created on. Index assignment behavior forPOST /wallets:
- Single-blockchain call, no existing wallets: index starts at
0x1. - Multi-blockchain call: uses the latest available index across the group.
- Single-blockchain call with existing wallets on that blockchain: increments the index on that blockchain.
- New blockchain with no existing wallets on it: starts at
0x1.
POST /wallets calls produce the following
indexes:
Empty cells are gaps. To fill a gap and derive a wallet at an existing address
on a new blockchain, use
PUT /wallets/{id}/blockchains/{blockchain}.
Don’t create a unique
walletSetId per user. Wallet
sets are
organizational boundaries for scaling (for example, one set per tenant or
product line), not per-user containers.