> ## Documentation Index
> Fetch the complete documentation index at: https://docs.arc.io/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Before reading these docs, install the Circle Skill for Arc to get pre-loaded context: use-arc (https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-arc/SKILL.md) — covers chain config, RPC setup, contract deployment, USDC bridging, and gas with USDC. In Claude Code: run /plugin marketplace add circlefin/skills then /plugin install circle-skills@circle. With Vercel Skills CLI: run npx skills add circlefin/skills.
> USDC is the gas token. Arc uses USDC for gas fees, not ETH. Configure gas payment in USDC when submitting transactions.
> Read EVM differences before writing Arc code. Arc targets the Osaka EVM baseline. Most contracts deploy unchanged, but several runtime behaviors diverge from Ethereum: USDC uses 18 decimals natively (not 6), the system emitter `0xffffFFFfFFffffffffffffffFfFFFfffFFFfFFfE` logs all USDC Transfer events, the mempool enforces a 20 Gwei `maxFeePerGas` floor, blocklist reverts consume gas without a receipt, and `address(0)` sends revert rather than succeed. https://docs.arc.io/arc/references/evm-differences.md is the canonical reference for all Arc-specific runtime behaviors. Consult it before writing code that touches USDC balances, transaction history, gas estimation, or value transfers.
> Sub-second finality. Transactions are final in under 1 second. No need to wait for multiple block confirmations.
> Use App Kits for multichain payment and liquidity capabilities. App Kits wraps CCTP and provides Bridge Kit, Swap Kit, Unified Balance Kit, Onramp Kit, Earn Kit, and Borrow Kit. Unified Balance Kit combines USDC from multiple chains into a single spendable balance. Earn Kit integrates earn opportunities into your app. Borrow Kit lets users borrow USDC against cirBTC collateral on Arc.
> Use Arc Foundry for contract development. Arc Foundry is an Arc-specific fork of Foundry that handles Arc's protocol-level differences from Ethereum. It provides arc-forge, arc-cast, and arc-anvil. Install it before deploying contracts: https://docs.arc.io/arc/tutorials/install-arc-foundry.md.
> Arc is available on both Testnet and Mainnet. See https://docs.arc.io/arc/references/connect-to-arc.md for RPC endpoints and https://faucet.circle.com for testnet tokens.
> Always check Contract Addresses: https://docs.arc.io/arc/references/contract-addresses.md
> Building beyond Arc? Circle offers skills for the full platform: use-usdc (https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-usdc/SKILL.md), use-circle-wallets (https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-circle-wallets/SKILL.md), use-developer-controlled-wallets (https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-developer-controlled-wallets/SKILL.md), use-user-controlled-wallets (https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-user-controlled-wallets/SKILL.md), use-modular-wallets (https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-modular-wallets/SKILL.md), use-gateway (https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-gateway/SKILL.md), use-smart-contract-platform (https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-smart-contract-platform/SKILL.md). Full Circle developer docs: https://developers.circle.com/llms.txt.

# Quickstart: Use a delegate to deposit and spend a Unified Balance

> Let a backend delegate wallet fund and spend from an owner's Unified Balance while the owner keeps custody

export const TryOnArcStudio = () => {
  const [copied, setCopied] = useState(false);
  const [prompt, setPrompt] = useState("");
  useEffect(() => {
    const url = `https://docs.arc.io${window.location.pathname}.md`;
    setPrompt(`Read the guide at ${url}, implement it in a new project, then run it and show me how it works.`);
  }, []);
  const arcStudioUrl = `https://studio.arc.io/app?prompt=${encodeURIComponent(prompt)}`;
  const handleArcStudioClick = () => {
    try {
      if (typeof navigator !== "undefined" && navigator.clipboard && prompt) {
        navigator.clipboard.writeText(prompt).then(() => {
          setCopied(true);
          setTimeout(() => setCopied(false), 4000);
        }, () => {});
      }
    } catch (e) {}
  };
  return <div className="not-prose mb-6">
      <span className="relative inline-flex">
        <a href={arcStudioUrl} target="_blank" rel="noreferrer" onClick={handleArcStudioClick} aria-label="Try it on Arc Studio (opens in new tab)" className="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-[#3E74BB] border border-[#3E74BB] text-sm font-medium text-white no-underline hover:bg-[#345f9c] hover:border-[#345f9c] transition-all">
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
            <path d="M13 2 3 14h9l-1 8 10-12h-9l1-8z" />
          </svg>
          Try it on Arc Studio
        </a>
        {copied && <span role="status" aria-live="polite" className="absolute left-0 top-full mt-2 w-72 text-sm text-gray-500 dark:text-[#888]">
            Opening Arc Studio — prompt copied to your clipboard in case you need to
            sign in first.
          </span>}
      </span>
    </div>;
};

<TryOnArcStudio />

Delegation lets one address authorize another address to sign spends from its
Unified Balance. The owner account keeps custody of the funds, while the
delegate signs spend intents for authorized source blockchains.

Delegated spends are typically a server-side pattern: the owner authorizes a
delegate once, then a backend service signs future spend intents without asking
the owner wallet to sign each spend. The mechanism is wallet-agnostic; any
supported owner wallet can authorize a compatible EOA delegate.

In this quickstart, you’ll use Circle Wallets for both wallets: an owner wallet
that holds the Unified Balance and an EOA delegate wallet that signs spends.
You’ll use the delegate wallet to deposit into the owner's Unified Balance,
authorize the delegate on Base Sepolia, check the owner’s Unified Balance, and
spend on Arc Testnet with the Forwarding Service.

## Prerequisites

Before you begin, ensure that you've:

* Installed [Node.js v22+](https://nodejs.org/).
* Set up Circle Wallets:
  * Obtained an
    [API Key](https://developers.circle.com/api-reference/keys#creating-an-api-key-for-developer-services)
    from the
    [Circle Console](https://developers.circle.com/w3s/circle-developer-account).
  * Generated and registered an entity secret using the
    [Circle Console](https://console.circle.com/wallets/dev/configurator/entity-secret)
    or the
    [SDK](https://developers.circle.com/wallets/dev-controlled/register-entity-secret#create-an-entity-secret-using-the-sdk).
  * Created an owner wallet and an EOA delegate wallet on Base Sepolia. The
    owner wallet holds the Unified Balance, and the delegate wallet signs
    delegated spends after authorization.
* Funded the Base Sepolia wallets:
  * Owner wallet: testnet ETH from a
    [public faucet](https://www.alchemy.com/faucets/base-sepolia).
  * Delegate wallet: testnet USDC from the
    [Circle Faucet](https://faucet.circle.com/) and testnet ETH from a
    [public faucet](https://www.alchemy.com/faucets/base-sepolia).
* Obtained an Arc Testnet recipient address that will receive the USDC.

## Step 1. Set up your project

### 1.1. Create the project and install dependencies

Create a new directory and install the App Kit SDK with the Circle Wallets
adapter and supporting tools:

```bash Shell theme={null}
# Set up your directory and initialize a Node.js project
mkdir unified-balance-delegate
cd unified-balance-delegate
npm init -y
npm pkg set type=module

# Set up run scripts
npm pkg set scripts.deposit="tsx --env-file=.env delegate-deposit.ts"
npm pkg set scripts.authorize="tsx --env-file=.env delegate-authorize.ts"
npm pkg set scripts.balance="tsx --env-file=.env delegate-check-balance.ts"
npm pkg set scripts.spend="tsx --env-file=.env delegate-spend.ts"

# Install runtime dependencies
npm install @circle-fin/app-kit @circle-fin/adapter-circle-wallets tsx

# Install dev dependencies
npm install --save-dev typescript @types/node
```

<Tip>
  Only need a Unified Balance and want a lighter install than the full App Kit
  SDK? Install the standalone Unified Balance Kit instead:
  `@circle-fin/unified-balance-kit`
</Tip>

### 1.2. Configure TypeScript (optional)

<Info>
  This step is optional. It helps prevent missing types in your IDE or editor.
</Info>

Create a `tsconfig.json` file configured for ESM and Node:

```bash Shell theme={null}
cat <<'EOF' > tsconfig.json
{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "types": ["node"]
  }
}
EOF
```

### 1.3. Set environment variables

Create an `.env` file in the project directory:

```bash Shell theme={null}
touch .env
```

Add your credentials. Replace `YOUR_API_KEY` with your Circle Developer API key
and `YOUR_ENTITY_SECRET` with your entity secret:

```text .env theme={null}
CIRCLE_API_KEY=YOUR_API_KEY
CIRCLE_ENTITY_SECRET=YOUR_ENTITY_SECRET
```

<Tip>
  Edit `.env` files in your IDE or editor so credentials are not leaked to your
  shell history.
</Tip>

## Step 2. Deposit into the owner's Unified Balance

In this step, the delegate wallet deposits USDC into the owner's Unified Balance
from Base Sepolia.

### 2.1. Create the deposit script

Create a `delegate-deposit.ts` file. In this script, the delegate wallet
deposits 2.00 USDC from Base Sepolia into the owner's Unified Balance.

```typescript delegate-deposit.ts theme={null}
import { AppKit } from "@circle-fin/app-kit";
import { createCircleWalletsAdapter } from "@circle-fin/adapter-circle-wallets";

const DEPOSIT_AMOUNT = "2.00";

const kit = new AppKit();

kit.on("*", (payload) => {
  console.log("Event received:", payload);
});

const adapter = createCircleWalletsAdapter({
  apiKey: process.env.CIRCLE_API_KEY!,
  entitySecret: process.env.CIRCLE_ENTITY_SECRET!,
});

const ownerWalletAddress = "YOUR_BASE_SEPOLIA_OWNER_WALLET_ADDRESS";
const delegateWalletAddress = "YOUR_BASE_SEPOLIA_DELEGATE_WALLET_ADDRESS";

const result = await kit.unifiedBalance.depositFor({
  from: {
    adapter,
    chain: "Base_Sepolia",
    address: delegateWalletAddress,
  },
  amount: DEPOSIT_AMOUNT,
  token: "USDC",
  depositAccount: ownerWalletAddress,
});

console.dir(result, { depth: null, colors: true });
```

<Info>
  `depositFor` is permissionless. Any wallet can fund another account's Unified
  Balance. This quickstart uses it so the delegate funds the owner before the
  owner grants spend authorization.
</Info>

### 2.2. Run the deposit script

In your terminal, run:

```bash Shell theme={null}
npm run deposit
```

You'll see output like:

```bash Shell theme={null}
{
  amount: '2.00',
  token: 'USDC',
  depositedTo: '0x...',
  depositedBy: '0x...',
  chain: 'Base_Sepolia',
  txHash: '0x...',
  explorerUrl: 'https://sepolia.basescan.org/tx/0x...'
}
```

### 2.3. Verify the deposit

Open the `explorerUrl` from the deposit result to confirm the onchain
transaction on Base Sepolia.

## Step 3. Authorize the delegate

In this step, the owner wallet grants the delegate permission to spend from its
Unified Balance on a specific blockchain.

### 3.1. Create the authorize script

Create a `delegate-authorize.ts` file. In this script, the owner wallet
authorizes the delegate to spend from its Unified Balance on Base Sepolia:

```typescript delegate-authorize.ts theme={null}
import { AppKit } from "@circle-fin/app-kit";
import { createCircleWalletsAdapter } from "@circle-fin/adapter-circle-wallets";

const kit = new AppKit();

kit.on("*", (payload) => {
  console.log("Event received:", payload);
});

const adapter = createCircleWalletsAdapter({
  apiKey: process.env.CIRCLE_API_KEY!,
  entitySecret: process.env.CIRCLE_ENTITY_SECRET!,
});

const ownerWalletAddress = "YOUR_BASE_SEPOLIA_OWNER_WALLET_ADDRESS";
const delegateWalletAddress = "YOUR_BASE_SEPOLIA_DELEGATE_WALLET_ADDRESS";

async function main() {
  const status = await kit.unifiedBalance.getDelegateStatus({
    from: {
      adapter,
      chain: "Base_Sepolia",
      address: ownerWalletAddress,
    },
    delegateAddress: delegateWalletAddress,
  });

  if (status === "ready") {
    console.log(
      `Delegate ${delegateWalletAddress} is already authorized on Base_Sepolia.`,
    );
    return;
  }

  if (status === "pending") {
    console.log(
      `Delegate ${delegateWalletAddress} is still pending on Base_Sepolia. Wait and run this script again.`,
    );
    return;
  }

  // addDelegate: owner-signed transaction granting the delegate spend rights.
  const result = await kit.unifiedBalance.addDelegate({
    from: {
      adapter,
      chain: "Base_Sepolia",
      address: ownerWalletAddress,
    },
    delegateAddress: delegateWalletAddress,
  });

  console.dir(result, { depth: null, colors: true });
}

void main();
```

<Info>
  `addDelegate` is an onchain transaction signed by the owner wallet. Once
  authorized, the delegate can spend repeatedly on the same blockchain without
  reauthorization. Authorize only delegate addresses you control, and revoke
  access when it is no longer needed. Authorization is source-blockchain specific.
  See [Manage Delegates](/app-kit/tutorials/unified-balance/manage-delegates) for
  details.
</Info>

### 3.2. Run the authorize script

In your terminal, run:

```bash Shell theme={null}
npm run authorize
```

You'll see output like:

```bash Shell theme={null}
{
  account: '0x...',
  delegateAddress: '0x...',
  chain: 'Base_Sepolia',
  state: 'added',
  txHash: '0x...',
  explorerUrl: 'https://sepolia.basescan.org/tx/0x...'
}
```

If `status` is already `'ready'`, the script exits without calling
`addDelegate`. If `status` is `'pending'`, it asks you to wait and run the
script again. Otherwise it submits `addDelegate`.

## Step 4. Check the owner's Unified Balance

In this step, you check the owner's Unified Balance by address.

### 4.1. Create the balance check script

Create a `delegate-check-balance.ts` file. This script prints the owner's
confirmed and pending Unified Balance totals:

```typescript delegate-check-balance.ts theme={null}
import { AppKit } from "@circle-fin/app-kit";

const kit = new AppKit();

const ownerWalletAddress = "YOUR_BASE_SEPOLIA_OWNER_WALLET_ADDRESS";

const balances = await kit.unifiedBalance.getBalances({
  sources: {
    address: ownerWalletAddress,
    chains: ["Base_Sepolia"],
  },
  networkType: "testnet",
  includePending: true,
});

console.dir(balances, { depth: null, colors: true });
```

<Info>
  You can check balances by address, adapter, chain, and network. See
  [Check Unified Balance](/app-kit/tutorials/unified-balance/check-unified-balance)
  for more options.
</Info>

### 4.2. Run the balance check script

In your terminal, run:

```bash Shell theme={null}
npm run balance
```

You'll see output like:

```bash Shell theme={null}
{
  token: 'USDC',
  totalConfirmedBalance: '2.000000',
  breakdown: [
    {
      depositor: '0x...',
      totalConfirmed: '2.000000',
      breakdown: [
        {
          chain: 'Base_Sepolia',
          confirmedBalance: '2.000000',
          pendingBalance: '0.000000',
          pendingTransactions: []
        }
      ],
      totalPending: '0.000000'
    }
  ],
  totalPendingBalance: '0.000000'
}
```

After a deposit, funds can appear in `totalPendingBalance` before they are
reflected in `totalConfirmedBalance`. Wait until the owner's
`totalConfirmedBalance` is high enough for the spend you plan to make before you
continue.

## Step 5. Spend from the owner's balance

In this step, the delegate spends from the owner's Unified Balance on Arc
Testnet for the recipient. The
[Forwarding Service](/app-kit/tutorials/unified-balance/use-forwarding-service)
submits the destination mint, so you don't need a wallet on Arc Testnet.

<Info>
  The Forwarding Service charges a fee that is deducted from the amount minted on
  the destination chain. The spend result includes the forwarding fee in the fee
  breakdown.
</Info>

### 5.1. Create the spend script

Create a `delegate-spend.ts` file. This script spends 0.50 USDC from the owner's
Unified Balance on Arc Testnet for the recipient, signed by the delegate.

```typescript delegate-spend.ts theme={null}
import { AppKit } from "@circle-fin/app-kit";
import { createCircleWalletsAdapter } from "@circle-fin/adapter-circle-wallets";

const SPEND_AMOUNT = "0.50";

const kit = new AppKit();

kit.on("*", (payload) => {
  console.log("Event received:", payload);
});

const adapter = createCircleWalletsAdapter({
  apiKey: process.env.CIRCLE_API_KEY!,
  entitySecret: process.env.CIRCLE_ENTITY_SECRET!,
});

const ownerWalletAddress = "YOUR_BASE_SEPOLIA_OWNER_WALLET_ADDRESS";
const delegateWalletAddress = "YOUR_BASE_SEPOLIA_DELEGATE_WALLET_ADDRESS";
const recipientAddress = "YOUR_ARC_TESTNET_RECIPIENT_ADDRESS";

console.log(
  `Spending ${SPEND_AMOUNT} USDC on Arc_Testnet for ${recipientAddress}...\n`,
);

const result = await kit.unifiedBalance.spend({
  amount: SPEND_AMOUNT,
  token: "USDC",
  from: [
    {
      adapter,
      address: delegateWalletAddress,
      // Spend from the owner's balance; the delegate wallet signs.
      sourceAccount: ownerWalletAddress,
      allocations: [{ amount: SPEND_AMOUNT, chain: "Base_Sepolia" }],
    },
  ],
  to: {
    chain: "Arc_Testnet",
    recipientAddress,
    useForwarder: true,
  },
});

console.dir(result, { depth: null, colors: true });
```

<Tip>
  You can also customize your Unified Balance to
  [collect a custom fee](/app-kit/tutorials/unified-balance/collect-custom-spend-fees)
  from end users,
  [estimate fees](/app-kit/tutorials/unified-balance/estimate-spend-fees) before
  spending,
  [select source blockchains and allocations](/app-kit/tutorials/unified-balance/select-source-blockchains)
  to fund a balance.
</Tip>

### 5.2. Run the spend script

In your terminal, run:

```bash Shell theme={null}
npm run spend
```

The script logs SDK events and prints the spend result. You'll see output like:

```bash Shell theme={null}
{
  recipientAddress: '0x...',
  destinationChain: 'Arc_Testnet',
  txHash: '0x...',
  explorerUrl: 'https://explorer.testnet.arc.io/tx/0x...',
  allocations: [
    {
      amount: '0.5',
      chain: 'Base_Sepolia',
      sourceAccount: '0x...'
    }
  ],
  fees: [
    { type: 'provider', token: 'USDC', amount: '0.000025', ... },
    { type: 'gasFee', token: 'USDC', amount: '0.024137', ... },
    { type: 'forwarder', token: 'USDC', amount: '0.014162' }
  ],
  transferId: '...',
  expirationBlock: '...'
}
```

### 5.3. Verify the spend

Use the spend result to confirm that USDC arrived at the recipient address on
Arc Testnet. When you use the Forwarding Service, the result can include a
`transferId` instead of a locally submitted destination transaction hash. The
received amount can be less than the requested spend after fees. For more on
fees, see
[How Unified Balance fees work](/app-kit/concepts/unified-balance-fees).
