コンテンツにスキップ

モザイク情報の取得⚓︎

初級

Symbol上のすべてのモザイクには、供給量、可分性、振る舞いのフラグといったオンチェーンプロパティのセットがあります。

このチュートリアルでは、モザイクのプロパティと、それにリンクされているネームスペースのエイリアスを取得する方法を説明します。

前提条件⚓︎

このチュートリアルはネットワークからデータを読み取るだけです。アカウントは必要ありません。

開始する前に、開発環境のセットアップを済ませておいてください。

完全なコード⚓︎

このチュートリアルの完全なコード一覧を以下に示します。 詳細な手順ごとの説明は次のセクションで行います。

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'\nSupply in whole units: {formatted}')

    # Fetch namespace names linked to the mosaic
    print(f'\nFetching 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

このスニペットでは、 NODE_URL 環境変数を使用してSymbol APIノードを設定します。 値が指定されない場合は、デフォルトのテストネットノードが使用されます。

MOSAIC_ID 環境変数は、照会するモザイクを指定します。 設定されていない場合は、テストネット上のXYMのモザイク IDである 72C0212E67A08BCE がデフォルトで設定されます。

コード解説⚓︎

モザイク情報の取得⚓︎

    # 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());

/mosaics/{mosaicId} GET エンドポイントは、以下のモザイクの現在のプロパティを取得します。

  • Supply (供給量): 現在流通している絶対単位の総数。
  • Divisibility (可分性): モザイクがサポートする小数点以下の桁数。 例えば、XYMの可分性は 6 であり、1 XYMは1,000,000絶対単位に等しいことを意味します。
  • Flags (フラグ): モザイクの振る舞いの制限をエンコードしたビットマスク。 各フラグは単一のビットを占有します: supply_mutable(供給量変更可能) (1)、 transferable(譲渡可能) (2)、 restrictable(制限可能) (4)、 および revokable(取り消し可能) (8)。 複数のフラグは加算的に組み合わされます。例えば、値が 6 の場合は transferable (2) + restrictable (4) を意味します。
  • Duration (有効期限): モザイクが有効なままであるブロック数。 値が 0 の場合は、モザイクの有効期限が決して切れないことを意味します。
  • Start height (開始ブロック高): モザイクが作成されたブロックの高さ。
  • 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'\nSupply in whole units: {formatted}')
    // 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}`);
        // 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);

APIによって返される供給量の値は、絶対単位で表されます。 これを全体の単位に変換するために、コードはモザイクの可分性を使用して、供給量を整数部分と小数部分に分割します。

XYM(可分性 6 )の場合、 8325447775994408 絶対単位の供給量は 8325447775.994408 全体単位に等しくなります。

ネームスペースエイリアスの取得⚓︎

    # Fetch namespace names linked to the mosaic
    print(f'\nFetching 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");
            }
        }

モザイクは、人間が読めるネームスペースエイリアスにリンクさせることができます。 /namespaces/mosaic/names POST エンドポイントはモザイクIDを受け取り、現在それらにリンクされているネームスペース名を返します。

異なるネームスペースが同じモザイクにリンクしている場合、モザイクは複数のネームスペースエイリアスを持つことができます。 ネームスペースがリンクされていない場合、レスポンスはエイリアスが存在しないことを示します。

出力⚓︎

以下に示す出力は、テストネット上のXYMモザイクを照会する、プログラムの典型的な実行結果に対応しています。

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

出力の主なポイント:

  • Mosaic ID (5行目): テストネット上のXYMモザイク識別子( 72C0212E67A08BCE )。
  • Supply (6行目): 絶対単位での総供給量。
  • Divisibility (7行目): 値 6 は、1 XYM = 1,000,000 (106) 絶対単位であることを意味します。
  • Flags (8行目): 値 2 は transferable(譲渡可能)に解決され、XYMがアカウント間で自由に送信できることを意味します。
  • Duration (9行目): 値 0 は、XYMの有効期限が決して切れないことを意味します。
  • Supply in whole units (13行目): モザイクの可分性を使用して、絶対単位から全体単位に変換された供給量。
  • Namespace alias (16行目): モザイクは symbol.xym ネームスペースにリンクされています。

結論⚓︎

このチュートリアルでは、以下の方法を説明しました。

ステップ 関連ドキュメント
モザイクプロパティの取得 /mosaics/{mosaicId} GET
ネームスペースエイリアスの取得 /namespaces/mosaic/names POST

次のステップ⚓︎