Create a pool of developer-controlled wallets before users arrive so wallets are
ready to assign instantly. When a new user signs up, assign an available wallet
by updating its name and refId fields. This approach reduces sign-up latency
because wallet creation happens ahead of time rather than during registration.
Cost consideration for Externally Owned Account (EOA) walletsCreating EOA wallets on mainnet incurs a minor expense per wallet. To optimize
costs, forecast user demand and batch-create wallets accordingly. For example,
prepare enough wallets to cover the expected demand for the next 30 days to
avoid unnecessary expenses.
Prerequisites
Before you begin, ensure that you’ve:
Step 1. Create a pool of unassigned wallets
Call the create wallets endpoint with a count value but without metadata.
Omitting metadata creates wallets that have no name or refId, leaving them
available for assignment later.
You can create up to 200 wallets per request. If you need more, make additional
requests until you reach the required total.
import { initiateDeveloperControlledWalletsClient } from "@circle-fin/developer-controlled-wallets";
const client = initiateDeveloperControlledWalletsClient({
apiKey: process.env.CIRCLE_API_KEY!,
entitySecret: process.env.CIRCLE_ENTITY_SECRET!,
});
const response = await client.createWallets({
walletSetId: "your-wallet-set-id",
blockchains: ["ARC-TESTNET"],
count: 50,
accountType: "SCA",
});
console.log(response.data?.wallets);
The response contains the newly created wallets. The name and refId fields
are omitted because no metadata was provided:
{
"data": {
"wallets": [
{
"id": "66b67097-307e-5b46-a1d5-0b1577d67fd4",
"state": "LIVE",
"walletSetId": "018b42d2-cc38-7a1e-a47a-5927d2c97f16",
"custodyType": "DEVELOPER",
"address": "0xe55628c98f5d81daaa79b72899b38a3535d10990",
"blockchain": "ARC-TESTNET",
"accountType": "SCA",
"updateDate": "2024-01-22T22:57:20Z",
"createDate": "2024-01-22T22:57:20Z"
},
{
"id": "cda61d8d-7d46-5a39-a8a6-6b4a3d6fdac3",
"state": "LIVE",
"walletSetId": "018b42d2-cc38-7a1e-a47a-5927d2c97f16",
"custodyType": "DEVELOPER",
"address": "0x29b27e792b8b854e48e85ab4f456cf5a9f1579fb",
"blockchain": "ARC-TESTNET",
"accountType": "SCA",
"updateDate": "2024-01-22T22:57:20Z",
"createDate": "2024-01-22T22:57:20Z"
}
]
}
}
Step 2. Store wallet IDs
Persist the returned wallet IDs in your system and mark each one as
unassigned. The storage mechanism is application-specific—you might use a
database table, a key-value store, or any other persistence layer your
application already uses.
Track each wallet’s assignment status so that when a new user signs up, your
application can quickly select an unassigned wallet and avoid double-assigning
the same wallet to multiple users. A simple boolean flag or status column (for
example, assigned: false) works for most implementations.
Step 3. Assign a wallet to a new user
When a user registers, select an unassigned wallet from the pool and call the
update wallet endpoint to set its name and refId. The refId field is
designed for linking wallets to entities in your own system, such as a user ID.
This endpoint does not require entitySecretCiphertext, so no entity secret
encryption is needed for this call.
const walletId: string = "66b67097-307e-5b46-a1d5-0b1577d67fd4";
const userId: string = "d4f2c8a1-9b3e-4f7d-a6e5-1c8b9d0e3f2a";
const updated = await client.updateWallet({
id: walletId,
name: "User Wallet",
refId: userId,
});
console.log(updated.data?.wallet);
The response confirms the wallet now has the assigned name and refId:
{
"data": {
"wallet": {
"id": "66b67097-307e-5b46-a1d5-0b1577d67fd4",
"state": "LIVE",
"walletSetId": "018b42d2-cc38-7a1e-a47a-5927d2c97f16",
"custodyType": "DEVELOPER",
"refId": "d4f2c8a1-9b3e-4f7d-a6e5-1c8b9d0e3f2a",
"name": "User Wallet",
"address": "0xe55628c98f5d81daaa79b72899b38a3535d10990",
"blockchain": "ARC-TESTNET",
"accountType": "SCA",
"updateDate": "2024-01-22T23:15:42Z",
"createDate": "2024-01-22T22:57:20Z"
}
}
}
After the update succeeds, mark the wallet as assigned in your own system so
it is no longer available in the unassigned pool.
Step 4. Verify the assignment
Call the list wallets endpoint filtered by refId to confirm the wallet is
correctly assigned to the user.
const userId: string = "d4f2c8a1-9b3e-4f7d-a6e5-1c8b9d0e3f2a";
const wallets = await client.listWallets({
refId: userId,
});
console.log(wallets.data?.wallets);
The response returns only wallets matching the specified refId:
{
"data": {
"wallets": [
{
"id": "66b67097-307e-5b46-a1d5-0b1577d67fd4",
"state": "LIVE",
"walletSetId": "018b42d2-cc38-7a1e-a47a-5927d2c97f16",
"custodyType": "DEVELOPER",
"refId": "d4f2c8a1-9b3e-4f7d-a6e5-1c8b9d0e3f2a",
"name": "User Wallet",
"address": "0xe55628c98f5d81daaa79b72899b38a3535d10990",
"blockchain": "ARC-TESTNET",
"accountType": "SCA",
"updateDate": "2024-01-22T23:15:42Z",
"createDate": "2024-01-22T22:57:20Z"
}
]
}
}