Arc House
Tooling + Technical
July 7, 2026

App Kit FAQs

App Kit FAQs
# App Kits
# Unified Balance Kit
# Bridge Kit
# Swap Kit

Implementation answers for building with App Kit

App Kit FAQs

Setup and wallets

Does App Kit work in a server app, a client (browser) app, or both?

Both.
  • Send, Bridge, and Unified Balance run in a server backend or a browser client app.
  • Swap runs server-side only today; client-side Swap is in progress.
Pick your adapter to match: a browser-wallet adapter for client apps, or a private key, Circle Wallets, or Turnkey adapter for servers.

How do I integrate a client-side (browser) wallet, like MetaMask or Trust Wallet?

Create the adapter from the wallet’s injected provider. Any EIP-1193 EVM browser wallet (MetaMask, Trust Wallet), or a Solana wallet such as Phantom, works:
import { createViemAdapterFromProvider } from "@circle-fin/adapter-viem-v2";
const adapter = await createViemAdapterFromProvider({
  provider: window.ethereum,
});
  • Ethers: createEthersAdapterFromProvider.
  • Solana: createSolanaKitAdapterFromProvider with window.solana.
Browser wallets work for Send, Bridge, and Unified Balance; Swap is server-side only for now. See the browser wallet setup.

How do I integrate a server-side wallet, like Turnkey or Circle Wallets?

Build an adapter from your wallet’s credentials and pass it to the kit. App Kit supports these server-side options:
  • Private key: createViemAdapterFromPrivateKey (and the Ethers and Solana equivalents).
  • Circle Wallets: createCircleWalletsAdapter, using a Circle API Key and Entity Secret.
  • Turnkey: a Turnkey company wallet passed into the Viem adapter through @turnkey/viem.
Each is configured the same way you’d build any adapter, then passed to the kit. See the server-side wallet setup.

What credentials does the Circle Wallets adapter need, and what changes for a smart-contract account?

  • It needs a Circle API Key and Entity Secret, and is server-side only (never expose these in a browser).
  • It currently supports Circle developer-controlled wallets only. User-controlled wallets and modular wallets support is in progress.
  • For a smart-contract account (SCA): set allowanceStrategy: "approve" for swaps and Unified Balance deposits, and use a delegate for Unified Balance spends, because an SCA can’t sign its own spends.

Does App Kit only support Arc, or other blockchains as well?

Many blockchains, not only Arc. App Kit supports a wide set of EVM blockchains (Arbitrum, Avalanche, Base, Ethereum, OP Mainnet, Polygon, Linea, and more), plus Solana and Arc, across mainnet and testnet.
Coverage differs by capability: Send and Bridge support the most blockchains, while Swap and Unified Balance support a subset. Check the matrices in supported blockchains and tokens.

Should I configure a custom RPC for production, and how?

Yes. The default public RPC endpoints are rate-limited, so for production pass your own endpoint (such as Alchemy or QuickNode) by mapping each blockchain to its URL in the adapter callback: getPublicClient (Viem), getProvider (Ethers), or getRpc (Solana). See the custom RPC setup.

Amounts and fees

How does Circle charge the service fee for App Kit?

App Kit is generally free to use. Any cost comes from the underlying provider or from optional value-add services, and varies by capability:
  • Send: free; you pay only network gas.
  • Bridge: free, with optional paid value-add services (such as Fast Transfer and the Forwarding Service).
  • Swap: pays a basic service-provider fee (the third-party liquidity provider that executes the swap).
  • Unified Balance: covers the gas cost of each spend, with optional paid value-add services (such as the Forwarding Service).
Separately, if you add a custom fee to monetize a capability, Circle takes 10% and you keep 90%.
See the per-capability fee details: swap fees, bridge fees, and Unified Balance fees.

How do I collect my own fee, and how does it differ by each capability?

Set a fee per call, or set a global policy. The per-call shape differs by capability:
// Swap: percentage-based
config: { customFee: { percentageBps: 50, recipientAddress: "0xYourWallet" } }
ďťż
// Bridge and Unified Balance spend: absolute value
config: { customFee: { value: "0.10", recipientAddress: "0xYourWallet" } }
For a global fee:
  • Swap and Bridge: developerFee hooks in the AppKit constructor.
  • Unified Balance spend: kit.unifiedBalance.setCustomFeePolicy(...).
Circle takes 10% of any custom fee; you keep 90%. See collect a swap fee, collect a bridge fee, and collect custom spend fees.

Swap

Why do I need a Kit Key for Swap, and how should I manage it?

Swap requires a Kit Key; Send, Bridge, and Unified Balance don’t. It’s a free credential from the Circle Console, used mainly for authorization and security.
Manage it as a secret: store it in an environment variable, never hardcode or ship it to the browser (another reason Swap runs server-side), and pass it as config.kitKey per call:
const result = await kit.swap({
  from: { adapter, chain: "Arc" },
  tokenIn: "USDT",
  tokenOut: "USDC",
  amountIn: "1.00",
  config: { kitKey: process.env.KIT_KEY },
});

Can I run Swap from a browser or client-side app?

Not yet. Swap requires a server-side adapter (a private key, Circle Wallets, or Turnkey). Client-side Swap is a known limitation that the team is actively working on.
Send, Bridge, and Unified Balance already work with browser wallets such as MetaMask today. See adapter setups.

How do I show users the exact output and minimum before executing?

Call estimateSwap with the same parameters as swap; it runs no transaction and returns:
  • estimatedOutput: the expected amount.
  • stopLimit: the guaranteed minimum after slippage.
  • fees: the fee breakdown.
Both amounts are TokenAmount objects with .amount and .token.
const estimate = await kit.estimateSwap({
  from: { adapter, chain: "Arc" },
  tokenIn: "EURC",
  tokenOut: "USDC",
  amountIn: "1.00",
  config: { kitKey: process.env.KIT_KEY },
});
console.log(estimate.estimatedOutput.amount, estimate.stopLimit.amount);
Control the floor with slippageBps (default 300, or 3%) or an explicit stopLimit, which overrides slippage. See estimate the swap rate and set slippage tolerance or stop limit.

What can I actually swap on testnet versus mainnet?

  • Arc Testnet: USDC, EURC, and cirBTC only, because testnet liquidity is limited.
  • Mainnet: stablecoins (USDC, EURC, USDT, USDe, DAI, PYUSD), wrapped tokens (WBTC, WETH, WSOL), and native tokens, plus any other token by contract address.
Swap also supports fewer blockchains than Bridge. Confirm with kit.getSupportedChains('swap') or the supported tokens reference.

Why did my swap revert? It happens often on testnet.

On testnet, the usual cause is thin liquidity: the swap route can’t fill at your price, so the transaction reverts. To reduce reverts:
  • Stay within the supported testnet pairs (Arc Testnet: USDC, EURC, cirBTC).
  • Use small amounts, and raise slippageBps (avoid 0) or set a realistic stopLimit.
  • Run estimateSwap first to confirm a route exists and see the expected output.
  • If a testnet pool is temporarily depleted, try again after a while. Circle’s liquidity provider monitors the testnet pools and replenishes tokens.
Other common causes (any network): no route or token pair, insufficient allowance (let the default permit run, or use approve), and insufficient gas. See set slippage tolerance or stop limit.

What differs between a crosschain swap and a same-chain swap?

Both are a single kit.swap() call. The difference is the optional to field:
  • Same-chain: omit to (output goes to the source wallet), or set to.recipientAddress to send it to another wallet on the same blockchain.
  • Crosschain: set to.chain to the destination blockchain. recipientAddress is required for crosschain, because the destination wallet may use a different chain or address format.
// Cross-chain: swap USDC on Ethereum, receive on Base at a specific wallet
const result = await kit.swap({
  from: { adapter, chain: "Ethereum" },
  tokenIn: "USDC",
  tokenOut: "USDC",
  amountIn: "100",
  to: { chain: "Base", recipientAddress: "0xRecipient" },
  config: { kitKey: process.env.KIT_KEY },
});
A crosschain swap settles asynchronously, so check its status with waitForSwap. It polls until the status is terminal (DONE, FAILED, or NOT_FOUND). For a same-chain swap it returns immediately, because the swap is already terminal when swap resolves.
const final = await kit.waitForSwap({ result, kitKey: process.env.KIT_KEY });
Also note:
  • Custom fees are collected from the source-chain input token (same-chain output fees are taken from the estimated output).
  • Both the source and destination must be swap-supported blockchains.

Can I do a same-chain swap and send the output to a different wallet?

Yes. Set to.recipientAddress; the destination chain defaults to the source blockchain when you omit to.chain. Omit to entirely and the swapped tokens go back to the source wallet.
const result = await kit.swap({
  from: { adapter, chain: "Arc" },
  tokenIn: "USDT",
  tokenOut: "USDC",
  amountIn: "1.00",
  to: { recipientAddress: "0xDifferentWallet" },
  config: { kitKey: process.env.KIT_KEY },
});
See Swap.

Bridge

How do I cap or avoid the bridge fee, and who pays it?

Bridge uses the Cross-Chain Transfer Protocol (CCTP):
  • FAST (default): carries a CCTP fee that varies by source blockchain, taken from the USDC amount.
  • SLOW (standard): free, but takes longer to finalize.
  • maxFee: caps the fee on a FAST transfer; if the quote exceeds it, the transfer falls back to SLOW.
There is no separate gas token for the fee itself. See configure transfer speed and max cost, estimate bridging costs, and bridge fees.

My Arc Testnet bridge reverts with ‘Max fee must be less than amount’. Why?

A FAST bridge takes its fee from the transfer, so the amount must exceed the CCTP fast-transfer fee; below that, the burn step reverts with this error.
It isn’t strictly testnet-only, but in practice it mainly surfaces on Arc Testnet, where the fee is relatively high (around 1.4 USDC). On mainnet the fast-transfer fee is small, so normal amounts clear it.
Fix: send a larger amount, or use SLOW transfer speed, which skips the fast fee. See configure transfer speed and max cost.

How do I track a bridge’s progress?

Listen for the lifecycle events, which App Kit namespaces with bridge.:
kit.on("bridge.approve", (e) => console.log("Approved", e.values.txHash));
kit.on("bridge.burn", (e) => console.log("Burned", e.values.txHash));
kit.on("bridge.attestation", () => console.log("Attestation received"));
kit.on("bridge.mint", (e) => console.log("Minted", e.values.txHash));
kit.on("*", (e) => console.log(e.method, e.values)); // all events
You can also inspect the returned result.steps array; each step has name, state, txHash, and explorerUrl. Remove a handler with kit.off(action, handler) using the same function reference.
The standalone Bridge Kit emits the same events without the bridge. prefix (for example, approve). See the SDK reference.

My bridge failed midway. Do I lose my funds, and how do I resume?

No, bridges are recoverable.
  • Inspect result.steps to find the failed step (each has state, errorMessage, and error).
  • Call retryBridge(result, retryContext) to resume from that step.
  • Use the isRetryableError helper to decide whether to retry.

Unified Balance

What kind of wallets does Unified Balance work with, and how do I implement it?

Any adapter backed by an EOA (externally owned account) works, including browser wallets like MetaMask, a private key, Circle Wallets, and Turnkey. It is not limited to Circle Developer-Controlled Wallets.
Unified Balance requires an EOA to sign. If your main wallet is a smart-contract account (SCA), use the delegate model: delegate to an EOA, which signs spends on the SCA’s behalf. Direct support for SCA and multi-sig wallets is in progress.
To implement it, use the same adapter you’d use for Send or Bridge, then:
  1. deposit USDC into the Unified Balance from one or more blockchains.
  1. spend from the balance on any supported blockchain.
For backends or smart-contract accounts, deposit on a user’s behalf with depositFor, and register a delegate to spend without per-spend signing. See the Unified Balance overview, the deposit and spend quickstart, and manage delegates.

How soon after a deposit can I spend, and what determines the wait?

Two stages, each bound by blockchain finality:
  • Deposit: reflected in the Unified Balance after the source blockchain finalizes, so depositing from a fast blockchain (such as Base or Solana) reflects sooner than from Ethereum.
  • Spend: once reflected, you can spend on any supported blockchain immediately; the recipient’s receive time depends on the destination blockchain.

How is gas paid for the spend transaction?

Gas is deducted from your Unified Balance USDC; you don’t need a separate gas token on the source blockchain. The amount varies by blockchain and network conditions. For the per-blockchain gas costs, see Gateway fees.

How do I avoid making users sign for every deposit and spend?

  • Deposit funds ahead of time, optionally with depositFor (deposit on the user’s behalf), so the user only signs the spend.
  • To remove signing from the spend too, register a delegate and spend on the user’s behalf.

My spend fails right after addDelegate. Why?

Delegate status is finality-aware. After addDelegate, the status is pending until the grant finalizes, and spends fail until it becomes ready.
Poll getDelegateStatus and wait for ready before spending. See manage delegates.

How do users withdraw funds without spending them, and why the delay?

Use the two-step trustless withdrawal: initiateRemoveFund, then removeFund after the activation period (about 7 days on EVM blockchains, immediate on Solana).
This is an escape hatch; for normal payouts, use spend. See remove funds trustlessly.

Can I combine a Unified Balance spend with another capability, such as spend and swap, in one call?

Not in a single call. Each capability is its own method, so compose them as separate steps: for example, spend from the Unified Balance to deliver USDC, then swap that USDC into another token on the destination blockchain. Sequence the calls and check each result before the next. See the Unified Balance overview and Swap.
Comments (92)
Popular
avatar
ďťż
Dive in

Related

External Content
Unified Balance Kit: Rethinking Payment and Treasury App Architectures
Jun 18th, 2026 • Views 21.7K
Video
App Kits Developer Office Hours: Bridge, Swap, Send, and Monetization
By Elton Tay • Apr 21st, 2026 • Views 137.4K
Video
Event Replay: Arcshop- Introducing Bridge Kit
By Tobias Golbs • Dec 10th, 2025 • Views 137.4K
Video
ArcShop with Elton: Unified Balance Kit for Crosschain USDC Flows
By Elton Tay • May 1st, 2026 • Views 134.5K
External Content
Unified Balance Kit: Rethinking Payment and Treasury App Architectures
Jun 18th, 2026 • Views 21.7K
Video
Event Replay: Arcshop- Introducing Bridge Kit
By Tobias Golbs • Dec 10th, 2025 • Views 137.4K
Video
ArcShop with Elton: Unified Balance Kit for Crosschain USDC Flows
By Elton Tay • May 1st, 2026 • Views 134.5K
Video
App Kits Developer Office Hours: Bridge, Swap, Send, and Monetization
By Elton Tay • Apr 21st, 2026 • Views 137.4K
Terms of Service
Your Privacy Choices