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