Skip to main content
If your app uses Dynamic for user authentication (email, social login, wallet connection) instead of passkeys, use the Dynamic-managed EOA as the signer for a modular wallet. The result is a modular wallet whose smart account is signed by the user’s Dynamic EOA. After setup, every transfer, signature, and batched operation is signed by Dynamic automatically. To use a passkey signer instead, see Create a modular wallet.

Before you begin

Before you begin, ensure that you’ve:
  • Created a Circle Console account and a client key for the modular wallets SDK: Console → Keys → Create a key → Client Key.
  • Created a Dynamic Dashboard account and obtained your Environment ID.
  • Enabled email login and embedded wallets (WaaS) in your Dynamic environment, and added Arc Testnet as a supported network. The examples below target Arc Testnet.
  • Installed Node.js 22+ and the modular wallets Web SDK.

Steps

1

Configure environment variables

Make your Circle client key, modular wallets backend URL, and Dynamic environment ID available to your app. The backend URL is the same for every app: https://modular-sdk.circle.com/v1/rpc/w3s/buidl.
Web SDK (TypeScript)
# .env
VITE_CLIENT_KEY=YOUR_CLIENT_KEY
VITE_CLIENT_URL=https://modular-sdk.circle.com/v1/rpc/w3s/buidl
VITE_DYNAMIC_ENV_ID=YOUR_DYNAMIC_ENVIRONMENT_ID
2

Install the Dynamic headless SDK packages

Dynamic’s headless SDK (@dynamic-labs-sdk/client) works with any web framework. Install it alongside the Circle modular wallets SDK and viem.
npm install @circle-fin/modular-wallets-core @dynamic-labs-sdk/client @dynamic-labs-sdk/evm viem
3

Initialize the Dynamic client

Create a Dynamic client, register the EVM extension, and wait for initialization before reading wallet accounts. If Dynamic’s network data for Arc Testnet omits RPC URLs, supply them in a networkData transformer.
Web SDK (TypeScript)
import {
  createDynamicClient,
  initializeClient,
  waitForClientInitialized,
} from "@dynamic-labs-sdk/client";
import { addEvmExtension } from "@dynamic-labs-sdk/evm";
import { arcTestnet } from "viem/chains";

const dynamicClient = createDynamicClient({
  autoInitialize: false,
  environmentId: dynamicEnvId,
  metadata: {
    name: "My App",
    universalLink: window.location.origin,
  },
  transformers: {
    networkData: (network) => {
      if (network.networkId !== String(arcTestnet.id)) return network;

      const rpcUrls = network.rpcUrls.http.filter((url): url is string =>
        Boolean(url),
      );
      if (rpcUrls.length > 0) return network;

      return {
        ...network,
        rpcUrls: { http: [...arcTestnet.rpcUrls.default.http] },
      };
    },
  },
});

addEvmExtension();
await initializeClient();
await waitForClientInitialized(dynamicClient);
4

Authenticate the user

Sign the user in with any Dynamic-supported auth flow. The snippet below uses email OTP; social login and external wallet connection work the same way once Dynamic reports an authenticated session.After authentication, create an embedded EVM wallet on your target chain if the user does not have one yet.
Web SDK (TypeScript)
import {
  getWalletAccounts,
  sendEmailOTP,
  verifyOTP,
} from "@dynamic-labs-sdk/client";
import {
  createWaasWalletAccounts,
  getChainsMissingWaasWalletAccounts,
} from "@dynamic-labs-sdk/client/waas";
import { isEvmWalletAccount } from "@dynamic-labs-sdk/evm";

const otpVerification = await sendEmailOTP({ email });
await verifyOTP({ otpVerification, verificationToken });

const walletAccounts = await getWalletAccounts();
if (!walletAccounts.some(isEvmWalletAccount)) {
  const missingChains = getChainsMissingWaasWalletAccounts();
  if (missingChains.length > 0) {
    await createWaasWalletAccounts({ chains: missingChains });
  }
}
5

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.
Web SDK (TypeScript)
import { toModularTransport } from "@circle-fin/modular-wallets-core";

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

Create the bundler client and smart account

Switch the Dynamic wallet to your target network, convert it to a viem local account, and pass it as the owner to toCircleSmartAccount. The bundler client submits user operations through the MSCA; every operation is signed by the Dynamic-managed EOA automatically.
Web SDK (TypeScript)
import {
  addNetwork,
  getWalletAccounts,
  NetworkNotAddedError,
  switchActiveNetwork,
} from "@dynamic-labs-sdk/client";
import { isEvmWalletAccount } from "@dynamic-labs-sdk/evm";
import { createWalletClientForWalletAccount } from "@dynamic-labs-sdk/evm/viem";
import {
  toCircleSmartAccount,
  walletClientToLocalAccount,
} from "@circle-fin/modular-wallets-core";
import { createPublicClient } from "viem";
import { createBundlerClient } from "viem/account-abstraction";
import { arcTestnet } from "viem/chains";

const evmAccount = (await getWalletAccounts()).find(isEvmWalletAccount);
if (!evmAccount) {
  throw new Error(
    "No EVM wallet account found. Enable email login and embedded wallets in Dynamic.",
  );
}

try {
  await switchActiveNetwork({
    walletAccount: evmAccount,
    networkId: String(arcTestnet.id),
  });
} catch (error) {
  if (error instanceof NetworkNotAddedError) {
    await addNetwork({
      walletAccount: evmAccount,
      networkData: error.networkData,
    });
    await switchActiveNetwork({
      walletAccount: evmAccount,
      networkId: String(arcTestnet.id),
    });
  } else {
    throw error;
  }
}

const walletClient = await createWalletClientForWalletAccount({
  walletAccount: evmAccount,
});

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

const smartAccount = await toCircleSmartAccount({
  client,
  owner: walletClientToLocalAccount(walletClient),
});

const bundlerClient = createBundlerClient({
  account: smartAccount,
  chain: arcTestnet,
  transport: modularTransport,
});
The user now has a modular wallet, signed by their Dynamic-managed EOA.

Next steps