PartyLayerDocs
Try Demo

Wallet Balances

PartyLayer does not have a dedicated getBalance() method. Token holdings on the Canton Network live as contracts in the Active Contract Set (ACS) — not as a single numeric value. You query the ACS, then sum the amounts across all holding contracts.

ℹ️ Note
Think of it like a UTXO model. A party's balance for a given token is the sum of all active holding contracts they own for that token template.

Prerequisites

  • Wallet connected — see Quick Start
  • ledgerApi capability supported by the connected wallet (Console, Loop, Nightly, and Bron all support this)

React

Single token balance

tsx
import { useState, useEffect } from 'react';
import { useSession, usePartyLayer } from '@partylayer/react';

function TokenBalance({ templateId }: { templateId: string }) {
  const session = useSession();
  const client = usePartyLayer();
  const [balance, setBalance] = useState<number | null>(null);

  useEffect(() => {
    if (!session) return;

    client.ledgerApi({
      requestMethod: 'POST',
      resource: '/v2/state/acs',
      body: JSON.stringify({
        filter: {
          filtersByParty: {
            [session.partyId]: {
              inclusive: {
                templateFilters: [{ templateId }],
              },
            },
          },
        },
      }),
    }).then((result) => {
      const { activeContracts = [] } = JSON.parse(result.response);
      const total = activeContracts.reduce(
        (sum: number, c: any) =>
          sum + parseFloat(c.payload?.amount?.initialAmount ?? '0'),
        0
      );
      setBalance(total);
    });
  }, [session, templateId]);

  if (!session) return null;
  return <span>{balance ?? '…'}</span>;
}

// Usage
<TokenBalance templateId="Splice.Amulet:Amulet" />

Multiple tokens in parallel

tsx
import { useState, useEffect } from 'react';
import { useSession, usePartyLayer } from '@partylayer/react';

const TOKEN_TEMPLATES = [
  'Splice.Amulet:Amulet',
  'YourProject.Token:Token',
];

function MultiTokenBalances() {
  const session = useSession();
  const client = usePartyLayer();
  const [balances, setBalances] = useState<Record<string, number>>({});

  useEffect(() => {
    if (!session) return;

    Promise.all(
      TOKEN_TEMPLATES.map((templateId) =>
        client
          .ledgerApi({
            requestMethod: 'POST',
            resource: '/v2/state/acs',
            body: JSON.stringify({
              filter: {
                filtersByParty: {
                  [session.partyId]: {
                    inclusive: { templateFilters: [{ templateId }] },
                  },
                },
              },
            }),
          })
          .then((result) => {
            const { activeContracts = [] } = JSON.parse(result.response);
            return {
              templateId,
              total: activeContracts.reduce(
                (sum: number, c: any) =>
                  sum + parseFloat(c.payload?.amount?.initialAmount ?? '0'),
                0
              ),
            };
          })
      )
    ).then((results) => {
      setBalances(
        Object.fromEntries(results.map((r) => [r.templateId, r.total]))
      );
    });
  }, [session]);

  return (
    <ul>
      {Object.entries(balances).map(([template, amount]) => (
        <li key={template}>
          {template}: {amount}
        </li>
      ))}
    </ul>
  );
}

Vanilla JS

Single token

typescript
import { createPartyLayer } from '@partylayer/sdk';

const client = createPartyLayer({
  network: 'mainnet',
  app: { name: 'My App' },
});

const session = await client.connect();

async function getBalance(templateId: string): Promise<number> {
  const result = await client.ledgerApi({
    requestMethod: 'POST',
    resource: '/v2/state/acs',
    body: JSON.stringify({
      filter: {
        filtersByParty: {
          [session.partyId]: {
            inclusive: {
              templateFilters: [{ templateId }],
            },
          },
        },
      },
    }),
  });

  const { activeContracts = [] } = JSON.parse(result.response);
  return activeContracts.reduce(
    (sum: number, c: any) =>
      sum + parseFloat(c.payload?.amount?.initialAmount ?? '0'),
    0
  );
}

const balance = await getBalance('Splice.Amulet:Amulet');
console.log('Balance:', balance);

All holdings (unfiltered)

Fetch every active contract for the connected party, regardless of token type:

typescript
const result = await client.ledgerApi({
  requestMethod: 'GET',
  resource: '/v2/state/acs/active-contracts',
});

const { activeContracts } = JSON.parse(result.response);
console.log(activeContracts);

Notes

Template ID format

Template IDs follow the pattern Module.Name:EntityName — for example, Splice.Amulet:Amulet. These are defined in your Daml project. Check your Daml source or deployed package to find the exact values for your tokens.

Response parsing

ledgerApi returns { response: string } — a raw JSON string from the Canton Ledger API. Always parse it with JSON.parse(result.response) before accessing fields like activeContracts.

Wallet support

Console, Loop, Nightly, and Bron all support ledgerApi. Cantor8 (mobile deep link) does not — calling ledgerApi with a Cantor8 session throws CapabilityNotSupportedError.

Paginated results

The ACS endpoint may paginate for parties with many contracts. Check nextPageToken in the parsed response and pass it as pageToken in subsequent requests to retrieve all pages.

PreviousTypeScript TypesNextToken Transfers