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

# Unified balance

> Read and spend an account's available assets across supported chains.

A unified balance groups an account's spendable assets across supported chains. Rhinestone uses that portfolio to fund intents without requiring the user to choose a bridge or hold gas on the target chain.

## How routing uses balances

When routing across a unified portfolio, the orchestrator generally prefers:

1. The requested token on the target chain.
2. The same token on another chain.
3. An equivalent asset, such as ETH and WETH or USDC and USDT.
4. Another target-chain asset that can be swapped.
5. Other available assets, including balances split across tokens or chains.

The route accounts for gas, swap, and bridge costs. Multiple source assets may be used when no single balance covers the requested output or when the intent requests several target tokens.

<Note>
  A portfolio is a routing snapshot, not a reservation guarantee. Prepare a
  transaction to receive the executable quote and current costs.
</Note>

## Spendable balance

Portfolio results exclude balances already committed to outstanding intents. This prevents the same funds from being allocated twice while claims or settlement are pending.

The spendable amount can still change between a portfolio read and quote preparation. Refresh the portfolio after completed or failed intents, deposits, withdrawals, and other balance changes.

## Read and spend a unified balance

<Tabs>
  <Tab title="Embedded wallet">
    Use the initialized `wallet` client and authenticated `accountAddress` from [Accounts](/wallets/embedded-wallets/accounts).

    ### Read assets

    Use `getAssets` to read the portfolio:

    ```ts {2} theme={null}
    try {
      const assets = await wallet.getAssets({ accountAddress });

      console.log(assets.balances);
      console.log(assets.mainnets.balances);
      console.log(assets.testnets.balances);
    } catch (error) {
      console.error("Could not load balances", error);
    }
    ```

    Each balance includes its chain ID, token address, symbol, decimals, and a base-unit balance string. `getAssets` requires the application access-token configuration from [Sponsorship setup](/transactions/sponsorship/set-up), including for user-paid intents. The access token identifies the application; it does not grant fee sponsorship by itself.

    `getAssets` rejects on missing app credentials, access-token failures, or portfolio request failures. Handle it with `try`/`catch`. This differs from `sendIntent`, which returns a structured success or failure result.

    ### Fund an intent

    Reuse the target token, amount, and calls from [Send a transaction](/transactions/multichain/send-a-transaction). Include `tokenRequests` and omit source constraints to let the orchestrator select from available mainnet balances:

    ```ts {5} theme={null}
    const result = await wallet.sendIntent({
      accountAddress,
      targetChain: arbitrum.id,
      calls: [...calls],
      tokenRequests: [{ token: usdcOnArbitrum, amount }],
    });
    ```

    Add `sourceAssets` and `sourceChainId` when the user explicitly chooses an input token on a particular chain. `sourceAssets` accepts ERC-20 addresses, not token symbols. Restrictions can prevent a route even when the portfolio has enough value elsewhere.

    With neither `tokenRequests` nor `sourceAssets`, omitting `sourceChainId` defaults funding to the target chain. Do not treat that call shape as a request to search the full crosschain portfolio.

    Sponsorship is required by default. `sendIntent()` does not expose the custom signer SDK's prepared quote object to your application. See [Send a transaction](/transactions/multichain/send-a-transaction) for funding and review options, then use the result to [track the intent](/transactions/multichain/end-to-end-transaction-flow).
  </Tab>

  <Tab title="Custom signer">
    Use `account` from the [Custom signer quickstart](/wallets/custom-signer/quickstart).

    ### Read assets

    ```ts theme={null}
    const portfolio = await account.getPortfolio();

    for (const token of portfolio) {
      console.log(token.symbol, token.chains);
    }
    ```

    Mainnet balances are returned by default. Pass `true` for testnet balances:

    ```ts theme={null}
    const testnetPortfolio = await account.getPortfolio(true);
    ```

    Amounts use each token's base units. Apply the token's decimals before displaying them.

    ### Prepare funding

    Reuse the target token, amount, and calls from [Send a transaction](/transactions/multichain/send-a-transaction). Omit source constraints to let Rhinestone spend the unified balance:

    ```ts theme={null}
    const prepared = await account.prepareTransaction({
      targetChain: arbitrum,
      calls: [...calls],
      tokenRequests: [{ address: usdcOnArbitrum, amount }],
    });

    const quote = prepared.quotes.best;
    console.log(quote.cost.input, quote.cost.output, quote.cost.fees);
    ```

    To restrict routing, add `sourceChains` or `sourceAssets`. Restrictions can prevent a route even when the portfolio has enough value elsewhere. Review the selected quote's inputs, outputs, and fees before signing; see [Send a transaction](/transactions/multichain/send-a-transaction) for signing and submission.
  </Tab>

  <Tab title="REST API">
    ### Read assets

    Call `GET /accounts/{accountAddress}/portfolio` with an EOA or smart-account address. Run this request on your backend; keep the API key out of browser code.

    ```ts {9,16-17} theme={null}
    const baseUrl = "https://v1.orchestrator.rhinestone.dev";
    const apiKey = process.env.RHINESTONE_API_KEY;
    if (!apiKey) throw new Error("RHINESTONE_API_KEY is required");

    const accountAddress = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045";
    const url = new URL(`${baseUrl}/accounts/${accountAddress}/portfolio`);
    url.searchParams.set("filterEmpty", "true");

    const response = await fetch(url, {
      headers: {
        "x-api-key": apiKey,
        "x-api-version": "2026-04.blanc",
      },
    });

    if (!response.ok) throw new Error(await response.text());
    const { portfolio } = await response.json();

    for (const token of portfolio) {
      console.log(token.symbol, token.chains);
    }
    ```

    The response groups each token's balances by chain. For example:

    ```json theme={null}
    {
      "portfolio": [
        {
          "symbol": "USDC",
          "chains": [
            {
              "chainId": "eip155:8453",
              "address": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
              "decimals": 6,
              "amount": "500000"
            }
          ]
        }
      ]
    }
    ```

    `amount` is a base-unit string: this example represents 0.5 USDC on Base. Apply each chain entry's `decimals` before displaying or combining amounts; the same token can have different decimals across chains. `filterEmpty=true` omits empty balances.

    ### Filter by chain or token

    Without filters, the endpoint reads across supported chains. To limit the request, add repeated `chainIds` parameters before calling `fetch`:

    ```ts theme={null}
    url.searchParams.append("chainIds", "eip155:10");
    url.searchParams.append("chainIds", "eip155:8453");
    ```

    Alternatively, add repeated `tokens` parameters using `eip155:<chainId>:<tokenAddress>` values:

    ```ts theme={null}
    url.searchParams.append(
      "tokens",
      "eip155:10:0x0b2c639c533813f4aa9d7837caf62653d097ff85",
    );
    url.searchParams.append(
      "tokens",
      "eip155:8453:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
    );
    ```

    These filters scope the portfolio read, not a later quote. To spend the balance, follow the REST API tab in [End-to-end transaction flow](/transactions/multichain/end-to-end-transaction-flow) and set source constraints on the quote only when needed.
  </Tab>
</Tabs>

## Keep displayed and executable amounts distinct

* Display portfolio balances as available assets, not guaranteed transaction outputs.
* Use the prepared quote to show exact inputs, outputs, and fees when your client exposes one.
* Keep pending intent IDs and refresh balances after their terminal result.
* Do not add an account's existing balance to `auxiliaryFunds`; doing so double-counts funds during quoting.
* If source restrictions cause insufficient liquidity, relax only the restrictions the user has agreed to change and prepare a new quote.

Next, [send a multichain transaction](/transactions/multichain/send-a-transaction) or [make a swap](/transactions/trading/make-a-swap) using the available balance.
