- Lets users create or access a user-owned wallet using an app-defined user identifier
- Prompts users to set or confirm a PIN to authorize sensitive wallet actions
- Displays the user’s wallet address and the USDC balance it holds
Prerequisites
Before you begin, ensure you have:- A Circle Developer Console account.
- A Circle Developer API key:
Console → Keys → Create a key → API key → Standard Key. - Node.js 18+ installed.
Step 1. Get your Circle App ID
In this step, you obtain the App ID that identifies your user-controlled wallet configuration in the Circle Developer Console, so it can manage the User IDs created by your app.- Log in to the Circle Developer Console.
- Go to Wallets → User Controlled → Configurator, and copy your App ID. You need it for the next step.
Step 2. Create the web application
In this step, you create a Next.js app that lets users set an authorization PIN and use it to create a user-controlled wallet.2.1. Create the Next.js project
In your terminal:2.2. Install dependencies
Install the user-controlled wallets Web SDK:2.3. Add environment variables
Create a.env.local file in your project directory:
.env.local file and add the following:
.env.local
YOUR_CIRCLE_API_KEYis your Circle Developer API key.YOUR_CIRCLE_APP_IDis the Circle Wallet App ID obtained in Step 1.
2.4. Simplify the default layout
Replace the contents ofapp/layout.tsx with the minimal layout below:
app/layout.tsx
Next.js requires an
app/layout.tsx file, but the default one created by
create-next-app includes fonts and styling that can cause build errors in
some environments.2.5. Add unified backend route
Create a file namedapp/api/endpoints/route.ts and add the code below:
app/api/endpoints/route.ts
| Handler | Description |
|---|---|
createUser | calls POST /v1/w3s/users to create (or retrieve) a Circle user using an application-defined userId. |
getUserToken | calls POST /v1/w3s/users/token to create a short-lived user session, returning a userToken and encryptionKey required by the Web SDK to run PIN and wallet challenges. |
initializeUser | calls POST /v1/w3s/user/initialize to initialize the user and return a challengeId required for wallet creation. |
listWallets | calls GET /v1/w3s/wallets to retrieve the wallets associated with the user. |
getTokenBalance | calls GET /v1/w3s/wallets//balances to retrieve digital asset balances for a specified user-controlled wallet. |
This quickstart calls
listWallets and getTokenBalance directly for
simplicity. In production, apps typically store wallet and balance data in a
backend database and keep it in sync using Circle webhooks for scalability.2.6. Add UI and frontend code
Replace the contents ofapp/page.tsx with the code below:
app/page.tsx
userId, retrieves
a userToken, and initializes the user’s wallet on a specified blockchain. The
returned challengeId is executed using the SDK, prompting the user to
authorize the action with a PIN. After wallet creation, the app loads the wallet
details and USDC balance.
Step 3. Run the app flow
- Start the dev server:
- Open http://localhost:3000 in your browser to view the app.
-
Complete the PIN setup and wallet creation flow:
-
Enter a User ID: Provide a unique identifier for the user (for
example, a username or email address). In this PIN-only flow, the app
receives the identifier directly from the user but doesn’t perform
authentication to verify ownership.
A PIN by itself does not verify user identity. This quickstart focuses on PIN-based authorization only. In production, apps should authenticate users first (for example, with social login or email OTP) to establish a Circle-authenticated user session. PINs can then serve as a layer to authorize sensitive wallet actions.
-
Click Create User: Your backend creates a Circle user record from the
provided
userId. Circle associates it with your Console account and user-controlled wallet configuration based on your API key. -
Click Get User Token: Your backend requests a short-lived
userTokenandencryptionKey. The Web SDK uses these credentials to authenticate the user session with Circle and submit subsequent user-scoped challenges. -
Click Initialize user (get challenge): Your backend initializes the
user using the
userToken. If the user hasn’t created a wallet yet, Circle returns achallengeIdrequired to create one. If the user is already initialized, the app loads the existing wallet instead. -
Click Create wallet (execute challenge): The Web SDK executes the
challenge using the
challengeId. Because wallet creation is a sensitive action, Circle opens a hosted UI where the user sets their authorization PIN and security questions. After the user approves the request, Circle creates the wallet.
-
Enter a User ID: Provide a unique identifier for the user (for
example, a username or email address). In this PIN-only flow, the app
receives the identifier directly from the user but doesn’t perform
authentication to verify ownership.
-
Once the flow completes:
- The app displays the wallet’s address, blockchain, and USDC balance.
- You can verify the user was created in the
Circle Dev Console:
Wallets → User Controlled → Users.
Step 4. Fund the wallet
In this step, you fund the new wallet manually using the Circle Faucet and confirm the updated balance in the app.- Copy the wallet address (
0x...) from the web app UI. - Visit the official Circle Faucet.
- Select Arc Testnet as the blockchain network.
- Paste the wallet address in the Send to field.
- Click Send USDC.
- Return to the app and walk through the flow again.
Note: Use the same email address to show the same wallet. - The app will display the updated USDC balance.
In this step, you’re acting as the end user to fund your user-controlled
wallet for testing. In production, app developers don’t control user wallets
or private keys. Instead, users typically fund wallets themselves, but apps
may also fund using faucets or airdrops without requiring wallet access.