BEGINNER
Every mosaic on Symbol has a set of on-chain properties such as supply, divisibility, and behavior flags.
This tutorial shows how to retrieve a mosaic's properties and any namespace aliases linked to 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
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
from symbolchain.sc import MosaicFlags
NODE_URL = os . getenv ( 'NODE_URL' , 'https://reference.symboltest.net:3001' )
print ( f 'Using node { NODE_URL } ' )
MOSAIC_ID = os . getenv ( 'MOSAIC_ID' , '72C0212E67A08BCE' )
print ( f 'Mosaic ID: { MOSAIC_ID } ' )
try :
# Fetch mosaic information
mosaic_path = f '/mosaics/ { 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 = response_json [ 'mosaic' ]
print ( 'Mosaic information:' )
print ( f ' Mosaic ID: { mosaic [ "id" ] } ' )
print ( f ' Supply: { mosaic [ "supply" ] } ' )
divisibility = mosaic [ 'divisibility' ]
print ( f ' Divisibility: { divisibility } ' )
flags = MosaicFlags ( mosaic [ 'flags' ])
print ( f ' Flags: { flags . value } ( { flags . name . lower () } )' )
print ( f ' Duration: { mosaic [ "duration" ] } ' )
print ( f ' Start height: { mosaic [ "startHeight" ] } ' )
print ( f ' Revision: { mosaic [ "revision" ] } ' )
# Display formatted supply
supply = int ( mosaic [ 'supply' ])
whole = supply // ( 10 ** divisibility )
fractional = supply % ( 10 ** divisibility )
formatted = f ' { whole } . { fractional : 0 { divisibility } d } '
print ( f ' \n Supply in whole units: { formatted } ' )
# Fetch namespace names linked to the mosaic
print ( f ' \n Fetching namespace names for mosaic { MOSAIC_ID } ' )
request_body = json . dumps ({ 'mosaicIds' : [ MOSAIC_ID ]}) . encode ()
request = urllib . request . Request (
f ' { NODE_URL } /namespaces/mosaic/names' ,
data = request_body ,
headers = { 'Content-Type' : 'application/json' }
)
with urllib . request . urlopen ( request ) as response :
names_info = json . loads ( response . read () . decode ())
for entry in names_info [ 'mosaicNames' ]:
names = entry [ 'names' ]
if names :
print ( f ' Namespace aliases: { ", " . join ( names ) } ' )
else :
print ( ' No namespace aliases linked' )
except Exception as e :
print ( e )
Download source
import { models } from 'symbol-sdk/symbol' ;
const NODE_URL = process . env . NODE_URL ||
'https://reference.symboltest.net:3001' ;
console . log ( 'Using node' , NODE_URL );
const MOSAIC_ID = process . env . MOSAIC_ID || '72C0212E67A08BCE' ;
console . log ( 'Mosaic ID:' , MOSAIC_ID );
try {
// Fetch mosaic information
const mosaicPath = `/mosaics/ ${ 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 mosaic = mosaicJSON . mosaic ;
console . log ( 'Mosaic information:' );
console . log ( ' Mosaic ID:' , mosaic . id );
console . log ( ' Supply:' , mosaic . supply );
const divisibility = mosaic . divisibility ;
console . log ( ' Divisibility:' , divisibility );
const flags = new models . MosaicFlags ( mosaic . flags );
const flagNames = flags . toString ()
. replace ( /MosaicFlags\./g , '' ). toLowerCase ();
console . log ( ` Flags: ${ mosaic . flags } ( ${ flagNames } )` );
console . log ( ' Duration:' , mosaic . duration );
console . log ( ' Start height:' , mosaic . startHeight );
console . log ( ' Revision:' , mosaic . revision );
// Display formatted supply
const supply = BigInt ( mosaic . supply );
const divisor = 10n ** BigInt ( divisibility );
const whole = supply / divisor ;
const fractional = supply % divisor ;
const fractionalStr = fractional . toString ()
. padStart ( divisibility , '0' );
console . log ( `\nSupply in whole units: ${ whole } . ${ fractionalStr } ` );
// Fetch namespace names linked to the mosaic
console . log ( `\nFetching namespace names for mosaic ${ MOSAIC_ID } ` );
const namesResponse = await fetch (
` ${ NODE_URL } /namespaces/mosaic/names` , {
method : 'POST' ,
headers : { 'Content-Type' : 'application/json' },
body : JSON . stringify ({ mosaicIds : [ MOSAIC_ID ] })
});
const namesInfo = await namesResponse . json ();
for ( const entry of namesInfo . mosaicNames ) {
if ( 0 < entry . names . length )
console . log ( ' Namespace aliases:' , entry . names . join ( ', ' ));
else
console . log ( ' No namespace aliases linked' );
}
} catch ( e ) {
console . error ( e . message );
}
Download source
//JAVA 21+
//DEPS org.symbol:symbol-sdk:3.3.1
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.HttpRequest.BodyPublishers ;
import java.net.http.HttpResponse ;
import java.net.http.HttpResponse.BodyHandlers ;
import java.util.ArrayList ;
import java.util.List ;
import com.fasterxml.jackson.databind.JsonNode ;
import com.fasterxml.jackson.databind.ObjectMapper ;
import com.fasterxml.jackson.databind.node.ArrayNode ;
import com.fasterxml.jackson.databind.node.ObjectNode ;
import org.symbol.sdk.symbol.models.MosaicFlags ;
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" , "https://reference.symboltest.net:3001" );
private static final String MOSAIC_ID = System . getenv (). getOrDefault (
"MOSAIC_ID" , "72C0212E67A08BCE" );
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 );
System . out . printf ( "Mosaic ID: %s%n" , MOSAIC_ID );
// Fetch mosaic information
final String mosaicPath = "/mosaics/" + MOSAIC_ID ;
System . out . printf ( "Fetching mosaic information from %s%n" ,
mosaicPath );
final HttpRequest request = HttpRequest . newBuilder (
URI . create ( NODE_URL + mosaicPath ))
. GET ()
. build ();
final HttpResponse < String > response = HTTP_CLIENT . send (
request , BodyHandlers . ofString ());
final JsonNode responseJson =
JSON_MAPPER . readTree ( response . body ());
final JsonNode mosaic = responseJson . get ( "mosaic" );
System . out . println ( "Mosaic information:" );
System . out . printf ( " Mosaic ID: %s%n" , mosaic . get ( "id" ). asText ());
System . out . printf ( " Supply: %s%n" , mosaic . get ( "supply" ). asText ());
final int divisibility = mosaic . get ( "divisibility" ). asInt ();
System . out . printf ( " Divisibility: %d%n" , divisibility );
final int flagsValue = mosaic . get ( "flags" ). asInt ();
final MosaicFlags flags = MosaicFlags . fromValue ( flagsValue );
final String flagNames = flags . toString ()
. replace ( "MosaicFlags." , "" )
. toLowerCase ();
System . out . printf ( " Flags: %d (%s)%n" , flagsValue , flagNames );
System . out . printf ( " Duration: %s%n" ,
mosaic . get ( "duration" ). asText ());
System . out . printf ( " Start height: %s%n" ,
mosaic . get ( "startHeight" ). asText ());
System . out . printf ( " Revision: %s%n" ,
mosaic . get ( "revision" ). asText ());
// Display formatted supply
final BigInteger supply = new BigInteger (
mosaic . get ( "supply" ). asText ());
final BigInteger divisor = BigInteger . TEN . pow ( divisibility );
final BigInteger [] parts = supply . divideAndRemainder ( divisor );
final String fractional = String . format (
"%0" + divisibility + "d" , parts [ 1 ] );
final String formatted =
String . format ( "%s.%s" , parts [ 0 ] , fractional );
System . out . printf ( "%nSupply in whole units: %s%n" , formatted );
// Fetch namespace names linked to the mosaic
System . out . printf ( "%nFetching namespace names for mosaic %s%n" ,
MOSAIC_ID );
final ObjectNode requestBody = JSON_MAPPER . createObjectNode ();
final ArrayNode mosaicIds = requestBody . putArray ( "mosaicIds" );
mosaicIds . add ( MOSAIC_ID );
final HttpRequest nsRequest = HttpRequest . newBuilder (
URI . create ( NODE_URL + "/namespaces/mosaic/names" ))
. header ( "Content-Type" , "application/json" )
. POST ( BodyPublishers . ofString ( requestBody . toString ()))
. build ();
final HttpResponse < String > nsResponse = HTTP_CLIENT . send (
nsRequest , BodyHandlers . ofString ());
final JsonNode namesInfo = JSON_MAPPER . readTree ( nsResponse . body ());
for ( final JsonNode entry : namesInfo . get ( "mosaicNames" )) {
final JsonNode names = entry . get ( "names" );
if ( ! names . isEmpty ()) {
final List < String > aliases = new ArrayList <> ();
for ( final JsonNode name : names )
aliases . add ( name . asText ());
System . out . printf ( " Namespace aliases: %s%n" ,
String . join ( ", " , aliases ));
} else {
System . out . println ( " No namespace aliases linked" );
}
}
}
}
Download source
The snippet uses the NODE_URL environment variable to set the Symbol API node.
If no value is provided, a default testnet node is used.
The MOSAIC_ID environment variable specifies which mosaic to query.
If not set, it defaults to the XYM mosaic ID on testnet (72C0212E67A08BCE).
Code Explanation
# Fetch mosaic information
mosaic_path = f '/mosaics/ { 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 = response_json [ 'mosaic' ]
print ( 'Mosaic information:' )
print ( f ' Mosaic ID: { mosaic [ "id" ] } ' )
print ( f ' Supply: { mosaic [ "supply" ] } ' )
divisibility = mosaic [ 'divisibility' ]
print ( f ' Divisibility: { divisibility } ' )
flags = MosaicFlags ( mosaic [ 'flags' ])
print ( f ' Flags: { flags . value } ( { flags . name . lower () } )' )
print ( f ' Duration: { mosaic [ "duration" ] } ' )
print ( f ' Start height: { mosaic [ "startHeight" ] } ' )
print ( f ' Revision: { mosaic [ "revision" ] } ' )
// Fetch mosaic information
const mosaicPath = `/mosaics/ ${ 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 mosaic = mosaicJSON . mosaic ;
console . log ( 'Mosaic information:' );
console . log ( ' Mosaic ID:' , mosaic . id );
console . log ( ' Supply:' , mosaic . supply );
const divisibility = mosaic . divisibility ;
console . log ( ' Divisibility:' , divisibility );
const flags = new models . MosaicFlags ( mosaic . flags );
const flagNames = flags . toString ()
. replace ( /MosaicFlags\./g , '' ). toLowerCase ();
console . log ( ` Flags: ${ mosaic . flags } ( ${ flagNames } )` );
console . log ( ' Duration:' , mosaic . duration );
console . log ( ' Start height:' , mosaic . startHeight );
console . log ( ' Revision:' , mosaic . revision );
// Fetch mosaic information
final String mosaicPath = "/mosaics/" + MOSAIC_ID ;
System . out . printf ( "Fetching mosaic information from %s%n" ,
mosaicPath );
final HttpRequest request = HttpRequest . newBuilder (
URI . create ( NODE_URL + mosaicPath ))
. GET ()
. build ();
final HttpResponse < String > response = HTTP_CLIENT . send (
request , BodyHandlers . ofString ());
final JsonNode responseJson =
JSON_MAPPER . readTree ( response . body ());
final JsonNode mosaic = responseJson . get ( "mosaic" );
System . out . println ( "Mosaic information:" );
System . out . printf ( " Mosaic ID: %s%n" , mosaic . get ( "id" ). asText ());
System . out . printf ( " Supply: %s%n" , mosaic . get ( "supply" ). asText ());
final int divisibility = mosaic . get ( "divisibility" ). asInt ();
System . out . printf ( " Divisibility: %d%n" , divisibility );
final int flagsValue = mosaic . get ( "flags" ). asInt ();
final MosaicFlags flags = MosaicFlags . fromValue ( flagsValue );
final String flagNames = flags . toString ()
. replace ( "MosaicFlags." , "" )
. toLowerCase ();
System . out . printf ( " Flags: %d (%s)%n" , flagsValue , flagNames );
System . out . printf ( " Duration: %s%n" ,
mosaic . get ( "duration" ). asText ());
System . out . printf ( " Start height: %s%n" ,
mosaic . get ( "startHeight" ). asText ());
System . out . printf ( " Revision: %s%n" ,
mosaic . get ( "revision" ). asText ());
The /mosaics/{mosaicId} GET endpoint retrieves the current properties of a mosaic, including:
Supply: The total number of atomic units currently in circulation.
Divisibility: The number of decimal places the mosaic supports.
For example, XYM has a divisibility of 6, meaning 1 XYM equals 1,000,000 atomic units.
Flags: A bitmask encoding the mosaic's behavior restrictions.
Each flag occupies a single bit:
supply_mutable (1),
transferable (2),
restrictable (4),
and revokable (8).
Multiple flags combine additively. For example, a value of 6 means transferable (2) + restrictable (4).
Duration: The number of blocks the mosaic remains active.
A value of 0 means the mosaic never expires.
Start height: The block height at which the mosaic was created.
Revision: Incremented each time the mosaic definition is modified.
The supply value returned by the API is expressed in atomic units.
To convert it to whole units, the code divides the supply into whole and fractional parts
using the mosaic's divisibility.
For XYM (divisibility 6), a supply of 8325447775994408 atomic units equals 8325447775.994408 whole units.
Fetching Namespace Aliases
# Fetch namespace names linked to the mosaic
print ( f ' \n Fetching namespace names for mosaic { MOSAIC_ID } ' )
request_body = json . dumps ({ 'mosaicIds' : [ MOSAIC_ID ]}) . encode ()
request = urllib . request . Request (
f ' { NODE_URL } /namespaces/mosaic/names' ,
data = request_body ,
headers = { 'Content-Type' : 'application/json' }
)
with urllib . request . urlopen ( request ) as response :
names_info = json . loads ( response . read () . decode ())
for entry in names_info [ 'mosaicNames' ]:
names = entry [ 'names' ]
if names :
print ( f ' Namespace aliases: { ", " . join ( names ) } ' )
else :
print ( ' No namespace aliases linked' )
// Fetch namespace names linked to the mosaic
console . log ( `\nFetching namespace names for mosaic ${ MOSAIC_ID } ` );
const namesResponse = await fetch (
` ${ NODE_URL } /namespaces/mosaic/names` , {
method : 'POST' ,
headers : { 'Content-Type' : 'application/json' },
body : JSON . stringify ({ mosaicIds : [ MOSAIC_ID ] })
});
const namesInfo = await namesResponse . json ();
for ( const entry of namesInfo . mosaicNames ) {
if ( 0 < entry . names . length )
console . log ( ' Namespace aliases:' , entry . names . join ( ', ' ));
else
console . log ( ' No namespace aliases linked' );
}
// Fetch namespace names linked to the mosaic
System . out . printf ( "%nFetching namespace names for mosaic %s%n" ,
MOSAIC_ID );
final ObjectNode requestBody = JSON_MAPPER . createObjectNode ();
final ArrayNode mosaicIds = requestBody . putArray ( "mosaicIds" );
mosaicIds . add ( MOSAIC_ID );
final HttpRequest nsRequest = HttpRequest . newBuilder (
URI . create ( NODE_URL + "/namespaces/mosaic/names" ))
. header ( "Content-Type" , "application/json" )
. POST ( BodyPublishers . ofString ( requestBody . toString ()))
. build ();
final HttpResponse < String > nsResponse = HTTP_CLIENT . send (
nsRequest , BodyHandlers . ofString ());
final JsonNode namesInfo = JSON_MAPPER . readTree ( nsResponse . body ());
for ( final JsonNode entry : namesInfo . get ( "mosaicNames" )) {
final JsonNode names = entry . get ( "names" );
if ( ! names . isEmpty ()) {
final List < String > aliases = new ArrayList <> ();
for ( final JsonNode name : names )
aliases . add ( name . asText ());
System . out . printf ( " Namespace aliases: %s%n" ,
String . join ( ", " , aliases ));
} else {
System . out . println ( " No namespace aliases linked" );
}
}
Mosaics can be linked to human-readable namespace aliases.
The /namespaces/mosaic/names POST endpoint accepts mosaic IDs and returns any namespace names currently linked to
them.
A mosaic can have multiple namespace aliases if different namespaces link to the same mosaic.
If no namespace is linked, the response indicates that no aliases exist.
Output
The output shown below corresponds to a typical run of the program, querying the XYM mosaic on testnet.
Using node https://reference.symboltest.net:3001
Mosaic ID: 72C0212E67A08BCE
Fetching mosaic information from /mosaics/72C0212E67A08BCE
Mosaic information:
Mosaic ID: 72C0212E67A08BCE
Supply: 8325492939035652
Divisibility: 6
Flags: 2 (transferable)
Duration: 0
Start height: 1
Revision: 1
Supply in whole units: 8325492939.035652
Fetching namespace names for mosaic 72C0212E67A08BCE
Namespace aliases: symbol.xym
Some highlights from the output:
Mosaic ID (line 5): The XYM mosaic identifier on testnet (72C0212E67A08BCE).
Supply (line 6): The total supply in atomic units.
Divisibility (line 7): The value 6 means 1 XYM = 1,000,000 (106 ) atomic units.
Flags (line 8): The value 2 resolves to transferable, meaning XYM can be freely sent between accounts.
Duration (line 9): The value 0 means XYM never expires.
Supply in whole units (line 13): The supply converted from atomic units to whole units using the mosaic's
divisibility.
Namespace alias (line 16): The mosaic is linked to the symbol.xym namespace.
Conclusion
This tutorial showed how to:
Next Steps