Skip to content

Querying Currency Supply⚓︎

BEGINNER

Exchanges and market data aggregators need accurate supply figures to display market capitalization and token metrics.

NEM exposes the supply of XEM, the native currency, through the REST API. This tutorial shows how to query the total supply and derive the circulating supply from it.

Prerequisites⚓︎

This tutorial uses the NEM REST API without requiring an SDK. You only need a way to make HTTP requests.

Full Code⚓︎

import json
import os
import urllib.request

NODE_URL = os.getenv('NODE_URL', 'http://portobelo.nemmain.net:7890')
print(f'Using node {NODE_URL}')

try:
    MOSAIC_ID = 'nem:xem'
    supply_path = f'/mosaic/supply?mosaicId={MOSAIC_ID}'
    with urllib.request.urlopen(f'{NODE_URL}{supply_path}') as response:
        supply_info = json.loads(response.read().decode())
    total_supply = supply_info['supply']
    print(f'Total supply: {total_supply:,.6f} {MOSAIC_ID}')
    # Read the mosaic's divisibility to convert balances to whole units
    definition_path = f'/mosaic/definition?mosaicId={MOSAIC_ID}'
    with urllib.request.urlopen(
        f'{NODE_URL}{definition_path}'
    ) as response:
        definition = json.loads(response.read().decode())
    properties = {
        prop['name']: prop['value']
        for prop in definition['properties']
    }
    divisibility = int(properties['divisibility'])


    NON_CIRCULATING_ADDRESSES = [
        ('Treasury', 'NCHESTYVD2P6P646AMY7WSNG73PCPZDUQNSD6JAK'),
        ('Nemesis', 'NANEMOABLAGR72AZ2RV3V4ZHDCXW25XQ73O7OBT5'),
        ('Namespace rental', 'NAMESPACEWH4MKFMBCVFERDPOOP4FK7MTBXDPZZA'),
        ('Mosaic rental', 'NBMOSAICOD4F54EE5CDMR23CCBGOAM2XSIUX6TRS'),
    ]
    non_circulating_supply = 0
    for label, address in NON_CIRCULATING_ADDRESSES:
        account_path = f'/account/get?address={address}'
        with urllib.request.urlopen(
            f'{NODE_URL}{account_path}'
        ) as response:
            account_info = json.loads(response.read().decode())
        balance = (account_info['account']['balance']
            / (10 ** divisibility))
        non_circulating_supply += balance
        print(f'  {label}: {balance:,.6f} {MOSAIC_ID}')
    print(
        f'Non-circulating supply: '
        f'{non_circulating_supply:,.6f} {MOSAIC_ID}')

    circulating_supply = total_supply - non_circulating_supply
    print(f'Circulating supply: {circulating_supply:,.6f} {MOSAIC_ID}')
except Exception as error:
    print(error)

Download source

const NODE_URL = process.env.NODE_URL ||
    'http://portobelo.nemmain.net:7890';
console.log(`Using node ${NODE_URL}`);

try {
    const fmt = xem =>
        xem.toLocaleString('en-US', { minimumFractionDigits: 6 });

    const MOSAIC_ID = 'nem:xem';
    const supplyPath = `/mosaic/supply?mosaicId=${MOSAIC_ID}`;
    const response = await fetch(`${NODE_URL}${supplyPath}`);
    const supplyInfo = await response.json();
    const totalSupply = supplyInfo.supply;
    console.log(`Total supply: ${fmt(totalSupply)} ${MOSAIC_ID}`);
    // Read the mosaic's divisibility to convert balances to whole units
    const definitionPath = `/mosaic/definition?mosaicId=${MOSAIC_ID}`;
    const definitionResponse =
        await fetch(`${NODE_URL}${definitionPath}`);
    const definition = await definitionResponse.json();
    const properties = Object.fromEntries(
        definition.properties.map(
            property => [property.name, property.value]));
    const divisibility = parseInt(properties.divisibility, 10);


    const NON_CIRCULATING_ADDRESSES = [
        ['Treasury', 'NCHESTYVD2P6P646AMY7WSNG73PCPZDUQNSD6JAK'],
        ['Nemesis', 'NANEMOABLAGR72AZ2RV3V4ZHDCXW25XQ73O7OBT5'],
        ['Namespace rental', 'NAMESPACEWH4MKFMBCVFERDPOOP4FK7MTBXDPZZA'],
        ['Mosaic rental', 'NBMOSAICOD4F54EE5CDMR23CCBGOAM2XSIUX6TRS']
    ];
    let nonCirculatingSupply = 0;
    for (const [label, address] of NON_CIRCULATING_ADDRESSES) {
        const accountPath = `/account/get?address=${address}`;
        const accountResponse = await fetch(`${NODE_URL}${accountPath}`);
        const accountInfo = await accountResponse.json();
        const balance =
            accountInfo.account.balance / (10 ** divisibility);
        nonCirculatingSupply += balance;
        console.log(`  ${label}: ${fmt(balance)} ${MOSAIC_ID}`);
    }
    console.log(
        'Non-circulating supply: ' +
        `${fmt(nonCirculatingSupply)} ${MOSAIC_ID}`
    );

    const circulatingSupply = totalSupply - nonCirculatingSupply;
    console.log(
        'Circulating supply: ' +
        `${fmt(circulatingSupply)} ${MOSAIC_ID}`
    );
} catch (error) {
    console.log(error);
}

Download source

The snippet uses the NODE_URL environment variable to set a NEM mainnet node.

Why mainnet?

Other tutorials typically run against testnet to avoid spending real funds. This one is different because it queries fixed mainnet account addresses to calculate the circulating supply, so NODE_URL must point to a mainnet node.

Code Explanation⚓︎

Fetching the Total Supply⚓︎

    MOSAIC_ID = 'nem:xem'
    supply_path = f'/mosaic/supply?mosaicId={MOSAIC_ID}'
    with urllib.request.urlopen(f'{NODE_URL}{supply_path}') as response:
        supply_info = json.loads(response.read().decode())
    total_supply = supply_info['supply']
    print(f'Total supply: {total_supply:,.6f} {MOSAIC_ID}')
    const MOSAIC_ID = 'nem:xem';
    const supplyPath = `/mosaic/supply?mosaicId=${MOSAIC_ID}`;
    const response = await fetch(`${NODE_URL}${supplyPath}`);
    const supplyInfo = await response.json();
    const totalSupply = supplyInfo.supply;
    console.log(`Total supply: ${fmt(totalSupply)} ${MOSAIC_ID}`);

The total supply of XEM is fixed. All 8'999'999'999 XEM were created in the nemesis block and no new XEM is ever minted.

This tutorial reads values like the supply and divisibility from the API rather than hard-coding them, so the same approach also works for other mosaics, including those whose supply can change.

The code sends a GET request to the /mosaic/supply GET endpoint, passing the XEM mosaic identifier nem:xem as the mosaicId query parameter.

The response is a JSON object with the mosaic identifier and its current supply, expressed in whole units.

Reading the Mosaic's Divisibility⚓︎

    # Read the mosaic's divisibility to convert balances to whole units
    definition_path = f'/mosaic/definition?mosaicId={MOSAIC_ID}'
    with urllib.request.urlopen(
        f'{NODE_URL}{definition_path}'
    ) as response:
        definition = json.loads(response.read().decode())
    properties = {
        prop['name']: prop['value']
        for prop in definition['properties']
    }
    divisibility = int(properties['divisibility'])
    // Read the mosaic's divisibility to convert balances to whole units
    const definitionPath = `/mosaic/definition?mosaicId=${MOSAIC_ID}`;
    const definitionResponse =
        await fetch(`${NODE_URL}${definitionPath}`);
    const definition = await definitionResponse.json();
    const properties = Object.fromEntries(
        definition.properties.map(
            property => [property.name, property.value]));
    const divisibility = parseInt(properties.divisibility, 10);

The supply fetched in the previous step is already in whole units, but the account balances read in the next steps are reported in atomic units.

To convert values to the same units, this step first fetches the mosaic's divisibility. This value is then used to convert the balances from atomic units to whole units.

The /mosaic/definition GET endpoint returns the mosaic's definition, which includes the divisibility. For nem:xem, the divisibility is 6.

Fetching the Non-Circulating Supply⚓︎

    NON_CIRCULATING_ADDRESSES = [
        ('Treasury', 'NCHESTYVD2P6P646AMY7WSNG73PCPZDUQNSD6JAK'),
        ('Nemesis', 'NANEMOABLAGR72AZ2RV3V4ZHDCXW25XQ73O7OBT5'),
        ('Namespace rental', 'NAMESPACEWH4MKFMBCVFERDPOOP4FK7MTBXDPZZA'),
        ('Mosaic rental', 'NBMOSAICOD4F54EE5CDMR23CCBGOAM2XSIUX6TRS'),
    ]
    non_circulating_supply = 0
    for label, address in NON_CIRCULATING_ADDRESSES:
        account_path = f'/account/get?address={address}'
        with urllib.request.urlopen(
            f'{NODE_URL}{account_path}'
        ) as response:
            account_info = json.loads(response.read().decode())
        balance = (account_info['account']['balance']
            / (10 ** divisibility))
        non_circulating_supply += balance
        print(f'  {label}: {balance:,.6f} {MOSAIC_ID}')
    print(
        f'Non-circulating supply: '
        f'{non_circulating_supply:,.6f} {MOSAIC_ID}')
    const NON_CIRCULATING_ADDRESSES = [
        ['Treasury', 'NCHESTYVD2P6P646AMY7WSNG73PCPZDUQNSD6JAK'],
        ['Nemesis', 'NANEMOABLAGR72AZ2RV3V4ZHDCXW25XQ73O7OBT5'],
        ['Namespace rental', 'NAMESPACEWH4MKFMBCVFERDPOOP4FK7MTBXDPZZA'],
        ['Mosaic rental', 'NBMOSAICOD4F54EE5CDMR23CCBGOAM2XSIUX6TRS']
    ];
    let nonCirculatingSupply = 0;
    for (const [label, address] of NON_CIRCULATING_ADDRESSES) {
        const accountPath = `/account/get?address=${address}`;
        const accountResponse = await fetch(`${NODE_URL}${accountPath}`);
        const accountInfo = await accountResponse.json();
        const balance =
            accountInfo.account.balance / (10 ** divisibility);
        nonCirculatingSupply += balance;
        console.log(`  ${label}: ${fmt(balance)} ${MOSAIC_ID}`);
    }
    console.log(
        'Non-circulating supply: ' +
        `${fmt(nonCirculatingSupply)} ${MOSAIC_ID}`
    );

A portion of the total supply is held by accounts that are not part of the open market:

  • Treasury: A reserve account that holds team-controlled XEM.
  • Nemesis: The account that signed the nemesis block. It cannot send transactions after the nemesis block, so any XEM held by this account is effectively out of circulation.
  • Namespace rental sink: Collects the fees paid to register namespaces.
  • Mosaic rental sink: Collects the fees paid to create mosaics.

The code queries each account with the /account/get GET endpoint and sums their balances. Each balance is divided by 10divisibility, using the divisibility value fetched in the previous step, to convert it from atomic units to whole units. For nem:xem, this divisor is 1'000'000.

Deriving the Circulating Supply⚓︎

    circulating_supply = total_supply - non_circulating_supply
    print(f'Circulating supply: {circulating_supply:,.6f} {MOSAIC_ID}')
    const circulatingSupply = totalSupply - nonCirculatingSupply;
    console.log(
        'Circulating supply: ' +
        `${fmt(circulatingSupply)} ${MOSAIC_ID}`
    );

The circulating supply is the total supply minus the non-circulating balances. This is the amount of XEM that is freely available on the open market.

Output⚓︎

The following output shows a typical run querying the currency supply:

1
2
3
4
5
6
7
8
Using node http://portobelo.nemmain.net:7890
Total supply: 8,999,999,999.000000 nem:xem
  Treasury: 1,414,609,948.972326 nem:xem
  Nemesis: 2,011,331.749000 nem:xem
  Namespace rental: 20,220.000000 nem:xem
  Mosaic rental: 1,950.000000 nem:xem
Non-circulating supply: 1,416,643,450.721326 nem:xem
Circulating supply: 7,583,356,548.278674 nem:xem

The output shows the full breakdown of the XEM supply:

  • Total supply (line 2): All the XEM that exists.
  • Non-circulating supply (line 7): The sum of the treasury, nemesis, and rental sink balances.
  • Circulating supply (line 8): The XEM actually available in circulation.

Conclusion⚓︎

This tutorial showed how to:

Step Related documentation
Fetch total supply /mosaic/supply GET
Read the mosaic divisibility /mosaic/definition GET
Fetch non-circulating supply /account/get GET

Next Steps⚓︎

To check a specific account's XEM balance, see the Query Account Balance tutorial.