# system_properties - Bridge Hub RPC Method

Returns the chain-specific properties for Bridge Hub, 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 Bridge Hub?** Build on Polkadot's trustless bridging parachain with $75M+ TVL via Snowbridge to Ethereum with on-chain BEEFY/Beacon light clients (no multisigs), 100+ ERC-20 tokens supported, 24+ parachain integrations, and 1-2 minute transfer times.

## When to Use This Method

`system_properties` is essential for cross-chain developers, bridge operators, and teams requiring trustless Ethereum-Polkadot transfers:

- **Token Formatting** -- Get the correct decimals and symbol to display human-readable balances on Bridge Hub
- **Address Validation** -- Retrieve the SS58 prefix to encode and validate addresses for trustless ETH and ERC-20 bridging, cross-chain messaging, and parachain-to-Ethereum asset transfers
- **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

## Best Practices

- `tokenDecimals` determines on-chain amount display (verified: Polkadot returns 10 decimals for DOT)
- `tokenSymbol` provides the native token ticker for UI display
- `ss58Format` is the address encoding prefix for this chain (0 for Polkadot, 2 for Kusama)
- Cache these properties at startup -- they do not change without a chain migration

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_properties",
  "params": [],
  "id": 1
}
```

## Response Fields

- `ss58Format or SS58Prefix` (`Number, required`): The SS58 address format prefix used by this chain, when the chain exposes one
- `tokenDecimals` (`Number | Array<Number>, required`): Number of decimal places for the native token, or an array for multi-token chains
- `tokenSymbol` (`String | Array<String>, required`): Native token symbol, or an array for multi-token chains

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "ss58Format": 42,
    "tokenDecimals": 9,
    "tokenSymbol": "TOKEN"
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://bridge-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_properties",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://bridge-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

const properties = await api.rpc.system.properties();

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

console.log('Token symbol:', tokenSymbol);
console.log('Token decimals:', tokenDecimals);
console.log('SS58 format:', ss58Format ?? 'not exposed');

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://bridge-hub-polkadot-rpc.n.dwellir.com', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'system_properties',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Properties:', result);
```

```python
import requests

def get_chain_properties():
    response = requests.post(
        'https://bridge-hub-polkadot-rpc.n.dwellir.com',
        json={
            'jsonrpc': '2.0',
            'method': 'system_properties',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

props = get_chain_properties()
token_symbol = props['tokenSymbol']
token_decimals = props['tokenDecimals']
ss58_format = props.get('ss58Format', props.get('SS58Prefix'))

print(f"Token: {token_symbol}")
print(f"Decimals: {token_decimals}")
print(f"SS58 Format: {ss58_format if ss58_format is not None else 'not exposed'}")

# Using substrate-interface
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://bridge-hub-polkadot-rpc.n.dwellir.com')
props = substrate.properties
print(f"Token: {props.get('tokenSymbol')}")
print(f"Decimals: {props.get('tokenDecimals')}")
print(f"SS58 Format: {props.get('ss58Format', props.get('SS58Prefix', 'not exposed'))}")
```

```rust
use serde_json::json;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct ChainProperties {
    #[serde(alias = "SS58Prefix")]
    ss58_format: Option<u16>,
    token_decimals: Option<serde_json::Value>,
    token_symbol: Option<serde_json::Value>,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://bridge-hub-polkadot-rpc.n.dwellir.com")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "system_properties",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let props: ChainProperties = serde_json::from_value(result["result"].clone())?;

    println!("SS58 Format: {:?}", props.ss58_format);
    println!("Token Decimals: {:?}", props.token_decimals);
    println!("Token Symbol: {:?}", props.token_symbol);
    Ok(())
}
```

## 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;
}
```

ze scalar vs array values and fall back to `SS58Prefix` when `ss58Format` is absent |

## Related Methods

- [`system_chain`](https://www.dwellir.com/docs/bridge-hub/system_chain) -- Get the chain name
- [`system_name`](https://www.dwellir.com/docs/bridge-hub/system_name) -- Get the node implementation name
- [`system_version`](https://www.dwellir.com/docs/bridge-hub/system_version) -- Get the node implementation version
- [`state_getMetadata`](https://www.dwellir.com/docs/bridge-hub/state_getMetadata) -- Get full runtime metadata including pallet definitions
- [`rpc_methods`](https://www.dwellir.com/docs/bridge-hub/rpc_methods) -- List all available RPC methods
