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

# 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

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 a
    [API Key](https://developers.circle.com/api-reference/keys#creating-an-api-key-for-developer-services)
    and
    [entity secret](https://developers.circle.com/wallets/dev-controlled/register-entity-secret)
    from the
    [Circle Console](https://developers.circle.com/w3s/circle-developer-account).
  * 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:

```bash Shell theme={null}
npx tsc --init
```

Then, update the `tsconfig.json` file:

```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://testnet.arcscan.app/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).
