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 ' \n Fetching 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 ' \n Supply 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
# 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
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
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:
Next Steps