コンテンツにスキップ

通貨供給量を照会する⚓︎

初級

取引所や市場データアグリゲーターは、時価総額やトークン指標を表示するために、正確な供給量を必要とします。

NEM は、ネイティブ通貨である XEM の供給量を REST API で公開しています。 このチュートリアルでは、総供給量を照会し、そこから流通供給量を導出する方法を説明します。

前提条件⚓︎

このチュートリアルでは、SDK を必要とせずに NEM REST API を使用します。 HTTP リクエストを送信する方法だけが必要です。

完全なコード⚓︎

import json
import os
import urllib.request

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

try:
    MOSAIC_ID = 'nem:xem'
    supply_path = f'/mosaic/supply?mosaicId={MOSAIC_ID}'
    with urllib.request.urlopen(f'{NODE_URL}{supply_path}') as response:
        supply_info = json.loads(response.read().decode())
    total_supply = supply_info['supply']
    print(f'Total supply: {total_supply:,.6f} {MOSAIC_ID}')
    # Read the mosaic's divisibility to convert balances to whole units
    definition_path = f'/mosaic/definition?mosaicId={MOSAIC_ID}'
    with urllib.request.urlopen(
        f'{NODE_URL}{definition_path}'
    ) as response:
        definition = json.loads(response.read().decode())
    properties = {
        prop['name']: prop['value']
        for prop in definition['properties']
    }
    divisibility = int(properties['divisibility'])


    scale = 10 ** divisibility

    def fmt_atomic(atomic):
        return f'{atomic // scale:,}.{atomic % scale:0{divisibility}d}'

    NON_CIRCULATING_ADDRESSES = [
        ('Treasury', 'NCHESTYVD2P6P646AMY7WSNG73PCPZDUQNSD6JAK'),
        ('Nemesis', 'NANEMOABLAGR72AZ2RV3V4ZHDCXW25XQ73O7OBT5'),
        ('Namespace rental', 'NAMESPACEWH4MKFMBCVFERDPOOP4FK7MTBXDPZZA'),
        ('Mosaic rental', 'NBMOSAICOD4F54EE5CDMR23CCBGOAM2XSIUX6TRS'),
    ]
    non_circulating_supply = 0
    for label, address in NON_CIRCULATING_ADDRESSES:
        account_path = f'/account/get?address={address}'
        with urllib.request.urlopen(
            f'{NODE_URL}{account_path}'
        ) as response:
            account_info = json.loads(response.read().decode())
        balance = account_info['account']['balance']
        non_circulating_supply += balance
        print(f'  {label}: {fmt_atomic(balance)} {MOSAIC_ID}')
    print(
        f'Non-circulating supply: '
        f'{fmt_atomic(non_circulating_supply)} {MOSAIC_ID}')

    circulating_supply = total_supply * scale - non_circulating_supply
    print(f'Circulating supply: {fmt_atomic(circulating_supply)} {MOSAIC_ID}')
except Exception as error:
    print(error)

Download source

const NODE_URL = process.env.NODE_URL ||
    'http://portobelo.nemmain.net:7890';
console.log(`Using node ${NODE_URL}`);

try {
    const fmt = xem =>
        xem.toLocaleString('en-US', { minimumFractionDigits: 6 });

    const MOSAIC_ID = 'nem:xem';
    const supplyPath = `/mosaic/supply?mosaicId=${MOSAIC_ID}`;
    const response = await fetch(`${NODE_URL}${supplyPath}`);
    const supplyInfo = await response.json();
    const totalSupply = supplyInfo.supply;
    console.log(`Total supply: ${fmt(totalSupply)} ${MOSAIC_ID}`);
    // Read the mosaic's divisibility to convert balances to whole units
    const definitionPath = `/mosaic/definition?mosaicId=${MOSAIC_ID}`;
    const definitionResponse =
        await fetch(`${NODE_URL}${definitionPath}`);
    const definition = await definitionResponse.json();
    const properties = Object.fromEntries(
        definition.properties.map(
            property => [property.name, property.value]));
    const divisibility = parseInt(properties.divisibility, 10);


    const scale = 10n ** BigInt(divisibility);

    const fmtAtomic = atomic =>
        `${(atomic / scale).toLocaleString('en-US')}.` +
        `${(atomic % scale).toString().padStart(divisibility, '0')}`;

    const NON_CIRCULATING_ADDRESSES = [
        ['Treasury', 'NCHESTYVD2P6P646AMY7WSNG73PCPZDUQNSD6JAK'],
        ['Nemesis', 'NANEMOABLAGR72AZ2RV3V4ZHDCXW25XQ73O7OBT5'],
        ['Namespace rental', 'NAMESPACEWH4MKFMBCVFERDPOOP4FK7MTBXDPZZA'],
        ['Mosaic rental', 'NBMOSAICOD4F54EE5CDMR23CCBGOAM2XSIUX6TRS']
    ];
    let nonCirculatingSupply = 0n;
    for (const [label, address] of NON_CIRCULATING_ADDRESSES) {
        const accountPath = `/account/get?address=${address}`;
        const accountResponse = await fetch(`${NODE_URL}${accountPath}`);
        const accountInfo = await accountResponse.json();
        const balance = BigInt(accountInfo.account.balance);
        nonCirculatingSupply += balance;
        console.log(`  ${label}: ${fmtAtomic(balance)} ${MOSAIC_ID}`);
    }
    console.log(
        'Non-circulating supply: ' +
        `${fmtAtomic(nonCirculatingSupply)} ${MOSAIC_ID}`
    );

    const circulatingSupply =
        (BigInt(totalSupply) * scale) - nonCirculatingSupply;
    console.log(
        'Circulating supply: ' +
        `${fmtAtomic(circulatingSupply)} ${MOSAIC_ID}`
    );
} catch (error) {
    console.log(error);
}

Download source

スニペットでは、NODE_URL 環境変数を使って NEM メインネット ノードを指定します。

なぜメインネットなのか

他のチュートリアルは、通常、実際の資金を使わないように テストネット に対して実行します。 このチュートリアルは、流通供給量を計算するために固定されたメインネットアカウントのアドレスを照会する点で異なります。そのため、NODE_URL はメインネットノードを指す必要があります。

コードの説明⚓︎

総供給量を取得する⚓︎

    MOSAIC_ID = 'nem:xem'
    supply_path = f'/mosaic/supply?mosaicId={MOSAIC_ID}'
    with urllib.request.urlopen(f'{NODE_URL}{supply_path}') as response:
        supply_info = json.loads(response.read().decode())
    total_supply = supply_info['supply']
    print(f'Total supply: {total_supply:,.6f} {MOSAIC_ID}')
    const MOSAIC_ID = 'nem:xem';
    const supplyPath = `/mosaic/supply?mosaicId=${MOSAIC_ID}`;
    const response = await fetch(`${NODE_URL}${supplyPath}`);
    const supplyInfo = await response.json();
    const totalSupply = supplyInfo.supply;
    console.log(`Total supply: ${fmt(totalSupply)} ${MOSAIC_ID}`);

XEM の総供給量は固定されています。 すべての 8'999'999'999 XEM は ネメシスブロック で作成され、新しい XEM が発行されることはありません。

このチュートリアルでは、供給量や可分性などの値をハードコードせず API から読み取ります。 そのため、同じ方法を、供給量を変更できるものを含む他の モザイク にも使用できます。

コードは、XEM モザイク識別子 nem:xemmosaicId クエリパラメーターとして渡し、/mosaic/supply GET エンドポイントへ GET リクエストを送信します。

レスポンスは、モザイク識別子と現在の supply を含む JSON オブジェクトです。 supply全体単位 で表されます。

モザイクの可分性を読み取る⚓︎

    # Read the mosaic's divisibility to convert balances to whole units
    definition_path = f'/mosaic/definition?mosaicId={MOSAIC_ID}'
    with urllib.request.urlopen(
        f'{NODE_URL}{definition_path}'
    ) as response:
        definition = json.loads(response.read().decode())
    properties = {
        prop['name']: prop['value']
        for prop in definition['properties']
    }
    divisibility = int(properties['divisibility'])
    // Read the mosaic's divisibility to convert balances to whole units
    const definitionPath = `/mosaic/definition?mosaicId=${MOSAIC_ID}`;
    const definitionResponse =
        await fetch(`${NODE_URL}${definitionPath}`);
    const definition = await definitionResponse.json();
    const properties = Object.fromEntries(
        definition.properties.map(
            property => [property.name, property.value]));
    const divisibility = parseInt(properties.divisibility, 10);

前の手順で取得した供給量はすでに全体単位ですが、次の手順で読み取るアカウント残高は 原子単位 で報告されます。

値を同じ単位に変換するため、この手順ではまずモザイクの 可分性 を取得します。 その値を使って、残高を原子単位から全体単位へ変換します。

/mosaic/definition GET エンドポイントは、可分性を含むモザイク定義を返します。 nem:xem の可分性は 6 です。

非流通供給量を取得する⚓︎

    scale = 10 ** divisibility

    def fmt_atomic(atomic):
        return f'{atomic // scale:,}.{atomic % scale:0{divisibility}d}'

    NON_CIRCULATING_ADDRESSES = [
        ('Treasury', 'NCHESTYVD2P6P646AMY7WSNG73PCPZDUQNSD6JAK'),
        ('Nemesis', 'NANEMOABLAGR72AZ2RV3V4ZHDCXW25XQ73O7OBT5'),
        ('Namespace rental', 'NAMESPACEWH4MKFMBCVFERDPOOP4FK7MTBXDPZZA'),
        ('Mosaic rental', 'NBMOSAICOD4F54EE5CDMR23CCBGOAM2XSIUX6TRS'),
    ]
    non_circulating_supply = 0
    for label, address in NON_CIRCULATING_ADDRESSES:
        account_path = f'/account/get?address={address}'
        with urllib.request.urlopen(
            f'{NODE_URL}{account_path}'
        ) as response:
            account_info = json.loads(response.read().decode())
        balance = account_info['account']['balance']
        non_circulating_supply += balance
        print(f'  {label}: {fmt_atomic(balance)} {MOSAIC_ID}')
    print(
        f'Non-circulating supply: '
        f'{fmt_atomic(non_circulating_supply)} {MOSAIC_ID}')
    const scale = 10n ** BigInt(divisibility);

    const fmtAtomic = atomic =>
        `${(atomic / scale).toLocaleString('en-US')}.` +
        `${(atomic % scale).toString().padStart(divisibility, '0')}`;

    const NON_CIRCULATING_ADDRESSES = [
        ['Treasury', 'NCHESTYVD2P6P646AMY7WSNG73PCPZDUQNSD6JAK'],
        ['Nemesis', 'NANEMOABLAGR72AZ2RV3V4ZHDCXW25XQ73O7OBT5'],
        ['Namespace rental', 'NAMESPACEWH4MKFMBCVFERDPOOP4FK7MTBXDPZZA'],
        ['Mosaic rental', 'NBMOSAICOD4F54EE5CDMR23CCBGOAM2XSIUX6TRS']
    ];
    let nonCirculatingSupply = 0n;
    for (const [label, address] of NON_CIRCULATING_ADDRESSES) {
        const accountPath = `/account/get?address=${address}`;
        const accountResponse = await fetch(`${NODE_URL}${accountPath}`);
        const accountInfo = await accountResponse.json();
        const balance = BigInt(accountInfo.account.balance);
        nonCirculatingSupply += balance;
        console.log(`  ${label}: ${fmtAtomic(balance)} ${MOSAIC_ID}`);
    }
    console.log(
        'Non-circulating supply: ' +
        `${fmtAtomic(nonCirculatingSupply)} ${MOSAIC_ID}`
    );

総供給量の一部は、公開市場に含まれないアカウントによって保有されています。

  • トレジャリー: チームが管理する XEM を保有する準備金アカウント。
  • ネメシス: ネメシスブロックに署名したアカウント。 ネメシスブロックの後はトランザクションを送信できないため、このアカウントが保有する XEM は実質的に流通していません。
  • ネームスペースレンタルシンク: ネームスペース の登録に支払われた手数料を集めます。
  • モザイクレンタルシンク: モザイク の作成に支払われた手数料を集めます。

コードは /account/get GET エンドポイントで各アカウントを照会し、残高を合計します。

残高は原子単位で合計します。 表示するときだけ全 XEM に変換します。scalenem:xem では 1'000'000)で割った商が整数部分で、余りが小数点以下 6 桁になります。

これを通常の除算で行うと浮動小数点数になり、残高が非常に大きいため、最後の桁を誤る可能性があります。

流通供給量を導出する⚓︎

    circulating_supply = total_supply * scale - non_circulating_supply
    print(f'Circulating supply: {fmt_atomic(circulating_supply)} {MOSAIC_ID}')
    const circulatingSupply =
        (BigInt(totalSupply) * scale) - nonCirculatingSupply;
    console.log(
        'Circulating supply: ' +
        `${fmtAtomic(circulatingSupply)} ${MOSAIC_ID}`
    );

流通供給量は、総供給量から非流通残高を引いた値です。

これは、公開市場で自由に利用できる XEM の量です。

/mosaic/supply GET の総供給量は全体単位なので、コードは差し引く前に scale を掛けて原子単位に変換し、その結果を表示します。

出力⚓︎

以下の出力は、通貨供給量を照会した場合の実行例です。

1
2
3
4
5
6
7
8
Using node http://portobelo.nemmain.net:7890
Total supply: 8,999,999,999.000000 nem:xem
  Treasury: 1,414,609,948.972326 nem:xem
  Nemesis: 2,011,331.749000 nem:xem
  Namespace rental: 20,220.000000 nem:xem
  Mosaic rental: 1,950.000000 nem:xem
Non-circulating supply: 1,416,643,450.721326 nem:xem
Circulating supply: 7,583,356,548.278674 nem:xem

出力には、XEM 供給量の内訳が表示されます。

  • 総供給量(2 行目): 存在するすべての XEM。
  • 非流通供給量(7 行目): トレジャリー、ネメシス、レンタルシンクの残高の合計。
  • 流通供給量(8 行目): 実際に流通して利用できる XEM。

まとめ⚓︎

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

手順 関連ドキュメント
総供給量を取得する /mosaic/supply GET
モザイクの可分性を読み取る /mosaic/definition GET
非流通供給量を取得する /account/get GET

次のステップ⚓︎

特定のアカウントの XEM 残高を確認するには、アカウント残高を照会する チュートリアルを参照してください。