Overview
The BalanceService is responsible for tracking and managing token balances across different blockchain networks supported by SubWallet Extension. It provides real-time balance updates, token detection, and cross-chain transfer capabilities.
Key Features
- Multi-chain balance tracking (Substrate, EVM, Ton, Cardano, Bitcoin)
- Real-time balance subscriptions
- Automatic token detection and enabling
- Transfer balance calculations
- XCM (Cross-Consensus Message) support
- Token optimization and balance caching
Class: BalanceService
Constructor
constructor(state: KoniState)
The global state object of the extension
Properties
Indicates whether the service is currently running
Current status of the service (NOT_INITIALIZED, INITIALIZING, INITIALIZED, STARTING, STARTED, STOPPING, STOPPED)
Core Methods
init()
Initializes the balance service and loads cached data.
async init(): Promise<void>
Usage:
const balanceService = new BalanceService(state);
await balanceService.init();
start()
Starts the balance service and begins tracking balances.
async start(): Promise<void>
stop()
Stops the balance service and unsubscribes from all balance updates.
async stop(): Promise<void>
Balance Subscription Methods
subscribeBalance()
Subscribes to balance updates for a specific address, chain, and token.
async subscribeBalance(
address: string,
chain: string,
tokenSlug: string | undefined,
balanceType: BalanceType = BalanceType.TRANSFERABLE,
extrinsicType?: ExtrinsicType,
callback?: (rs: AmountData) => void
): Promise<[() => void, AmountData]>
The wallet address to track
The chain slug (e.g., ‘polkadot’, ‘kusama’, ‘ethereum’)
The token identifier. If undefined, uses the native token
balanceType
BalanceType
default:"BalanceType.TRANSFERABLE"
Type of balance to track (TRANSFERABLE, TOTAL, TOTAL_MINUS_RESERVED)
Optional extrinsic type for context-specific balance calculations
Optional callback function called on balance updates
Returns:
Unsubscribe function to stop receiving updates
Usage:
const [unsub, balance] = await balanceService.subscribeBalance(
'5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
'polkadot',
'polkadot-NATIVE-DOT',
BalanceType.TRANSFERABLE,
undefined,
(balanceData) => {
console.log('New balance:', balanceData.value);
}
);
// Later, unsubscribe
unsub();
getTransferableBalance()
Fetches the transferable balance for an address on a specific chain.
async getTransferableBalance(
address: string,
chain: string,
tokenSlug?: string,
extrinsicType?: ExtrinsicType
): Promise<AmountData>
Returns:
Balance information object
getTotalBalance()
Fetches the total balance (free + locked) for an address.
async getTotalBalance(
address: string,
chain: string,
tokenSlug?: string,
extrinsicType?: ExtrinsicType
): Promise<AmountData>
subscribeBalanceMap()
Subscribes to the complete balance map for all accounts and tokens.
subscribeBalanceMap(): BehaviorSubject<BalanceJson>
Returns:
BehaviorSubject<BalanceJson>
Observable that emits balance updates for all tracked tokens
Usage:
balanceService.subscribeBalanceMap().subscribe((balanceData) => {
console.log('Balance map updated:', balanceData.details);
});
Token Detection Methods
autoEnableChains()
Automatically detects and enables chains with non-zero balances.
async autoEnableChains(addresses: string[]): Promise<void>
Array of addresses to check for balances
evmDetectBalanceToken()
Detects EVM tokens with balances for given addresses.
async evmDetectBalanceToken(addresses: string[]): Promise<string[]>
Returns:
Array of token slugs with non-zero balances
substrateDetectBalanceToken()
Detects Substrate tokens with balances for given addresses.
async substrateDetectBalanceToken(addresses: string[]): Promise<string[]>
Transfer Methods
getOptimalTransferProcess()
Determines the optimal transfer path for cross-chain transfers.
async getOptimalTransferProcess(
params: RequestOptimalTransferProcess
): Promise<CommonOptimalTransferPath>
params
RequestOptimalTransferProcess
required
getTokensHasBalance()
Retrieves tokens with non-zero balances for a specific address and chain.
async getTokensHasBalance(
address: string,
chain: string,
tokenSlug?: string
): Promise<Record<string, BalanceItem>>
Returns:
Record<string, BalanceItem>
Map of token slugs to balance items
Utility Methods
reloadBalance()
Reloads all balance data from scratch.
async reloadBalance(): Promise<void>
removeBalanceByAddresses()
Removes balance data for specific addresses.
removeBalanceByAddresses(addresses: string[]): void
Array of addresses to remove
optimizeEnableTokens()
Optimizes the token list by enabling tokens with balances and disabling zero-balance tokens.
async optimizeEnableTokens(): Promise<void>
Types
BalanceType
enum BalanceType {
TRANSFERABLE = 'TRANSFERABLE',
TOTAL = 'TOTAL',
TOTAL_MINUS_RESERVED = 'TOTAL_MINUS_RESERVED'
}
BalanceItem
interface BalanceItem {
address: string;
tokenSlug: string;
free: string;
locked: string;
state: APIItemState;
chain: string;
lockedDetails?: {
staking?: string;
reserved?: string;
frozen?: string;
};
}
Events
The BalanceService listens to and emits events through the EventService:
account.updateCurrent - Current account changed
account.add - New account added
account.remove - Account removed
chain.updateState - Chain state changed
asset.updateState - Asset state changed
Example: Complete Balance Tracking
import { BalanceService } from '@subwallet/extension-base';
// Initialize service
const balanceService = new BalanceService(state);
await balanceService.init();
await balanceService.start();
// Subscribe to specific token balance
const [unsub, initialBalance] = await balanceService.subscribeBalance(
'5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
'polkadot',
'polkadot-NATIVE-DOT',
BalanceType.TRANSFERABLE,
undefined,
(balance) => {
console.log(`DOT Balance: ${balance.value} ${balance.symbol}`);
}
);
// Get current balance
const balance = await balanceService.getTransferableBalance(
'5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
'polkadot'
);
console.log('Balance:', balance);
// Subscribe to all balances
balanceService.subscribeBalanceMap().subscribe((data) => {
console.log('All balances:', data.details);
});
// Cleanup
unsub();