Building onchain apps on
@arc with Circle wallet but not sure which wallet to use?
Let me explain.
Developer-Controlled, User-Controlled, or Modular each one is built for a different use case, and picking the wrong one wastes weeks.
Here's a complete breakdown to help you choose set up the right one from day one
👇🧵
First, the big question:
❓ Who controls the private keys?
→ Your backend controls them = Developer-Controlled
→ The end user controls them = User-Controlled or Modular
This single decision shapes your entire architecture.
DEVELOPER-CONTROLLED WALLETS
Your app holds custody of all private keys.
Users never see or touch them.
Best for:
• Automated payment backends
• AI agents
• Programmatic payouts
• High-TPS systems
Setting up Developer-Controlled Wallets:
1️⃣ npm install
@circle-fin/developer-controlled-wallets
2️⃣ Set env vars:
CIRCLE_API_KEY=...
ENTITY_SECRET=...
3️⃣ Generate register your 32-byte entity secret at Circle Console
⚠️ Never share it. Store the recovery file OUTSIDE your repo.
Dev-Controlled SDK init: const client = initiateDeveloperControlledWalletsClient({ apiKey:
process.env.CIRCLE_API_KEY, entitySecret: process.env.ENTITY_SECRET, }); Then create a WalletSet → create Wallets inside it → transfer tokens. Each mutating request needs a UUID v4 idempotencyKey.
Dev-Controlled SDK init:
const client = initiateDeveloperControlledWalletsClient({
apiKey:
process.env.CIRCLE_API_KEY,
entitySecret: process.env.ENTITY_SECRET,
});
Then create a WalletSet → create Wallets inside it → transfer tokens.
USER-CONTROLLED WALLETS
The end user owns their private keys.
Your app never has custody.
Auth options:
• Google / Apple / Facebook login
• Email OTP
• PIN
Best for:
• Consumer apps
• NFT marketplaces
• DeFi platforms on Ethereum or L2
User-Controlled needs BOTH backend frontend:
npm install
@circle-fin/user-controlled-wallets@latest
npm install
@circle-fin/w3s-pw-web-sdk@latest
npm install vite-plugin-node-polyfills
Env vars:
CIRCLE_API_KEY= (backend only!)
CIRCLE_APP_ID= (frontend)
🚨 Never expose your API key to the frontend.
How User-Controlled wallet creation works:
1. Backend creates user gets userToken & encryptionKey
2. Backend calls Circle API → gets a challengeId
3. Frontend receives challengeId tokens
4. User approves via Circle's hosted UI
5. Wallet is created ✅
This challenge-response model = user always consents.
Frontend challenge execution:
const sdk = new W3SSdk({ appSettings: { appId: APP_ID } });
await sdk.getDeviceId(); // MUST call this first!
sdk.setAuthentication({ userToken, encryptionKey });
sdk.execute(challengeId, (error, result) => {
// user approved ✅
});
⚠️ Skip getDeviceId() and execute() silently fails.
MODULAR WALLETS (Passkey)
User owns keys via biometric auth (Face ID / fingerprint).
Supports custom smart contract modules.
Best for:
• DeFi apps needing biometric UX
• Multisig setups
• Session keys & spending limits
• Gasless L2 experiences
MSCA account type only.
Modular Wallets — important chain limits:
✅ Supported: Base, Arbitrum, Polygon, Optimism, Avalanche, Unichain, Monad
❌ NOT supported: Ethereum mainnet, Solana, Aptos, NEAR
If you need Ethereum mainnet non-custodial → use User-Controlled EOA instead.
EOA vs SCA — which account type?
🔹 EOA: Default. Works everywhere (EVM Solana Aptos). No creation fees. Higher TPS. Needs native gas tokens.
🔹 SCA: EVM L2s only. Gas sponsorship via Circle Gas Station. Supports batch txns. Avoid on Ethereum mainnet (high gas cost).
Developer Building Pick your wallet:
🤖 Automating payouts / AI agents → Developer-Controlled EOA
👤 Consumer app (Google/email login) → User-Controlled SCA on L2
🔐 DeFi biometric auth custom logic → Modular MSCA on L2
🖼 NFT app on Ethereum → User-Controlled EOA
Docs:
developers.circle.com/wallet…
Once again build on
@arc the OS infrastructure is designed to suit builders