Skip to content

Getting Mosaic Information⚓︎

BEGINNER

Every mosaic on NEM has a set of on-chain properties such as supply, divisibility, and transfer rules.

This tutorial shows how to retrieve a mosaic's properties and its current supply.

Prerequisites⚓︎

This tutorial only reads data from the network. No account is required.

Before you start, make sure to set up your development environment.

Full Code⚓︎

The following is the complete code listing for this tutorial. A detailed, step-by-step explanation follows in the next section.

import json
import os
import urllib.request

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

MOSAIC_ID = os.getenv('MOSAIC_ID', 'nem:xem')
print(f'Mosaic ID: {MOSAIC_ID}')

try:
    # Fetch mosaic information
    mosaic_path = f'/mosaic/definition?mosaicId={MOSAIC_ID}'
    print(f'Fetching mosaic information from {mosaic_path}')
    with urllib.request.urlopen(f'{NODE_URL}{mosaic_path}') as response:
        response_json = json.loads(response.read().decode())
        mosaic_id = response_json['id']
        full_name = f'{mosaic_id["namespaceId"]}:{mosaic_id["name"]}'
        print('Mosaic information:')
        print(f'  Mosaic ID: {full_name}')
        print(f'  Description: {response_json["description"]}')
        print(f'  Creator: {response_json["creator"]}')
        properties = {
            prop['name']: prop['value']
            for prop in response_json['properties']
        }
        divisibility = int(properties['divisibility'])
        print(f'  Divisibility: {divisibility}')
        print(f'  Initial supply: {properties["initialSupply"]}')
        print(f'  Supply mutable: {properties["supplyMutable"]}')
        print(f'  Transferable: {properties["transferable"]}')
        levy = response_json['levy']
        print(f'  Levy: {levy if levy else "none"}')

    # Fetch the current supply
    supply_path = f'/mosaic/supply?mosaicId={MOSAIC_ID}'
    print(f'\nFetching current supply from {supply_path}')
    with urllib.request.urlopen(f'{NODE_URL}{supply_path}') as response:
        supply_info = json.loads(response.read().decode())
        supply = supply_info['supply']
        print(f'  Current supply: {supply}')

    # Convert the supply to atomic units
    atomic = supply * 10 ** divisibility
    print(f'\nSupply in atomic units: {atomic}')

except Exception as error:
    print(error)

Download source

const NODE_URL = process.env.NODE_URL ||
    'http://libertalia.nemtest.net:7890';
console.log('Using node', NODE_URL);

const MOSAIC_ID = process.env.MOSAIC_ID || 'nem:xem';
console.log('Mosaic ID:', MOSAIC_ID);

try {
    // Fetch mosaic information
    const mosaicPath = `/mosaic/definition?mosaicId=${MOSAIC_ID}`;
    console.log('Fetching mosaic information from', mosaicPath);
    const mosaicResponse = await fetch(`${NODE_URL}${mosaicPath}`);
    if (!mosaicResponse.ok)
        throw new Error(`HTTP error! status: ${mosaicResponse.status}`);

    const mosaicJSON = await mosaicResponse.json();
    const fullName = `${mosaicJSON.id.namespaceId}:${mosaicJSON.id.name}`;
    console.log('Mosaic information:');
    console.log(`  Mosaic ID: ${fullName}`);
    console.log('  Description:', mosaicJSON.description);
    console.log('  Creator:', mosaicJSON.creator);
    const properties = Object.fromEntries(mosaicJSON.properties
        .map(property => [property.name, property.value]));
    const divisibility = parseInt(properties.divisibility, 10);
    console.log('  Divisibility:', divisibility);
    console.log('  Initial supply:', properties.initialSupply);
    console.log('  Supply mutable:', properties.supplyMutable);
    console.log('  Transferable:', properties.transferable);
    const hasLevy = 0 !== Object.keys(mosaicJSON.levy).length;
    console.log('  Levy:', hasLevy ? mosaicJSON.levy : 'none');

    // Fetch the current supply
    const supplyPath = `/mosaic/supply?mosaicId=${MOSAIC_ID}`;
    console.log('\nFetching current supply from', supplyPath);
    const supplyResponse = await fetch(`${NODE_URL}${supplyPath}`);
    const supplyInfo = await supplyResponse.json();
    const supply = supplyInfo.supply;
    console.log('  Current supply:', supply);

    // Convert the supply to atomic units
    const atomic = BigInt(supply) * (10n ** BigInt(divisibility));
    console.log(`\nSupply in atomic units: ${atomic}`);

} catch (e) {
    console.error(e.message);
}

Download source

//JAVA 21+
//DEPS org.symbol:symbol-sdk:3.3.3

import java.io.IOException;
import java.math.BigInteger;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public final class GetMosaicInfo {
    private static final ObjectMapper JSON_MAPPER = new ObjectMapper();

    private static final HttpClient HTTP_CLIENT =
        HttpClient.newHttpClient();

    private static final String NODE_URL = System.getenv().getOrDefault(
        "NODE_URL", "http://libertalia.nemtest.net:7890");

    public static void main(final String[] args) {
        try {
            new GetMosaicInfo().run();
        } catch (final Exception ex) {
            System.out.println(null == ex.getMessage()
                ? ex.toString()
                : ex.getMessage());
        }
    }

    private void run() throws IOException, InterruptedException {
        System.out.printf("Using node %s%n", NODE_URL);

        final String mosaicId = System.getenv().getOrDefault(
            "MOSAIC_ID", "nem:xem");
        System.out.printf("Mosaic ID: %s%n", mosaicId);

        // Fetch mosaic information
        final String mosaicPath = String.format(
            "/mosaic/definition?mosaicId=%s", mosaicId);
        System.out.printf("Fetching mosaic information from %s%n",
            mosaicPath);
        final HttpRequest mosaicRequest = HttpRequest.newBuilder(
            URI.create(NODE_URL + mosaicPath)).GET().build();
        final HttpResponse<String> mosaicResponse = HTTP_CLIENT.send(
            mosaicRequest, BodyHandlers.ofString());
        if (2 != mosaicResponse.statusCode() / 100)
            throw new IOException("HTTP error! status: "
                + mosaicResponse.statusCode());
        final JsonNode mosaicJson = JSON_MAPPER.readTree(
            mosaicResponse.body());
        final JsonNode id = mosaicJson.get("id");
        final String fullName = String.format("%s:%s",
            id.get("namespaceId").asText(), id.get("name").asText());
        System.out.println("Mosaic information:");
        System.out.printf("  Mosaic ID: %s%n", fullName);
        System.out.printf("  Description: %s%n",
            mosaicJson.get("description").asText());
        System.out.printf("  Creator: %s%n",
            mosaicJson.get("creator").asText());
        final Map<String, String> properties = new HashMap<>();
        for (final JsonNode property : mosaicJson.get("properties")) {
            properties.put(property.get("name").asText(),
                property.get("value").asText());
        }
        final int divisibility = Integer.parseInt(
            properties.get("divisibility"));
        System.out.printf("  Divisibility: %d%n", divisibility);
        System.out.printf("  Initial supply: %s%n",
            properties.get("initialSupply"));
        System.out.printf("  Supply mutable: %s%n",
            properties.get("supplyMutable"));
        System.out.printf("  Transferable: %s%n",
            properties.get("transferable"));
        final JsonNode levy = mosaicJson.get("levy");
        System.out.printf("  Levy: %s%n",
            levy.isEmpty() ? "none" : levy.toString());

        // Fetch the current supply
        final String supplyPath = String.format(
            "/mosaic/supply?mosaicId=%s", mosaicId);
        System.out.printf("%nFetching current supply from %s%n",
            supplyPath);
        final HttpRequest supplyRequest = HttpRequest.newBuilder(
            URI.create(NODE_URL + supplyPath)).GET().build();
        final HttpResponse<String> supplyResponse = HTTP_CLIENT.send(
            supplyRequest, BodyHandlers.ofString());
        final BigInteger supply = new BigInteger(
            JSON_MAPPER.readTree(supplyResponse.body())
                .get("supply").asText());
        System.out.printf("  Current supply: %s%n", supply);

        // Convert the supply to atomic units
        final BigInteger atomic = supply.multiply(
            BigInteger.TEN.pow(divisibility));
        System.out.printf("%nSupply in atomic units: %s%n", atomic);

    }
}

Download source

The snippet uses the NODE_URL environment variable to set the NEM API node. If no value is provided, a default testnet node is used.

The MOSAIC_ID environment variable specifies which mosaic to query, given as its fully qualified name. If not set, it defaults to the XEM mosaic (nem:xem).

Code Explanation⚓︎

Fetching Mosaic Information⚓︎

    # Fetch mosaic information
    mosaic_path = f'/mosaic/definition?mosaicId={MOSAIC_ID}'
    print(f'Fetching mosaic information from {mosaic_path}')
    with urllib.request.urlopen(f'{NODE_URL}{mosaic_path}') as response:
        response_json = json.loads(response.read().decode())
        mosaic_id = response_json['id']
        full_name = f'{mosaic_id["namespaceId"]}:{mosaic_id["name"]}'
        print('Mosaic information:')
        print(f'  Mosaic ID: {full_name}')
        print(f'  Description: {response_json["description"]}')
        print(f'  Creator: {response_json["creator"]}')
        properties = {
            prop['name']: prop['value']
            for prop in response_json['properties']
        }
        divisibility = int(properties['divisibility'])
        print(f'  Divisibility: {divisibility}')
        print(f'  Initial supply: {properties["initialSupply"]}')
        print(f'  Supply mutable: {properties["supplyMutable"]}')
        print(f'  Transferable: {properties["transferable"]}')
        levy = response_json['levy']
        print(f'  Levy: {levy if levy else "none"}')
    // Fetch mosaic information
    const mosaicPath = `/mosaic/definition?mosaicId=${MOSAIC_ID}`;
    console.log('Fetching mosaic information from', mosaicPath);
    const mosaicResponse = await fetch(`${NODE_URL}${mosaicPath}`);
    if (!mosaicResponse.ok)
        throw new Error(`HTTP error! status: ${mosaicResponse.status}`);

    const mosaicJSON = await mosaicResponse.json();
    const fullName = `${mosaicJSON.id.namespaceId}:${mosaicJSON.id.name}`;
    console.log('Mosaic information:');
    console.log(`  Mosaic ID: ${fullName}`);
    console.log('  Description:', mosaicJSON.description);
    console.log('  Creator:', mosaicJSON.creator);
    const properties = Object.fromEntries(mosaicJSON.properties
        .map(property => [property.name, property.value]));
    const divisibility = parseInt(properties.divisibility, 10);
    console.log('  Divisibility:', divisibility);
    console.log('  Initial supply:', properties.initialSupply);
    console.log('  Supply mutable:', properties.supplyMutable);
    console.log('  Transferable:', properties.transferable);
    const hasLevy = 0 !== Object.keys(mosaicJSON.levy).length;
    console.log('  Levy:', hasLevy ? mosaicJSON.levy : 'none');
        // Fetch mosaic information
        final String mosaicPath = String.format(
            "/mosaic/definition?mosaicId=%s", mosaicId);
        System.out.printf("Fetching mosaic information from %s%n",
            mosaicPath);
        final HttpRequest mosaicRequest = HttpRequest.newBuilder(
            URI.create(NODE_URL + mosaicPath)).GET().build();
        final HttpResponse<String> mosaicResponse = HTTP_CLIENT.send(
            mosaicRequest, BodyHandlers.ofString());
        if (2 != mosaicResponse.statusCode() / 100)
            throw new IOException("HTTP error! status: "
                + mosaicResponse.statusCode());
        final JsonNode mosaicJson = JSON_MAPPER.readTree(
            mosaicResponse.body());
        final JsonNode id = mosaicJson.get("id");
        final String fullName = String.format("%s:%s",
            id.get("namespaceId").asText(), id.get("name").asText());
        System.out.println("Mosaic information:");
        System.out.printf("  Mosaic ID: %s%n", fullName);
        System.out.printf("  Description: %s%n",
            mosaicJson.get("description").asText());
        System.out.printf("  Creator: %s%n",
            mosaicJson.get("creator").asText());
        final Map<String, String> properties = new HashMap<>();
        for (final JsonNode property : mosaicJson.get("properties")) {
            properties.put(property.get("name").asText(),
                property.get("value").asText());
        }
        final int divisibility = Integer.parseInt(
            properties.get("divisibility"));
        System.out.printf("  Divisibility: %d%n", divisibility);
        System.out.printf("  Initial supply: %s%n",
            properties.get("initialSupply"));
        System.out.printf("  Supply mutable: %s%n",
            properties.get("supplyMutable"));
        System.out.printf("  Transferable: %s%n",
            properties.get("transferable"));
        final JsonNode levy = mosaicJson.get("levy");
        System.out.printf("  Levy: %s%n",
            levy.isEmpty() ? "none" : levy.toString());

The /mosaic/definition GET endpoint retrieves the definition of a mosaic, including:

  • Description: Text describing the mosaic.
  • Creator: The public key of the account that created the mosaic.
  • Properties: The mosaic's behavioral properties:
    • Divisibility: The number of decimal places the mosaic supports. For example, XEM has a divisibility of 6, meaning 1 XEM equals 1'000'000 atomic units.
    • Initial supply: The supply at creation time, expressed in whole units.
    • Supply mutability: Whether the creator can change the supply after creation.
    • Transferability: Whether the mosaic can be freely sent between accounts or only to and from the creator.
  • Levy: An optional extra fee paid to a third account whenever the mosaic is transferred.

Fetching the Current Supply⚓︎

    # Fetch the current supply
    supply_path = f'/mosaic/supply?mosaicId={MOSAIC_ID}'
    print(f'\nFetching current supply from {supply_path}')
    with urllib.request.urlopen(f'{NODE_URL}{supply_path}') as response:
        supply_info = json.loads(response.read().decode())
        supply = supply_info['supply']
        print(f'  Current supply: {supply}')
    // Fetch the current supply
    const supplyPath = `/mosaic/supply?mosaicId=${MOSAIC_ID}`;
    console.log('\nFetching current supply from', supplyPath);
    const supplyResponse = await fetch(`${NODE_URL}${supplyPath}`);
    const supplyInfo = await supplyResponse.json();
    const supply = supplyInfo.supply;
    console.log('  Current supply:', supply);
        // Fetch the current supply
        final String supplyPath = String.format(
            "/mosaic/supply?mosaicId=%s", mosaicId);
        System.out.printf("%nFetching current supply from %s%n",
            supplyPath);
        final HttpRequest supplyRequest = HttpRequest.newBuilder(
            URI.create(NODE_URL + supplyPath)).GET().build();
        final HttpResponse<String> supplyResponse = HTTP_CLIENT.send(
            supplyRequest, BodyHandlers.ofString());
        final BigInteger supply = new BigInteger(
            JSON_MAPPER.readTree(supplyResponse.body())
                .get("supply").asText());
        System.out.printf("  Current supply: %s%n", supply);

The definition only records the initial supply. For mosaics with mutable supply, the current value can differ, so the /mosaic/supply GET endpoint returns the supply currently in circulation, expressed in whole units.

Converting to Atomic Units⚓︎

    # Convert the supply to atomic units
    atomic = supply * 10 ** divisibility
    print(f'\nSupply in atomic units: {atomic}')
    // Convert the supply to atomic units
    const atomic = BigInt(supply) * (10n ** BigInt(divisibility));
    console.log(`\nSupply in atomic units: ${atomic}`);
        // Convert the supply to atomic units
        final BigInteger atomic = supply.multiply(
            BigInteger.TEN.pow(divisibility));
        System.out.printf("%nSupply in atomic units: %s%n", atomic);

The endpoint reports supply in whole units, but transaction quantities are expressed in atomic units. To convert from whole to atomic units, the code multiplies the supply by 10 raised to the mosaic's divisibility.

For XEM (divisibility 6), a supply of 8'999'999'999 whole units equals 8'999'999'999'000'000' atomic units.

Output⚓︎

The output shown below corresponds to a typical run of the program, querying the XEM mosaic on testnet.

Using node http://libertalia.nemtest.net:7890
Mosaic ID: nem:xem
Fetching mosaic information from /mosaic/definition?mosaicId=nem:xem
Mosaic information:
  Mosaic ID: nem:xem
  Description: reserved xem mosaic
  Creator: 3e82e1c1e4a75adaa3cba8c101c3cd31d9817a2eb966eb3b511fb2ed45b8e262
  Divisibility: 6
  Initial supply: 8999999999
  Supply mutable: false
  Transferable: true
  Levy: none

Fetching current supply from /mosaic/supply?mosaicId=nem:xem
  Current supply: 8999999999

Supply in atomic units: 8999999999000000

Some highlights from the output:

  • Mosaic ID (line 5): The XEM mosaic identifier, the fully qualified name nem:xem.

  • Description (line 6): Text that describes the mosaic, set by its creator.

  • Creator (line 7): The public key of the account that created the mosaic.

  • Divisibility (line 8): The value 6 means 1 XEM = 1'000'000 (106) atomic units.

  • Initial supply (line 9): The supply at creation time, in whole units.

  • Supply mutable (line 10): The value false means the XEM supply can never change.

  • Transferable (line 11): The value true means XEM can be freely sent between accounts.

  • Levy (line 12): XEM transfers carry no additional mosaic fee.

  • Current supply (line 15): The supply currently in circulation, identical to the initial supply because XEM is not mutable.

  • Supply in atomic units (line 17): The supply converted from whole units to atomic units using the mosaic's divisibility.

Conclusion⚓︎

This tutorial showed how to:

Step Related documentation
Fetch the mosaic definition /mosaic/definition GET
Fetch the current supply /mosaic/supply GET

Next Steps⚓︎