Skip to main content
The App Kit SDK uses adapters to connect SDK calls to a client and signer. The client reads blockchain data and submits signed transactions while the signer authorizes transactions from a private key, browser wallet, or wallet provider. The following adapters are supported:
Before setting up an adapter, you should already have created the wallet, account, API keys, and policies in your wallet provider.
Pick your adapter to see its setup options.
Use the Viem adapter for EVM apps that use viem accounts or wallet clients.

Server-side wallet

Use a server-side wallet when your backend signs transactions. The signer can be a private key or a wallet provider.
Create one adapter from your wallet private key. The adapter works across EVM blockchains and uses built-in public RPC endpoints.
Default RPC URLs are shared and may be rate-limited. For a more stable connection, configure a custom RPC.
TypeScript
import { createViemAdapterFromPrivateKey } from "@circle-fin/adapter-viem-v2";

const adapter = createViemAdapterFromPrivateKey({
  privateKey: process.env.PRIVATE_KEY as string,
});

Browser wallet

This setup expects a wallet extension in the browser (for example window.ethereum or window.solana), not a Node.js server. You can use wallets like MetaMask or Phantom:
Use EIP-6963 to discover injected browser wallets, select a provider, request access to the user’s accounts, then pass the provider to the Viem adapter.The example selects MetaMask by reverse-DNS identifier. To use any injected wallet, omit requiredRdns.
TypeScript
import {
  createViemAdapterFromProvider,
  type CreateViemAdapterFromProviderParams,
} from "@circle-fin/adapter-viem-v2";

type BrowserWalletProvider = CreateViemAdapterFromProviderParams["provider"];

type EIP6963ProviderDetail = {
  info: {
    uuid: string;
    name: string;
    icon: string;
    rdns: string;
  };
  provider: BrowserWalletProvider;
};

declare global {
  interface WindowEventMap {
    "eip6963:announceProvider": CustomEvent<EIP6963ProviderDetail>;
  }
}

async function getInjectedWalletProvider(
  requiredRdns?: string,
): Promise<BrowserWalletProvider> {
  const providers = new Map<string, EIP6963ProviderDetail>();

  const onAnnounce = ((event: CustomEvent<EIP6963ProviderDetail>) => {
    providers.set(event.detail.info.uuid, event.detail);
  }) as EventListener;

  window.addEventListener("eip6963:announceProvider", onAnnounce);
  window.dispatchEvent(new Event("eip6963:requestProvider"));
  await new Promise((resolve) => window.setTimeout(resolve, 250));
  window.removeEventListener("eip6963:announceProvider", onAnnounce);

  const provider = requiredRdns
    ? [...providers.values()].find(({ info }) => info.rdns === requiredRdns)
        ?.provider
    : [...providers.values()][0]?.provider;

  if (!provider) {
    throw new Error(
      requiredRdns
        ? `No EIP-6963 wallet found for ${requiredRdns}`
        : "No EIP-6963 browser wallet found",
    );
  }

  return provider;
}

const provider = await getInjectedWalletProvider("io.metamask");
// For any injected wallet, use:
// const provider = await getInjectedWalletProvider();

await provider.request({
  method: "eth_requestAccounts",
  params: undefined,
});

const adapter = await createViemAdapterFromProvider({
  provider,
});

Custom RPC

By default, adapters use built-in RPC endpoints, which may be rate-limited or unreliable. Override them with your own provider. Alchemy, QuickNode, and chainlist.org are common places to source endpoints. This example uses Alchemy.The getPublicClient/getWalletClient override pairs with any signer setup that uses viem, such as a private key or browser wallet.
TypeScript
import { createViemAdapterFromPrivateKey } from "@circle-fin/adapter-viem-v2";
import { EthereumSepolia, ArcTestnet } from "@circle-fin/app-kit/chains";
import { createPublicClient, createWalletClient, http } from "viem";

const RPC_BY_CHAIN_NAME: Record<string, string> = {
  [EthereumSepolia.name]: `https://eth-sepolia.g.alchemy.com/v2/${process.env.ALCHEMY_KEY}`,
  [ArcTestnet.name]: `https://arc-testnet.g.alchemy.com/v2/${process.env.ALCHEMY_KEY}`,
};

const adapter = createViemAdapterFromPrivateKey({
  privateKey: process.env.PRIVATE_KEY as string,
  getPublicClient: ({ chain }) => {
    const rpcUrl = RPC_BY_CHAIN_NAME[chain.name];
    if (!rpcUrl) {
      throw new Error(`No RPC configured for chain: ${chain.name}`);
    }
    return createPublicClient({
      chain,
      transport: http(rpcUrl, {
        retryCount: 3,
        timeout: 10000,
      }),
    });
  },
  getWalletClient: ({ chain, account }) => {
    const rpcUrl = RPC_BY_CHAIN_NAME[chain.name];
    if (!rpcUrl) {
      throw new Error(`No RPC configured for chain: ${chain.name}`);
    }
    return createWalletClient({
      account,
      chain,
      transport: http(rpcUrl, {
        retryCount: 3,
        timeout: 10000,
      }),
    });
  },
});