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

# Balances and deposits

> Read your app's view of the user's value with balances(), and open the host's deposit flow with deposit().

`charge()` already handles a user who is short — Bankroll prompts them to add
funds and your call rejects `insufficient_funds`. These two calls are for the
cases where your app wants to act *before* that: show what the user has, and
offer to top up on your own terms.

<Warning>
  **Prerelease.** `balances()` and `deposit()` are still settling and their
  contract may change. Both need a current Bankroll host and are feature-detected,
  so an older one rejects with `update_required` rather than throwing — treat that
  as "this user can't do it yet" and fall back to the flow you'd have shipped
  without them.
</Warning>

## `balances()`

```ts theme={null}
import { bankroll } from "@joinbankroll/sdk";

const { cashCents, creditsCents, tokens } = await bankroll.balances();
```

Your app's view of the user's value — and only yours. Undeclared mints and other
apps' credits are unreachable by construction, so this can't be used to survey
what a user holds elsewhere.

| Field          | Type                               | Meaning                                                                                                                        |
| -------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `cashCents`    | number                             | The user's cash — HSUSD — in whole US cents.                                                                                   |
| `creditsCents` | number                             | Credits registered to **your app**, in whole US cents. `0` if your app has none.                                               |
| `tokens`       | `Record<string, { amount, name }>` | One entry per mint your manifest declares in [`appTokens`](/build/app-tokens), keyed by mint — the same shape as the manifest. |

```ts theme={null}
{
  cashCents: 2450,
  creditsCents: 500,
  tokens: {
    "7Nk3…": { amount: 12, name: "Acme Chips" },
  },
}
```

<Note>
  **`amount` is whole tokens, not cents.** App tokens are 9-decimal and one token
  to the dollar, so `amount: 12` is twelve Acme Chips. The two cents fields are
  cents. Cents are truncated down — never show a user money they don't have.
</Note>

A mint you declare that the user has never held reads `0` rather than going
missing, so you can render every token in your manifest without a presence
check. `name` comes from your own manifest entry, which is what keeps a token's
display name consistent whether Bankroll or your page draws it.

The host answers from the balance store it already keeps live — the same numbers
its wallet renders. The call costs no upstream fetch, so polling it as a display
heartbeat is fine.

<Warning>
  This is a **display** value, read from the client. Never price an order, settle
  a bet, or release value against it. What a user can pay is decided by whether
  their `charge()` settles, and what they're owed is decided by your own records —
  see [Confirm on your server](/build/payments#confirm-on-your-server).
</Warning>

## `deposit()`

```ts theme={null}
await bankroll.deposit();                     // the host's default source
await bankroll.deposit({ method: "card" });   // preselect the card sheet
```

Presents the host's deposit UI. It **resolves once the modal is presented** —
not when money arrives. The deposit itself runs in native UI and settles long
after this call returns, so treat a resolved `deposit()` as "the user is now
looking at the deposit sheet" and nothing more. To notice funds landing, read
`balances()` again when the user comes back.

`method` preselects a funding source. It is a hint: the host validates it and
falls back to its default when the value is unknown or the user isn't eligible,
so an unrecognized string opens the sheet rather than failing.

| `method`  | Source                                                                                | Availability |
| --------- | ------------------------------------------------------------------------------------- | ------------ |
| `card`    | Debit or credit card — [settles as your app's credits](#app-credits) once provisioned | Live         |
| `cashapp` | Cash App → the user's cash (HSUSD)                                                    | Live         |
| `solana`  | An on-chain transfer into the user's wallet                                           | Live         |
| `bank`    | Bank transfer                                                                         | Coming soon  |
| `interac` | Interac                                                                               | Coming soon  |
| `moonpay` | MoonPay                                                                               | Coming soon  |

A coming-soon value is accepted today — the sheet opens on the host's default
source until that method ships, at which point the same preselection starts
working with no change on your side.

### App credits

For an app with credits, **every card purchase opened from your app settles as
your app's credits**: value scoped to your app, shown in the user's wallet as
yours, and readable back as `creditsCents` in [`balances()`](#balances). There
is nothing to select — `card` does it. The other sources (Cash App, an
on-chain transfer, and the coming-soon set) top up the user's own cash.

This requires Bankroll to have **provisioned credits for your app** — a
per-app setup on Bankroll's side, keyed to your origin; contact the Bankroll
team. Without it, the card sheet still opens but the purchase does not settle
to your app. The resolution is always from your app's verified origin, never
from anything your page passes — so a page cannot route a purchase into
another app's account.

## Together

The pattern these are for: decide with your own numbers, offer the top-up, and
still let `charge()` be the thing that moves money.

```ts theme={null}
import { bankroll, BankrollError } from "@joinbankroll/sdk";

async function buy(order) {
  const { cashCents, creditsCents } = await bankroll.balances();
  if (cashCents + creditsCents < order.amountCents) {
    await bankroll.deposit({ method: "card" });
    return; // they're in the deposit sheet — pick this back up when they return
  }

  try {
    const signature = await bankroll.charge({ amountCents: order.amountCents });
    await confirmOnServer(signature);
  } catch (e) {
    // The balance was a hint; the charge is the authority.
    if (e instanceof BankrollError && e.code === "insufficient_funds") return;
    throw e;
  }
}
```

Handle `update_required` from either call by skipping the pre-check entirely:
`charge()` alone works on every host, and its `insufficient_funds` rejection
already sends the user to add funds.
