Skip to content

Getting Namespace Information⚓︎

BEGINNER

This tutorial shows how to retrieve a namespace's properties, its Subnamespaces, and the mosaics defined under it.

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⚓︎

import json
import os
import urllib.request

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

NAMESPACE_NAME = os.getenv('NAMESPACE_NAME', 'company')
print(f'Namespace name: {NAMESPACE_NAME}')

try:
    # Fetch namespace information
    namespace_path = f'/namespace?namespace={NAMESPACE_NAME}'
    print(f'Fetching namespace information from {namespace_path}')
    with urllib.request.urlopen(f'{NODE_URL}{namespace_path}') as response:
        namespace_info = json.loads(response.read().decode())
        print('Namespace information:')
        print(f'  Name: {namespace_info["fqn"]}')
        print(f'  Owner: {namespace_info["owner"]}')
        lease_height = namespace_info['height']
        print(f'  Height: {lease_height}')

    # Compute the lease expiration
    LEASE_DURATION = 525600  # approximately one year of blocks
    with urllib.request.urlopen(f'{NODE_URL}/chain/height') as response:
        current_height = json.loads(response.read().decode())['height']
    expiration_height = lease_height + LEASE_DURATION
    print(f'\nCurrent chain height: {current_height}')
    print(f'Lease expiration height: {expiration_height}')
    print(f'Blocks until expiration: {expiration_height - current_height}')

    # List the subnamespaces
    owner = namespace_info['owner']
    subnamespaces_path = (
        f'/account/namespace/page'
        f'?address={owner}&parent={NAMESPACE_NAME}')
    print(f'\nFetching subnamespaces from {subnamespaces_path}')
    with urllib.request.urlopen(
        f'{NODE_URL}{subnamespaces_path}'
    ) as response:
        subnamespaces = json.loads(response.read().decode())['data']
        print(f'Subnamespaces of {NAMESPACE_NAME}: {len(subnamespaces)}')
        for subnamespace in subnamespaces:
            print(f'  {subnamespace["fqn"]}')

    # List the mosaics defined under the namespace
    mosaics_path = (
        f'/namespace/mosaic/definition/page?namespace={NAMESPACE_NAME}')
    print(f'\nFetching mosaic definitions from {mosaics_path}')
    with urllib.request.urlopen(f'{NODE_URL}{mosaics_path}') as response:
        mosaics = json.loads(response.read().decode())['data']
        print(f'Mosaics defined under {NAMESPACE_NAME}: {len(mosaics)}')
        for entry in mosaics:
            mosaic_id = entry['mosaic']['id']
            print(f'  {mosaic_id["namespaceId"]}:{mosaic_id["name"]}')

except Exception as e:
    print(e)

Download source

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

const NAMESPACE_NAME = process.env.NAMESPACE_NAME || 'company';
console.log('Namespace name:', NAMESPACE_NAME);

try {
    // Fetch namespace information
    const namespacePath = `/namespace?namespace=${NAMESPACE_NAME}`;
    console.log('Fetching namespace information from', namespacePath);
    const namespaceResponse = await fetch(`${NODE_URL}${namespacePath}`);
    if (!namespaceResponse.ok)
        throw new Error(`HTTP error! status: ${namespaceResponse.status}`);

    const namespaceInfo = await namespaceResponse.json();
    console.log('Namespace information:');
    console.log('  Name:', namespaceInfo.fqn);
    console.log('  Owner:', namespaceInfo.owner);
    const leaseHeight = namespaceInfo.height;
    console.log('  Height:', leaseHeight);

    // Compute the lease expiration
    const LEASE_DURATION = 525600; // approximately one year of blocks
    const chainResponse = await fetch(`${NODE_URL}/chain/height`);
    const currentHeight = (await chainResponse.json()).height;
    const expirationHeight = leaseHeight + LEASE_DURATION;
    console.log('\nCurrent chain height:', currentHeight);
    console.log('Lease expiration height:', expirationHeight);
    console.log('Blocks until expiration:',
        expirationHeight - currentHeight);

    // List the subnamespaces
    const owner = namespaceInfo.owner;
    const subnamespacesPath = '/account/namespace/page' +
        `?address=${owner}&parent=${NAMESPACE_NAME}`;
    console.log('\nFetching subnamespaces from', subnamespacesPath);
    const subnamespacesResponse =
        await fetch(`${NODE_URL}${subnamespacesPath}`);
    const subnamespaces = (await subnamespacesResponse.json()).data;
    console.log(`Subnamespaces of ${NAMESPACE_NAME}:`,
        subnamespaces.length);
    for (const subnamespace of subnamespaces)
        console.log(`  ${subnamespace.fqn}`);

    // List the mosaics defined under the namespace
    const mosaicsPath =
        `/namespace/mosaic/definition/page?namespace=${NAMESPACE_NAME}`;
    console.log('\nFetching mosaic definitions from', mosaicsPath);
    const mosaicsResponse = await fetch(`${NODE_URL}${mosaicsPath}`);
    const mosaics = (await mosaicsResponse.json()).data;
    console.log(`Mosaics defined under ${NAMESPACE_NAME}:`,
        mosaics.length);
    for (const entry of mosaics) {
        const mosaicId = entry.mosaic.id;
        console.log(`  ${mosaicId.namespaceId}:${mosaicId.name}`);
    }

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

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 NAMESPACE_NAME environment variable specifies which namespace to query, given as its full dot-separated name like foo or foo.bar. If not set, it defaults to company, a root namespace registered on testnet.

Code Explanation⚓︎

Fetching Namespace Information⚓︎

    # Fetch namespace information
    namespace_path = f'/namespace?namespace={NAMESPACE_NAME}'
    print(f'Fetching namespace information from {namespace_path}')
    with urllib.request.urlopen(f'{NODE_URL}{namespace_path}') as response:
        namespace_info = json.loads(response.read().decode())
        print('Namespace information:')
        print(f'  Name: {namespace_info["fqn"]}')
        print(f'  Owner: {namespace_info["owner"]}')
        lease_height = namespace_info['height']
        print(f'  Height: {lease_height}')
    // Fetch namespace information
    const namespacePath = `/namespace?namespace=${NAMESPACE_NAME}`;
    console.log('Fetching namespace information from', namespacePath);
    const namespaceResponse = await fetch(`${NODE_URL}${namespacePath}`);
    if (!namespaceResponse.ok)
        throw new Error(`HTTP error! status: ${namespaceResponse.status}`);

    const namespaceInfo = await namespaceResponse.json();
    console.log('Namespace information:');
    console.log('  Name:', namespaceInfo.fqn);
    console.log('  Owner:', namespaceInfo.owner);
    const leaseHeight = namespaceInfo.height;
    console.log('  Height:', leaseHeight);

The /namespace GET endpoint retrieves the current properties of a namespace, including:

  • Name: The complete dot-separated identifier of the namespace, from the root down to the queried level. For example, foo is a root namespace and foo.bar is a subnamespace of foo.

    fqn in the returned data structure stands for Fully-Qualified Name.

  • Owner: The address of the account that registered the namespace.

  • Height: The block height at which the current ownership began.

Computing the Lease Expiration⚓︎

    # Compute the lease expiration
    LEASE_DURATION = 525600  # approximately one year of blocks
    with urllib.request.urlopen(f'{NODE_URL}/chain/height') as response:
        current_height = json.loads(response.read().decode())['height']
    expiration_height = lease_height + LEASE_DURATION
    print(f'\nCurrent chain height: {current_height}')
    print(f'Lease expiration height: {expiration_height}')
    print(f'Blocks until expiration: {expiration_height - current_height}')
    // Compute the lease expiration
    const LEASE_DURATION = 525600; // approximately one year of blocks
    const chainResponse = await fetch(`${NODE_URL}/chain/height`);
    const currentHeight = (await chainResponse.json()).height;
    const expirationHeight = leaseHeight + LEASE_DURATION;
    console.log('\nCurrent chain height:', currentHeight);
    console.log('Lease expiration height:', expirationHeight);
    console.log('Blocks until expiration:',
        expirationHeight - currentHeight);

Namespaces are not owned permanently. A root namespace is leased for 525600 blocks (approximately one year) and must be renewed before it expires. Subnamespaces are not leased individually, as they expire together with their root namespace.

The expiration height is not part of the API response, but it can be derived by adding the lease duration to the namespace's height. Comparing it with the current chain height, returned by /chain/height GET, gives the number of blocks remaining before the namespace expires.

Listing Subnamespaces⚓︎

    # List the subnamespaces
    owner = namespace_info['owner']
    subnamespaces_path = (
        f'/account/namespace/page'
        f'?address={owner}&parent={NAMESPACE_NAME}')
    print(f'\nFetching subnamespaces from {subnamespaces_path}')
    with urllib.request.urlopen(
        f'{NODE_URL}{subnamespaces_path}'
    ) as response:
        subnamespaces = json.loads(response.read().decode())['data']
        print(f'Subnamespaces of {NAMESPACE_NAME}: {len(subnamespaces)}')
        for subnamespace in subnamespaces:
            print(f'  {subnamespace["fqn"]}')
    // List the subnamespaces
    const owner = namespaceInfo.owner;
    const subnamespacesPath = '/account/namespace/page' +
        `?address=${owner}&parent=${NAMESPACE_NAME}`;
    console.log('\nFetching subnamespaces from', subnamespacesPath);
    const subnamespacesResponse =
        await fetch(`${NODE_URL}${subnamespacesPath}`);
    const subnamespaces = (await subnamespacesResponse.json()).data;
    console.log(`Subnamespaces of ${NAMESPACE_NAME}:`,
        subnamespaces.length);
    for (const subnamespace of subnamespaces)
        console.log(`  ${subnamespace.fqn}`);

There is no endpoint that returns the children of a namespace directly. However, because subnamespaces always share the owner of their root namespace, they can be found by querying the namespaces owned by that account.

The /account/namespace/page GET endpoint returns the namespaces owned by an account, and its optional parent parameter restricts the results to subnamespaces of a given namespace. Using the namespace owner obtained in the previous step and the queried namespace as the parent value returns its subnamespaces.

Listing the Namespace's Mosaics⚓︎

    # List the mosaics defined under the namespace
    mosaics_path = (
        f'/namespace/mosaic/definition/page?namespace={NAMESPACE_NAME}')
    print(f'\nFetching mosaic definitions from {mosaics_path}')
    with urllib.request.urlopen(f'{NODE_URL}{mosaics_path}') as response:
        mosaics = json.loads(response.read().decode())['data']
        print(f'Mosaics defined under {NAMESPACE_NAME}: {len(mosaics)}')
        for entry in mosaics:
            mosaic_id = entry['mosaic']['id']
            print(f'  {mosaic_id["namespaceId"]}:{mosaic_id["name"]}')
    // List the mosaics defined under the namespace
    const mosaicsPath =
        `/namespace/mosaic/definition/page?namespace=${NAMESPACE_NAME}`;
    console.log('\nFetching mosaic definitions from', mosaicsPath);
    const mosaicsResponse = await fetch(`${NODE_URL}${mosaicsPath}`);
    const mosaics = (await mosaicsResponse.json()).data;
    console.log(`Mosaics defined under ${NAMESPACE_NAME}:`,
        mosaics.length);
    for (const entry of mosaics) {
        const mosaicId = entry.mosaic.id;
        console.log(`  ${mosaicId.namespaceId}:${mosaicId.name}`);
    }

Mosaics are always defined under a namespace, which acts as a prefix grouping related mosaics together.

The /namespace/mosaic/definition/page GET endpoint returns one definition for each mosaic whose namespace matches the queried name exactly. Mosaics defined under deeper subnamespaces (such as foo.bar:baz when querying foo) are not included. To list those as well, repeat this query for each subnamespace found in the previous step.

Output⚓︎

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

Using node http://libertalia.nemtest.net:7890
Namespace name: company
Fetching namespace information from /namespace?namespace=company
Namespace information:
  Name: company
  Owner: TBONKWCOWBZYZB2I5JD3LSDBQVBYHB757VN3SKPP
  Height: 625711

Current chain height: 655420
Lease expiration height: 1151311
Blocks until expiration: 495891

Fetching subnamespaces from /account/namespace/page?address=TBONKWCOWBZYZB2I5JD3LSDBQVBYHB757VN3SKPP&parent=company
Subnamespaces of company: 1
  company.division

Fetching mosaic definitions from /namespace/mosaic/definition/page?namespace=company
Mosaics defined under company: 1
  company:token

Some highlights from the output:

  • Namespace name (line 5): The queried namespace, company. Because it contains no dots, it is a root namespace.

  • Owner (line 6): The account that currently owns the namespace.

  • Height (line 7): The block height at which the current ownership period began.

  • Lease expiration (lines 9-11): The expiration height is the ownership height plus the lease duration of 525600 blocks. Subtracting the current chain height shows how many blocks remain before expiration.

  • Subnamespaces (lines 14-15): One subnamespace exists under company: company.division.

  • Mosaics (lines 18-19): One mosaic is defined directly under the namespace: company:token.

Conclusion⚓︎

This tutorial showed how to:

Step Related documentation
Fetch namespace properties /namespace GET
Compute the lease expiration /chain/height GET
List subnamespaces /account/namespace/page GET
List the namespace's mosaics /namespace/mosaic/definition/page GET