Use @circle-fin/user-controlled-wallets
on the server and
@circle-fin/w3s-pw-web-sdk
in the browser to authenticate with email OTP, initialize a user-controlled
wallet, and list it.
In user-controlled wallets, privileged actions (initialize wallets, sign transactions, and similar) do not complete on the server alone. Circle returns a challenge: an authorization request the end user must complete in the browser.
Flow for this sample:
- Email OTP (login rail): server issues device/OTP tokens; the Web SDK
verifies the code and returns a
userTokenandencryptionKey. - Server calls initialize with that
userToken. On first login, Circle returns achallengeIdto create the wallet. - Browser calls
sdk.setAuthentication({ userToken, encryptionKey }), thensdk.execute(challengeId, …). Whenexecutesucceeds, the wallet exists; this sample then lists it.
A challenge is not an on-chain transaction by itself. It is Circle’s way of requiring end-user approval before a sensitive wallet operation finishes.
OTP login is not the challenge. Login only produces session credentials.
The challenge runs afterward on first initialize. If the user is already
initialized (155106), this sample lists wallets and does not run
execute.
| Step | What happens | Challenge? |
|---|---|---|
| Send OTP | createDeviceTokenForEmailLogin → configure SDK |
No |
| Verify OTP | sdk.verifyOtp() → userToken / encryptionKey |
No |
| Initialize (first time) | POST /user/initialize → challengeId → execute |
Yes — create wallet |
| Initialize (again) | 155106 already initialized → list wallets |
No |
This sample creates an SCA wallet on Arc Testnet.
- Node.js 22+
- A Circle Console app with:
- API key →
CIRCLE_API_KEY - App ID →
VITE_CIRCLE_APP_ID
- API key →
- A reachable inbox for the OTP (or Mailtrap / similar in Console)
cp .env.example .env
# fill CIRCLE_API_KEY and VITE_CIRCLE_APP_ID
npm installTwo processes:
npm run servernpm run devOpen the Vite URL, enter an email, Send OTP, then Verify OTP. On first login, complete the challenge UI when it appears.
| File | Role |
|---|---|
server.ts |
API key; device/OTP tokens; initialize (REST); list wallets |
src/main.ts |
Web SDK: OTP login, execute challenge, list wallets |
index.html |
Minimal UI for the email path |