# Circle Smart Account

## Install

:::info
This package & guide is maintained by [Circle](https://www.circle.com).
:::

Make sure to add [Circle's Modular Wallets](https://developers.circle.com/w3s/modular-wallets) SDK to your project:

```sh
npm install @circle-fin/modular-wallets-core
```

This package provides utilities for passkey authentication and smart account creation.

## Prerequisites

Before you start, make sure you have:

* Set up and retrieved your **Client Key and Client URL** [by following this step](https://developers.circle.com/w3s/modular-wallets-setup)
* Configured a domain for WebAuthn credential (Passkey) registration
* Familiarize yourself with [Circle's API Key and Client Key authentication](https://developers.circle.com/w3s/web3-services-api-client-keys-auth)

::::steps

## Create a Passkey Credential

First, register a new **Passkey** (WebAuthn credential) or use an existing one for your user.

The Circle SDK uses a **Passkey server** to handle WebAuthn registration and login.

We need to initialize a Passkey Transport with the Circle **Client Key** and **Client URL** (obtained from the Circle Modular Wallets console setup), then create a credential:

```ts
import { 
  toPasskeyTransport, 
  toWebAuthnCredential, 
  WebAuthnMode 
} from '@circle-fin/modular-wallets-core'

const clientKey = '...'
const clientUrl = 'https://...'

const passkeyTransport = toPasskeyTransport(clientUrl, clientKey)

const credential = await toWebAuthnCredential({
  transport: passkeyTransport,
  mode: WebAuthnMode.Register,
  username: 'user-example',
})
```

This will trigger the WebAuthn flow in the browser (e.g. biometric prompt) and yield a `credential` object for the passkey.

## Create Client

Next, create a Viem **Public Client** configured to use Circle's infrastructure.

Circle provides custom RPC endpoints (via the Client URL) for each supported network.

Use `toModularTransport` to get a Viem transport for your target chain, and then initialize the client.

For example, to connect to the Polygon Amoy testnet (a Circle test network):

```ts
import { toModularTransport } from '@circle-fin/modular-wallets-core'
import { createPublicClient } from 'viem'
import { polygonAmoy } from 'viem/chains'

const transport = toModularTransport(
  `${clientUrl}/polygonAmoy`,
  clientKey,
)

const client = createPublicClient({
  chain: polygonAmoy,
  transport,
})
```

:::note
When calling `toModularTransport`, specify a supported network path. Circle's Modular Wallet service supports major EVM chains (Ethereum, Polygon, Base, Optimism, Arbitrum, etc., including their testnets). For example, use `${clientUrl}/baseSepolia` for Base Sepolia, `${clientUrl}/optimism` for Optimism, and so on.
:::

## Create a Circle Smart Account

With the client ready and a passkey credential available, you can create the **Circle Smart Account**. This smart account is a contract wallet (an ERC-4337 & ERC-6900 compatible smart contract) controlled by the passkey. We'll transform the WebAuthn credential into a Viem account object and then generate the smart account:

```ts
import { toCircleSmartAccount } from '@circle-fin/modular-wallets-core'
import { toWebAuthnAccount } from 'viem/account-abstraction'

const owner = toWebAuthnAccount({
  credential,
})

const account = await toCircleSmartAccount({
  client,
  owner,
})
```

## Set up a Bundler Client

To send transactions from a smart account, we use an ERC-4337 **Bundler**. Viem provides a Bundler client that interfaces with bundler RPC endpoints. Using Circle's Modular transport, we can create a Bundler client that will forward User Operations to Circle's bundler.

```ts
import { createBundlerClient } from 'viem/account-abstraction'
import { polygonAmoy } from 'viem/chains'

const bundlerClient = createBundlerClient({
  account,
  chain: polygonAmoy,
  transport,
})
```

## Send a Gasless USDC Transaction

Finally, let's send a token transfer from the smart account **without requiring the user to pay gas**. In this example, we'll transfer **1 USDC** on the test network to an arbitrary address. Circle's bundler and paymaster will sponsor the gas fees as long as a Gas Station policy is in place (on testnet, a default policy is usually already configured).

We will use the Circle SDK's helper to encode an ERC-20 transfer call and then send a User Operation via the bundler client:

```ts
import { encodeTransfer } from '@circle-fin/modular-wallets-core'
import { parseUnits } from 'viem'

const usdcAddress = '0x41e94eb019c0762f9bfcf9fb1e58725bfb0e7582'

const hash = await bundlerClient.sendUserOperation({
  calls: [
    encodeTransfer(
      '0x{recipient}', 
      usdcAddress, 
      parseUnits('1', 6)
    )
  ],
  paymaster: true,
})
```

The bundler returns a User Operation `hash` for the submitted operation. You can then wait for the transaction to be included:

```ts
const { receipt } = await bundlerClient.waitForUserOperationReceipt({ hash: userOpHash })
```

::::

That's it!

You have created a Circle Smart Account with a passkey and sent a gasless USDC transfer using Viem.

The Circle Modular Wallets SDK seamlessly handles the account abstraction flow – from passkey-based signing to sponsored gas payments – allowing you to build a user-friendly dApp without exposing the complexity of gas fees to your users.

## Example Apps

The Circle team has prepared working demos to help you get started quickly with Modular Wallets:

* [Modular Wallet + Passkey (Replit)](https://replit.com/@buildoncircle/modular-wallet-passkey) – A simple web demo showcasing passkey login and gasless USDC transfer.
* [Modular Wallets Web SDK Example (GitHub)](https://github.com/circlefin/modularwallets-web-sdk/tree/master/examples/circle-smart-account) – A full React implementation using Circle Smart Accounts, bundler, and paymaster.

Use these as references when building or testing your own integration.
