コンテンツにスキップ

アカウント残高を照会する⚓︎

初級

NEM の アカウント は、ネイティブ通貨である XEM を含む モザイク(代替可能トークン)を保有できます。

このチュートリアルでは、アカウントのモザイク残高を照会し、NEM の整数で表される 原子単位の数量 を小数形式で表示する方法を説明します。

前提条件⚓︎

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

完全なコード⚓︎

import json
import os
import urllib.request

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


def get_mosaic_balances(address):
    """
    Fetch all mosaic balances owned by an account.

    Args:
        address: The account address

    Returns:
        List of mosaics, each with a structured mosaicId and quantity
    """
    balances_path = f'/account/mosaic/owned?address={address}'
    with urllib.request.urlopen(f'{NODE_URL}{balances_path}') as response:
        balances_info = json.loads(response.read().decode())
        return balances_info['data']


def get_mosaic_definitions(address):
    """
    Fetch mosaic definitions for every mosaic owned by an account.

    Args:
        address: The account address

    Returns:
        Dictionary mapping "namespace:name" to the mosaic definition
    """
    definitions_path = '/account/mosaic/owned/definition'
    with urllib.request.urlopen(
        f'{NODE_URL}{definitions_path}?address={address}'
    ) as response:
        definitions_info = json.loads(response.read().decode())
        # Build a dictionary mapping "namespace:name" to its definition
        definitions_map = {}
        for entry in definitions_info['data']:
            entry_id = entry['id']
            entry_key = f'{entry_id["namespaceId"]}:{entry_id["name"]}'
            definitions_map[entry_key] = entry
        return definitions_map


def format_amount(amount, divisibility):
    """
    Format an atomic amount with decimal places.

    Args:
        amount: The atomic amount as an integer
        divisibility: Number of decimal places

    Returns:
        Formatted amount as a string
    """
    if divisibility == 0:
        return str(amount)
    whole_part = amount // (10 ** divisibility)
    fractional_part = amount % (10 ** divisibility)
    return f'{whole_part}.{fractional_part:0{divisibility}d}'


# The account address to query
ADDRESS = os.getenv('ADDRESS', 'TBONKWCOWBZYZB2I5JD3LSDBQVBYHB757VN3SKPP')
print(f'Fetching balances for {ADDRESS}')

try:
    # Fetch mosaic balances and definitions for the account
    account_mosaics = get_mosaic_balances(ADDRESS)
    mosaic_definitions = get_mosaic_definitions(ADDRESS)

    if not account_mosaics:
        print('Account holds no mosaics')
    else:
        print(f'Account holds {len(account_mosaics)} mosaic(s):')

        for mosaic_entry in account_mosaics:
            mosaic_id = mosaic_entry['mosaicId']
            key = f'{mosaic_id["namespaceId"]}:{mosaic_id["name"]}'
            balance = int(mosaic_entry['quantity'])

            # Get mosaic divisibility from the definition
            definition = mosaic_definitions[key]
            properties = {
                p['name']: p['value']
                for p in definition['properties']
            }
            mosaic_divisibility = int(
                properties.get('divisibility', '0'))

            # Format and display the balance
            formatted_balance = format_amount(
                balance, mosaic_divisibility)
            print(f'- Mosaic {key}')
            print(f'  Balance: {formatted_balance}')
            print(f'  Balance (atomic): {balance}')
            print(f'  Divisibility: {mosaic_divisibility}')
except urllib.error.URLError as e:
    print(e.reason)

Download source

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


/**
 * Fetch all mosaic balances owned by an account.
 * @param {string} address - Account address
 * @returns {Promise<object[]>} List of mosaics with id and quantity
 */
async function getMosaicBalances(address) {
    const path = `/account/mosaic/owned?address=${address}`;
    const response = await fetch(`${NODE_URL}${path}`);
    const info = await response.json();
    return info.data;
}

/**
 * Fetch mosaic definitions for every mosaic owned by an account.
 * @param {string} address - Account address
 * @returns {Promise<Map>} Map of "namespace:name" to mosaic definition
 */
async function getMosaicDefinitions(address) {
    const path = `/account/mosaic/owned/definition?address=${address}`;
    const response = await fetch(`${NODE_URL}${path}`);
    const info = await response.json();
    // Build a map from "namespace:name" to mosaic definition
    const definitionsMap = new Map();
    for (const entry of info.data) {
        const key = `${entry.id.namespaceId}:${entry.id.name}`;
        definitionsMap.set(key, entry);
    }
    return definitionsMap;
}

/**
 * Format an atomic amount with decimal places.
 * @param {bigint} amount - The atomic amount
 * @param {number} divisibility - Number of decimal places
 * @returns {string} The formatted amount
 */
function formatAmount(amount, divisibility) {
    if (0 === divisibility)
        return amount.toString();

    const divisor = 10n ** BigInt(divisibility);
    const wholePart = amount / divisor;
    const fractionalPart = amount % divisor;
    const fractionalStr = fractionalPart.toString()
        .padStart(divisibility, '0');
    return `${wholePart}.${fractionalStr}`;
}

// The account address to query
const ADDRESS = process.env.ADDRESS ||
    'TBONKWCOWBZYZB2I5JD3LSDBQVBYHB757VN3SKPP';
console.log('Fetching balances for', ADDRESS);

try {
    // Fetch mosaic balances and definitions for the account
    const accountMosaics = await getMosaicBalances(ADDRESS);
    const mosaicDefinitions = await getMosaicDefinitions(ADDRESS);

    if (0 === accountMosaics.length) {
        console.log('Account holds no mosaics');
    } else {
        console.log(`Account holds ${accountMosaics.length} mosaic(s):`);

        for (const mosaicEntry of accountMosaics) {
            const { mosaicId } = mosaicEntry;
            const key = `${mosaicId.namespaceId}:${mosaicId.name}`;
            const balance = BigInt(mosaicEntry.quantity);

            // Get mosaic divisibility from the definition
            const definition = mosaicDefinitions.get(key);
            const properties = Object.fromEntries(
                definition.properties.map(p => [p.name, p.value])
            );
            const divisibility = parseInt(
                properties.divisibility || '0', 10);

            // Format and display the balance
            const formattedBalance = formatAmount(balance, divisibility);
            console.log(`- Mosaic ${key}`);
            console.log(`  Balance: ${formattedBalance}`);
            console.log(`  Balance (atomic): ${balance.toString()}`);
            console.log(`  Divisibility: ${divisibility}`);
        }
    }
} catch (e) {
    console.error(e.message, '| Cause:', e.cause?.code ?? 'unknown');
}

Download source

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

チュートリアルでは、次の関数を定義します。

  • : アカウントが所有するすべての モザイク を取得します。
  • : 可分性 を含むモザイク定義を取得します。
  • : モザイクの 可分性 に応じた小数桁数で金額を整形します。

コードの説明⚓︎

モザイク残高を取得する⚓︎

def get_mosaic_balances(address):
    """
    Fetch all mosaic balances owned by an account.

    Args:
        address: The account address

    Returns:
        List of mosaics, each with a structured mosaicId and quantity
    """
    balances_path = f'/account/mosaic/owned?address={address}'
    with urllib.request.urlopen(f'{NODE_URL}{balances_path}') as response:
        balances_info = json.loads(response.read().decode())
        return balances_info['data']
/**
 * Fetch all mosaic balances owned by an account.
 * @param {string} address - Account address
 * @returns {Promise<object[]>} List of mosaics with id and quantity
 */
async function getMosaicBalances(address) {
    const path = `/account/mosaic/owned?address=${address}`;
    const response = await fetch(`${NODE_URL}${path}`);
    const info = await response.json();
    return info.data;
}

/account/mosaic/owned GET エンドポイントは、アカウントが保有するすべてのモザイクと、その数量を 原子単位 で返します。

モザイク定義を取得する⚓︎

def get_mosaic_definitions(address):
    """
    Fetch mosaic definitions for every mosaic owned by an account.

    Args:
        address: The account address

    Returns:
        Dictionary mapping "namespace:name" to the mosaic definition
    """
    definitions_path = '/account/mosaic/owned/definition'
    with urllib.request.urlopen(
        f'{NODE_URL}{definitions_path}?address={address}'
    ) as response:
        definitions_info = json.loads(response.read().decode())
        # Build a dictionary mapping "namespace:name" to its definition
        definitions_map = {}
        for entry in definitions_info['data']:
            entry_id = entry['id']
            entry_key = f'{entry_id["namespaceId"]}:{entry_id["name"]}'
            definitions_map[entry_key] = entry
        return definitions_map
/**
 * Fetch mosaic definitions for every mosaic owned by an account.
 * @param {string} address - Account address
 * @returns {Promise<Map>} Map of "namespace:name" to mosaic definition
 */
async function getMosaicDefinitions(address) {
    const path = `/account/mosaic/owned/definition?address=${address}`;
    const response = await fetch(`${NODE_URL}${path}`);
    const info = await response.json();
    // Build a map from "namespace:name" to mosaic definition
    const definitionsMap = new Map();
    for (const entry of info.data) {
        const key = `${entry.id.namespaceId}:${entry.id.name}`;
        definitionsMap.set(key, entry);
    }
    return definitionsMap;
}

モザイク残高を正しく整形するため、スニペットはネットワークからモザイク定義を取得します。 必要となる主なプロパティは 可分性 で、モザイクがサポートする小数桁数を定義します。

/account/mosaic/owned/definition GET エンドポイントは、アカウントが所有するすべてのモザイクの定義を、可分性やその他のプロパティとともに 1 回のリクエストで返します。

金額を整形する⚓︎

def format_amount(amount, divisibility):
    """
    Format an atomic amount with decimal places.

    Args:
        amount: The atomic amount as an integer
        divisibility: Number of decimal places

    Returns:
        Formatted amount as a string
    """
    if divisibility == 0:
        return str(amount)
    whole_part = amount // (10 ** divisibility)
    fractional_part = amount % (10 ** divisibility)
    return f'{whole_part}.{fractional_part:0{divisibility}d}'
/**
 * Format an atomic amount with decimal places.
 * @param {bigint} amount - The atomic amount
 * @param {number} divisibility - Number of decimal places
 * @returns {string} The formatted amount
 */
function formatAmount(amount, divisibility) {
    if (0 === divisibility)
        return amount.toString();

    const divisor = 10n ** BigInt(divisibility);
    const wholePart = amount / divisor;
    const fractionalPart = amount % divisor;
    const fractionalStr = fractionalPart.toString()
        .padStart(divisibility, '0');
    return `${wholePart}.${fractionalStr}`;
}

このユーティリティ関数は、原子単位 の数量を人が読みやすい形式に変換します。

  • 原子単位の数量: ブロックチェーンに保存される整数の生の値。
  • 整形済みの金額: モザイクの可分性によって決まる小数桁数を使った表示形式。

整形では、\(10^{\text{divisibility}}\) に対する除算と剰余によって、原子単位の数量を整数部分と小数部分に分けます。 その後、小数部分をゼロで埋め、常に正しい小数桁数で表示されるようにします。

すべてを組み合わせる⚓︎

# The account address to query
ADDRESS = os.getenv('ADDRESS', 'TBONKWCOWBZYZB2I5JD3LSDBQVBYHB757VN3SKPP')
print(f'Fetching balances for {ADDRESS}')

try:
    # Fetch mosaic balances and definitions for the account
    account_mosaics = get_mosaic_balances(ADDRESS)
    mosaic_definitions = get_mosaic_definitions(ADDRESS)

    if not account_mosaics:
        print('Account holds no mosaics')
    else:
        print(f'Account holds {len(account_mosaics)} mosaic(s):')

        for mosaic_entry in account_mosaics:
            mosaic_id = mosaic_entry['mosaicId']
            key = f'{mosaic_id["namespaceId"]}:{mosaic_id["name"]}'
            balance = int(mosaic_entry['quantity'])

            # Get mosaic divisibility from the definition
            definition = mosaic_definitions[key]
            properties = {
                p['name']: p['value']
                for p in definition['properties']
            }
            mosaic_divisibility = int(
                properties.get('divisibility', '0'))

            # Format and display the balance
            formatted_balance = format_amount(
                balance, mosaic_divisibility)
            print(f'- Mosaic {key}')
            print(f'  Balance: {formatted_balance}')
            print(f'  Balance (atomic): {balance}')
            print(f'  Divisibility: {mosaic_divisibility}')
except urllib.error.URLError as e:
    print(e.reason)
// The account address to query
const ADDRESS = process.env.ADDRESS ||
    'TBONKWCOWBZYZB2I5JD3LSDBQVBYHB757VN3SKPP';
console.log('Fetching balances for', ADDRESS);

try {
    // Fetch mosaic balances and definitions for the account
    const accountMosaics = await getMosaicBalances(ADDRESS);
    const mosaicDefinitions = await getMosaicDefinitions(ADDRESS);

    if (0 === accountMosaics.length) {
        console.log('Account holds no mosaics');
    } else {
        console.log(`Account holds ${accountMosaics.length} mosaic(s):`);

        for (const mosaicEntry of accountMosaics) {
            const { mosaicId } = mosaicEntry;
            const key = `${mosaicId.namespaceId}:${mosaicId.name}`;
            const balance = BigInt(mosaicEntry.quantity);

            // Get mosaic divisibility from the definition
            const definition = mosaicDefinitions.get(key);
            const properties = Object.fromEntries(
                definition.properties.map(p => [p.name, p.value])
            );
            const divisibility = parseInt(
                properties.divisibility || '0', 10);

            // Format and display the balance
            const formattedBalance = formatAmount(balance, divisibility);
            console.log(`- Mosaic ${key}`);
            console.log(`  Balance: ${formattedBalance}`);
            console.log(`  Balance (atomic): ${balance.toString()}`);
            console.log(`  Divisibility: ${divisibility}`);
        }
    }
} catch (e) {
    console.error(e.message, '| Cause:', e.cause?.code ?? 'unknown');
}

メインコードは ADDRESS 環境変数を読み込み、照会するアカウントを決定します。 値が指定されていない場合は、デフォルトのサンプルアドレスを使用します。

ヘルパー関数を組み合わせて、次の処理を行います。

  1. アカウントのモザイク残高を取得する。
  2. モザイク定義を取得して、それぞれの可分性を確認する。
  3. 各モザイクを反復処理し、適切な小数桁数で残高を整形する。

出力⚓︎

以下は、プログラムの実行時の出力例です。

Using node http://libertalia.nemtest.net:7890
Fetching balances for TBONKWCOWBZYZB2I5JD3LSDBQVBYHB757VN3SKPP
Account holds 2 mosaic(s):
- Mosaic nem:xem
  Balance: 9883.200000
  Balance (atomic): 9883200000
  Divisibility: 6
- Mosaic company:token
  Balance: 1000000
  Balance (atomic): 1000000
  Divisibility: 0

出力には、アカウントが保有するすべてのモザイクが表示されます。モザイクによって可分性の値が異なることに注目してください。

  • 1 つ目のモザイクは nem:xem で、ネットワークのネイティブ通貨です。可分性は 6 なので、小数点以下 6 桁(9883.200000)で表示されます。
  • 2 つ目のモザイクは company:token で、ユーザー定義モザイクです。可分性は 0 なので、整数(1000000)で表示されます。

まとめ⚓︎

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

手順 関連ドキュメント
モザイク残高を取得する /account/mosaic/owned GET
モザイク定義を取得する /account/mosaic/owned/definition GET