Docs

system_properties - Bifrost RPC Method

Get chain properties on Bifrost including token symbol, decimals, and the address-format prefix when the chain exposes one. Essential for token formatting, wallet configuration, and address handling.

Returns the chain-specific properties for Bifrost, including the native token symbol, token decimals, and the address-format prefix when the chain exposes one. This information is critical for correctly formatting balances, validating addresses, and configuring wallets.

Why Bifrost? Build on Polkadot's largest liquid staking appchain with 60% DOT LST market share and $125M+ TVL with first LST governance on OpenGov, 60% DOT market share, Hyperbridge ETH integration, and 500K DOT treasury support.

When to Use This Method

system_properties is essential for liquid staking developers, DeFi builders, and teams requiring cross-chain yield solutions:

  • Token Formatting -- Get the correct decimals and symbol to display human-readable balances on Bifrost
  • Address Validation -- Retrieve the SS58 prefix to encode and validate addresses for omnichain liquid staking (vDOT, vKSM, vGLMR, vMOVR, vASTR), cross-chain vToken governance, and DOT/ETH liquidity bridging
  • Wallet and dApp Configuration -- Dynamically configure your UI with the correct token symbol, decimals, and address format
  • Multi-Chain Support -- Automatically adapt your application to different Substrate chains without hardcoding properties

Code Examples

Common Use Cases

1. Human-Readable Balance Formatting

Format raw on-chain balances into human-readable token amounts:

JavaScript
import { ApiPromise, WsProvider } from '@polkadot/api';
import { BN } from '@polkadot/util';

async function formatBalance(api, rawBalance) {
  const properties = await api.rpc.system.properties();
  const raw = properties.toJSON();
  const decimalsRaw = raw.tokenDecimals;
  const symbolRaw = raw.tokenSymbol;
  const decimals = Array.isArray(decimalsRaw) ? decimalsRaw[0] : decimalsRaw;
  const symbol = Array.isArray(symbolRaw) ? symbolRaw[0] : symbolRaw;

  const divisor = new BN(10).pow(new BN(decimals));
  const whole = new BN(rawBalance).div(divisor);
  const fractional = new BN(rawBalance).mod(divisor).toString().padStart(decimals, '0');

  return `${whole}.${fractional.slice(0, 4)} ${symbol}`;
}

// Example output depends on the chain's live token symbol and decimals.

2. Dynamic Wallet Configuration

Auto-configure your wallet or dApp based on chain properties:

JavaScript
async function configureWallet(endpoint) {
  const provider = new WsProvider(endpoint);
  const api = await ApiPromise.create({ provider });

  const [chain, properties] = await Promise.all([
    api.rpc.system.chain(),
    api.rpc.system.properties()
  ]);

  const raw = properties.toJSON();
  const symbolsRaw = raw.tokenSymbol;
  const decimalsRaw = raw.tokenDecimals;
  const symbols = Array.isArray(symbolsRaw) ? symbolsRaw : [symbolsRaw];
  const decimals = Array.isArray(decimalsRaw) ? decimalsRaw : [decimalsRaw];
  const ss58Format = raw.ss58Format ?? raw.SS58Prefix ?? null;

  const config = {
    chainName: chain.toString(),
    ss58Format,
    tokens: symbols.map((symbol, idx) => ({
      symbol,
      decimals: decimals[idx] ?? decimals[0],
    }))
  };

  console.log('Wallet configured for:', config.chainName);
  console.log('Native token:', config.tokens[0].symbol, `(${config.tokens[0].decimals} decimals)`);
  console.log('Address format SS58:', config.ss58Format ?? 'not exposed');

  await api.disconnect();
  return config;
}

3. SS58 Address Encoding and Validation

Use the SS58 prefix to properly encode addresses for the target chain:

JavaScript
import { encodeAddress, decodeAddress } from '@polkadot/util-crypto';

async function formatAddressForChain(api, genericAddress) {
  const properties = await api.rpc.system.properties();
  const raw = properties.toJSON();
  const ss58Format = raw.ss58Format ?? raw.SS58Prefix;

  if (ss58Format == null) {
    throw new Error('This chain does not expose an SS58 prefix through system_properties.');
  }

  // Convert any SS58 address to this chain's format
  const publicKey = decodeAddress(genericAddress);
  const chainAddress = encodeAddress(publicKey, ss58Format);

  console.log(`Address on ${ss58Format}: ${chainAddress}`);
  return chainAddress;
}

Error Handling

Common errors and solutions:

Error CodeDescriptionSolution
-32603Internal errorNode may be starting up -- retry after delay
-32005Rate limit exceededReduce request frequency or implement client-side rate limiting
Connection refusedNode unreachableVerify the RPC endpoint URL and that the node is running
Empty or alternate result fieldsChain spec missing properties or using SS58PrefixNormalize scalar vs array values and fall back to SS58Prefix when ss58Format is absent