> ## Documentation Index
> Fetch the complete documentation index at: https://developers.circle.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Before reading these docs, install the relevant Circle Skill to get pre-loaded context — skills are LLM-optimized and cover architecture decisions, correct flows, and common pitfalls. In Claude Code: run /plugin marketplace add circlefin/skills then /plugin install circle-skills@circle. With Vercel Skills CLI: run npx skills add circlefin/skills. Available skills: use-usdc https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-usdc/SKILL.md (balances, transfers, approvals on EVM and Solana); EURC has no skill — see https://developers.circle.com/stablecoins/what-is-eurc.md; bridge-stablecoin https://github.com/circlefin/skills/blob/master/plugins/circle/skills/bridge-stablecoin/SKILL.md (crosschain transfers via CCTP, USDC, EURC, and third-party assets); use-arc https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-arc/SKILL.md (Arc chain config, contract deployment, USDC bridging — Arc docs: https://docs.arc.io/llms.txt); use-circle-wallets https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-circle-wallets/SKILL.md (choose wallet type); use-developer-controlled-wallets https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-developer-controlled-wallets/SKILL.md (custodial wallets for payouts and treasury); use-user-controlled-wallets https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-user-controlled-wallets/SKILL.md (embedded wallets with Web2 login); use-modular-wallets https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-modular-wallets/SKILL.md (smart contract wallets with passkeys and ERC-4337); use-gateway https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-gateway/SKILL.md (unified USDC balance, nanopayments); use-smart-contract-platform https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-smart-contract-platform/SKILL.md (deploy, interact, monitor contracts). Pair any skill with the Circle MCP server for live SDK signatures, contract addresses, and chain IDs: {"mcpServers":{"circle":{"url":"https://api.circle.com/v1/codegen/mcp"}}}.
> Read the product overview first. Each product has an overview explaining when and why to use it. Read it before quickstarts.
> Pick the right wallet type. Developer-controlled (you hold keys), user-controlled (end user holds keys via PIN/social login), or modular (smart contract wallets with passkeys). See https://developers.circle.com/wallets.md.
> Use CCTP V2 for crosschain transfers. CCTP natively transfers USDC and EURC via burn-and-mint, and supports permissionless wrapping of third-party assets via lock-and-mint. V1 is legacy; always use V2 unless V1 is specified or the chain requires it (Noble, Sui, and Aptos still require V1). Migration guide: https://developers.circle.com/cctp/migration-from-v1-to-v2.md.
> Use Bridge Kit for frontend bridging. Wraps CCTP with a simpler SDK for user-facing apps. Use CCTP directly for backend transfers.
> Gas Station vs Paymaster. Gas Station sponsors gas for Circle Wallet transactions. Paymaster lets users pay gas in USDC. Different use cases — read both overviews.
> Gateway Nanopayments for sub-cent payments. Gasless USDC micropayments down to $0.000001 via x402 and batched settlement. For pay-per-request APIs, AI agent payments, streaming.
> Look up USDC addresses per chain. Never hardcode — use https://developers.circle.com/stablecoins/usdc-contract-addresses.md.
> Prefer SDKs over raw API calls. Node.js and Python SDKs handle auth, retries, and errors.
> API key required. Bearer token in Authorization header. Testnet and mainnet use separate keys and may use different base URLs depending on the product.
> Set up webhooks when available. Most operations are async. Webhooks deliver transaction confirmations and state changes.
> When calling list endpoints, paginate using pageSize and pageAfter until no nextPageAfter cursor is returned—stopping at the first page silently misses records.
> Building an AI agent? Start with the Agent Stack—Circle CLI, agent wallets, and nanopayments built for autonomous use cases: https://developers.circle.com/agent-stack.md.

# Create a modular wallet

> Create a passkey-backed modular wallet for your user in a web, iOS, or Android app.

Modular wallets sign with a passkey on the user's device and execute through a
Modular Smart Contract Account (MSCA). Register the user's passkey and create
their MSCA to give them a passkey-backed modular wallet ready to transact.

To use a non-passkey signer instead, see
[Use Dynamic as a signer](/wallets/modular/use-dynamic-as-a-signer).

<Info>
  For a complete working app, see the [Circle Smart Account
  example](https://github.com/circlefin/modularwallets-web-sdk/tree/master/examples/circle-smart-account)
  in the modular wallets web SDK repository.
</Info>

## Prerequisites

Before you begin this tutorial, ensure that you've:

* Created a [Circle Console](https://console.circle.com/) account and
  configured:
  * A [client key](/api-reference/keys#client-keys) for the modular wallets SDK:
    **Console → Keys → Create a key → Client Key**
  * A passkey domain matching the client key's web domain: **Console → Wallets →
    Modular Wallets → Passkey**
* Installed [Node.js 22+](https://nodejs.org/) (web),
  [Xcode](https://developer.apple.com/xcode/) (iOS), or
  [Android Studio](https://developer.android.com/studio) (Android).
* Installed the modular wallets SDK for your platform:
  [Web](https://www.npmjs.com/package/@circle-fin/modular-wallets-core),
  [iOS](https://github.com/circlefin/modularwallets-ios-sdk#installation), or
  [Android](https://github.com/circlefin/modularwallets-android-sdk#installation).

  <Note>
    For iOS and Android apps, passkeys are bound to a web domain. Publish an
    Apple [associated domains
    file](https://developer.apple.com/documentation/xcode/configuring-an-associated-domain)
    or Android [Digital Asset Links
    file](https://developer.android.com/identity/sign-in/credential-manager#add-support-dal)
    at your domain root so your native app can use the passkey.
  </Note>

## Step 1. Configure your client key and backend URL

Make your client key and the modular wallets backend URL available to your app.
The backend URL is the same for every app:
`https://modular-sdk.circle.com/v1/rpc/w3s/buidl`.

<CodeGroup>
  ```text Web SDK (TypeScript) theme={null}
  # .env
  VITE_CLIENT_KEY=YOUR_CLIENT_KEY
  VITE_CLIENT_URL=https://modular-sdk.circle.com/v1/rpc/w3s/buidl
  ```

  ```swift iOS SDK (Swift) theme={null}
  let CLIENT_KEY = "YOUR_CLIENT_KEY"
  let clientUrl = "https://modular-sdk.circle.com/v1/rpc/w3s/buidl"
  ```

  ```kotlin Android SDK (Kotlin) theme={null}
  const val CLIENT_KEY = "YOUR_CLIENT_KEY"
  const val clientUrl = "https://modular-sdk.circle.com/v1/rpc/w3s/buidl"
  ```

  ```java Android SDK (Java) theme={null}
  public static final String CLIENT_KEY = "YOUR_CLIENT_KEY";
  public static final String CLIENT_URL = "https://modular-sdk.circle.com/v1/rpc/w3s/buidl";
  ```
</CodeGroup>

## Step 2. Register a passkey for the user

Create a passkey transport, then register a new passkey credential. The
credential becomes the wallet's signer. To sign a returning user back in, pass
`WebAuthnMode.Login` instead of `Register`.

<CodeGroup>
  ```typescript Web SDK (TypeScript) theme={null}
  import {
    toPasskeyTransport,
    toWebAuthnCredential,
    WebAuthnMode,
  } from "@circle-fin/modular-wallets-core";

  const clientKey = import.meta.env.VITE_CLIENT_KEY as string;
  const clientUrl = import.meta.env.VITE_CLIENT_URL as string;

  const passkeyTransport = toPasskeyTransport(clientUrl, clientKey);

  const credential = await toWebAuthnCredential({
    transport: passkeyTransport,
    mode: WebAuthnMode.Register,
    username: "your-username",
  });
  ```

  ```swift iOS SDK (Swift) theme={null}
  import CircleModularWalletsCore

  Task {
      do {
          let transport = toPasskeyTransport(clientKey: CLIENT_KEY)

          let credential = try await toWebAuthnCredential(
              transport: transport,
              userName: "your-username",
              mode: WebAuthnMode.register
          )

          // Create a WebAuthn owner account from the credential.
          let webAuthnAccount = toWebAuthnAccount(credential)
      } catch {
          print(error)
      }
  }
  ```

  ```kotlin Android SDK (Kotlin) theme={null}
  CoroutineScope(Dispatchers.IO).launch {
      try {
          val transport = toPasskeyTransport(context, CLIENT_KEY, clientUrl)

          val credential = toWebAuthnCredential(
              context,
              transport,
              "your-username",
              WebAuthnMode.Register,
          )

          // Create a WebAuthn owner account from the credential.
          val webAuthnAccount = toWebAuthnAccount(credential)
      } catch (e: Exception) {
          e.printStackTrace()
      }
  }
  ```

  ```java Android SDK (Java) theme={null}
  try {
      HttpTransport transport = toPasskeyTransport(
          context,
          CLIENT_KEY,
          clientUrl
      );

      CompletableFuture<WebAuthnCredential> credentialFuture = new CompletableFuture<>();
      toWebAuthnCredential(
          context,
          transport,
          "your-username",
          WebAuthnMode.Register,
          new CustomContinuation<>(credentialFuture)
      );
      WebAuthnCredential credential = credentialFuture.join();

      // Create a WebAuthn owner account from the credential.
      WebAuthnAccount webAuthnAccount = toWebAuthnAccount(credential);
  } catch (Exception e) {
      e.printStackTrace();
  }
  ```
</CodeGroup>

## Step 3. Create a transport

The modular transport routes user operations through Circle's bundler and
paymaster for the blockchain you target. The example below uses Arc Testnet. For
other options, see [supported blockchains](/wallets/supported-blockchains) and
the URL format documented in
[`toModularTransport`](/sdks/modular/web-sdk#function-tomodulartransport).

<CodeGroup>
  ```typescript Web SDK (TypeScript) theme={null}
  import { toModularTransport } from "@circle-fin/modular-wallets-core";
  import { arcTestnet } from "viem/chains";

  const modularTransport = toModularTransport(
    `${clientUrl}/arcTestnet`,
    clientKey,
  );
  ```

  ```swift iOS SDK (Swift) theme={null}
          let modularTransport = toModularTransport(
              clientKey: CLIENT_KEY,
              url: "\(clientUrl)/arcTestnet"
          )
  ```

  ```kotlin Android SDK (Kotlin) theme={null}
          val modularTransport = toModularTransport(
              context,
              CLIENT_KEY,
              "$clientUrl/arcTestnet",
          )
  ```

  ```java Android SDK (Java) theme={null}
      Transport modularTransport = toModularTransport(
          context,
          CLIENT_KEY,
          clientUrl + "/arcTestnet"
      );
  ```
</CodeGroup>

## Step 4. Create the bundler client and smart account

The bundler client submits user operations through Circle's bundler. The smart
account is the user's MSCA, with the passkey credential as its signer. Its
address is deterministic, so you can read it before the account is deployed
onchain.

<CodeGroup>
  ```typescript Web SDK (TypeScript) theme={null}
  import { toCircleSmartAccount } from "@circle-fin/modular-wallets-core";
  import { createPublicClient } from "viem";
  import {
    createBundlerClient,
    toWebAuthnAccount,
  } from "viem/account-abstraction";
  import { arcTestnet } from "viem/chains";

  const client = createPublicClient({
    chain: arcTestnet,
    transport: modularTransport,
  });

  const smartAccount = await toCircleSmartAccount({
    client,
    owner: toWebAuthnAccount({ credential }),
  });

  const bundlerClient = createBundlerClient({
    account: smartAccount,
    chain: arcTestnet,
    transport: modularTransport,
  });

  console.log("Smart account address:", smartAccount.address);
  ```

  ```swift iOS SDK (Swift) theme={null}
          let bundlerClient = BundlerClient(
              chain: ArcTestnet,
              transport: modularTransport
          )

          let smartAccount = try await toCircleSmartAccount(
              client: bundlerClient,
              owner: webAuthnAccount
          )

          print("Smart account address: \(smartAccount.address)")
  ```

  ```kotlin Android SDK (Kotlin) theme={null}
          val bundlerClient = BundlerClient(
              ArcTestnet,
              modularTransport,
          )

          val smartAccount = toCircleSmartAccount(
              bundlerClient,
              webAuthnAccount,
          )

          println("Smart account address: ${smartAccount.address}")
  ```

  ```java Android SDK (Java) theme={null}
      BundlerClient bundlerClient = new BundlerClient(
          ArcTestnet.INSTANCE,
          modularTransport
      );

      CompletableFuture<CircleSmartAccount> smartAccountFuture = new CompletableFuture<>();
      toCircleSmartAccount(
          bundlerClient,
          webAuthnAccount,
          new CustomContinuation<>(smartAccountFuture)
      );
      CircleSmartAccount smartAccount = smartAccountFuture.join();

      System.out.println("Smart account address: " + smartAccount.getAddress());
  ```
</CodeGroup>

The user now has a modular wallet, signed by their passkey, ready to transact.

## Next steps

* [Set up passkey recovery](/wallets/modular/set-up-passkey-recovery) so the
  user can restore access if they lose their passkey. Strongly recommended
  before shipping to production.
* [Transfer tokens](/wallets/modular/transfer-tokens) from the modular wallet
  with gas sponsored by Circle's paymaster.
* [Sign and verify messages](/wallets/modular/sign-and-verify-messages) for
  Sign-In With Ethereum (SIWE) or EIP-712 typed data.
