> ## 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.

# How-to: Use Dynamic as a signer

> Use a Dynamic-managed EOA as the signer for a modular wallet, instead of a passkey.

If your app uses [Dynamic](https://www.dynamic.xyz/) 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](/wallets/modular/transfer-tokens),
[signature](/wallets/modular/sign-and-verify-messages), and
[batched operation](/wallets/modular/batch-and-parallel-user-operations) is
signed by Dynamic automatically.

To use a passkey signer instead, see
[Create a modular wallet](/wallets/modular/create-a-modular-wallet).

## Before you begin

Before you begin, ensure that you've:

* Created a [Circle Console](https://console.circle.com/) account and a
  [client key](/api-reference/keys#client-keys) for the modular wallets SDK:
  **Console → Keys → Create a key → Client Key**.
* Created a [Dynamic Dashboard](https://app.dynamic.xyz/) account and obtained
  your **Environment ID**.
* Enabled email login and embedded wallets (WaaS) in your Dynamic environment,
  and added [Arc Testnet](/wallets/supported-blockchains) as a supported
  network. The examples below target Arc Testnet.
* Installed [Node.js 22+](https://nodejs.org/) and the modular wallets
  [Web SDK](https://www.npmjs.com/package/@circle-fin/modular-wallets-core).

## Steps

<Steps>
  <Step title="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`.

    ```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
    VITE_DYNAMIC_ENV_ID=YOUR_DYNAMIC_ENVIRONMENT_ID
    ```
  </Step>

  <Step title="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`.

    ```shell theme={null}
    npm install @circle-fin/modular-wallets-core @dynamic-labs-sdk/client @dynamic-labs-sdk/evm viem
    ```
  </Step>

  <Step title="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.

    ```typescript Web SDK (TypeScript) theme={null}
    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);
    ```
  </Step>

  <Step title="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.

    ```typescript Web SDK (TypeScript) theme={null}
    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 });
      }
    }
    ```
  </Step>

  <Step title="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.

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

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

  <Step title="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.

    ```typescript Web SDK (TypeScript) theme={null}
    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,
    });
    ```
  </Step>
</Steps>

The user now has a modular wallet, signed by their Dynamic-managed EOA.

## Next steps

* [Transfer tokens](/wallets/modular/transfer-tokens) from the wallet.
* [Sign and verify messages](/wallets/modular/sign-and-verify-messages) for
  Sign-In With Ethereum (SIWE) or EIP-712 typed data.
* [Batch and parallel user operations](/wallets/modular/batch-and-parallel-user-operations)
  for multi-call patterns.
