コンテンツにスキップ

モザイクを作成する⚓︎

中級

モザイク は、通貨、コレクティブル、アクセス権など、NEM ブロックチェーン上の資産を表します。 他のプラットフォームのトークンとは異なり、NEM のモザイクはプロトコルレベルで直接サポートされているため、利用に追加のコーディングは必要ありません。

モザイクのプロパティは設定可能で、単純な通貨から、供給量や転送ルールをカスタマイズしたトークンまで、さまざまな用途に対応できます。

すべてのモザイクは登録済みの ネームスペース に属します。 ネームスペースは、my_namespace:token のような 完全修飾名 の前半部分を提供します。 そのため、モザイクを作成する前にネームスペースを登録する必要があります。

このチュートリアルでは、既存のネームスペースの下にモザイクを作成し、初期プロパティを設定する方法を説明します。

前提条件⚓︎

始める前に、次の準備をしてください。

さらに、トランザクションのアナウンスと承認の方法を理解するため、XEM を送信する チュートリアルを確認してください。

完全なコード⚓︎

import json
import os
import time
import urllib.request

from symbolchain.CryptoTypes import PrivateKey
from symbolchain.facade.NemFacade import NemFacade
from symbolchain.nc import Amount
from symbolchain.nem.FeeCalculator import (
    calculate_mosaic_rental_fee,
    calculate_transaction_fee
)
from symbolchain.nem.Network import NetworkTimestamp

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

SIGNER_PRIVATE_KEY = os.getenv(
    'SIGNER_PRIVATE_KEY',
    '0000000000000000000000000000000000000000000000000000000000000000')
signer_key_pair = NemFacade.KeyPair(PrivateKey(SIGNER_PRIVATE_KEY))

facade = NemFacade('testnet')
signer_address = facade.network.public_key_to_address(
    signer_key_pair.public_key)
print(f'Signer address: {signer_address}')

try:
    # Fetch current network time
    time_path = '/time-sync/network-time'
    print(f'Fetching current network time from {time_path}')
    with urllib.request.urlopen(f'{NODE_URL}{time_path}') as response:
        response_json = json.loads(response.read().decode())
        network_time = response_json['receiveTimeStamp'] // 1000
        print(f'  Network time: {network_time} s since the nemesis block')

    # Derived fields from network time
    timestamp = NetworkTimestamp(network_time)
    deadline = timestamp.add_hours(2)

    # Build the mosaic ID
    namespace_name = os.getenv('NAMESPACE', 'my_namespace')
    mosaic_name = os.getenv('MOSAIC', f'token_{int(time.time())}')
    mosaic_id = f'{namespace_name}:{mosaic_name}'
    print(f'Creating mosaic: {mosaic_id}')

    # Define the mosaic
    mosaic_definition = {
        'owner_public_key': signer_key_pair.public_key,
        'id': {
            'namespace_id': {'name': namespace_name},
            'name': mosaic_name
        },
        'description': 'My tutorial mosaic',
        'properties': [
            {'property_': {
                'name': b'divisibility', 'value': b'2'}},
            {'property_': {
                'name': b'initialSupply', 'value': b'1000'}},
            {'property_': {
                'name': b'supplyMutable', 'value': b'true'}},
            {'property_': {
                'name': b'transferable', 'value': b'true'}}
        ]
    }

    # Build the mosaic definition transaction
    rental_fee = calculate_mosaic_rental_fee()
    print(f'  Mosaic creation fee: {rental_fee / 1_000_000} XEM')

    transaction = facade.transaction_factory.create({
        'type': 'mosaic_definition_transaction_v1',
        'signer_public_key': signer_key_pair.public_key,
        'timestamp': timestamp.timestamp,
        'deadline': deadline.timestamp,
        'rental_fee_sink': 'TBMOSAICOD4F54EE5CDMR23CCBGOAM2XSJBR5OLC',
        'rental_fee': rental_fee,
        'mosaic_definition': mosaic_definition
    })

    # Calculate and attach the transaction fee
    fee = calculate_transaction_fee(transaction)
    transaction.fee = Amount(fee)
    print(f'  Transaction fee: {fee / 1_000_000} XEM')

    # Sign and generate final payload
    signature = facade.sign_transaction(signer_key_pair, transaction)
    json_payload = facade.transaction_factory.attach_signature(
        transaction, signature)
    print('Built mosaic definition transaction:')
    print(json.dumps(transaction.to_json(), indent=2))

    # Announce the transaction
    announce_path = '/transaction/announce'
    print(f'Announcing mosaic definition to {announce_path}')
    announce_request = urllib.request.Request(
        f'{NODE_URL}{announce_path}',
        data=json_payload.encode(),
        headers={'Content-Type': 'application/json'},
        method='POST'
    )
    with urllib.request.urlopen(announce_request) as response:
        announce_result = json.loads(response.read().decode())
    print(f'  Result: {announce_result["message"]}')

    # Wait for confirmation
    if 'SUCCESS' == announce_result['message']:
        transaction_hash = facade.hash_transaction(transaction)
        status_path = f'/transaction/get?hash={transaction_hash}'
        print(f'Waiting for confirmation from {status_path}')
        is_confirmed = False
        for attempt in range(120):
            try:
                with urllib.request.urlopen(
                    f'{NODE_URL}{status_path}'
                ) as response:
                    confirmed = json.loads(response.read().decode())
                    height = confirmed['meta']['height']
                    print(f'Transaction confirmed in block {height}')
                    is_confirmed = True
                    break
            except urllib.error.HTTPError:
                print('  Transaction status: pending')
            time.sleep(1)
        if not is_confirmed:
            print('Confirmation took too long.')
    else:
        print(f'Transaction rejected: {announce_result["message"]}')

    # Retrieve the mosaic
    definition_path = f'/mosaic/definition?mosaicId={mosaic_id}'
    print(f'Fetching mosaic information from {definition_path}')
    with urllib.request.urlopen(
        f'{NODE_URL}{definition_path}'
    ) as response:
        mosaic_info = json.loads(response.read().decode())
        properties = {
            prop['name']: prop['value']
            for prop in mosaic_info['properties']
        }
        print('Mosaic information:')
        print(f'  Creator: {mosaic_info["creator"]}')
        print(f'  Divisibility: {properties["divisibility"]}')
        print(f'  Initial supply: {properties["initialSupply"]}')
        print(f'  Supply mutable: {properties["supplyMutable"]}')
        print(f'  Transferable: {properties["transferable"]}')

except urllib.error.URLError as e:
    print(e.reason)

Download source

import { PrivateKey } from 'symbol-sdk';
import {
    NemFacade,
    NetworkTimestamp,
    calculateMosaicRentalFee,
    calculateTransactionFee,
    models
} from 'symbol-sdk/nem';

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

const SIGNER_PRIVATE_KEY = process.env.SIGNER_PRIVATE_KEY ||
    '0000000000000000000000000000000000000000000000000000000000000000';
const signerKeyPair = new NemFacade.KeyPair(
    new PrivateKey(SIGNER_PRIVATE_KEY));

const facade = new NemFacade('testnet');
const signerAddress = facade.network.publicKeyToAddress(
    signerKeyPair.publicKey);
console.log('Signer address:', signerAddress.toString());

try {
    // Fetch current network time
    const timePath = '/time-sync/network-time';
    console.log('Fetching current network time from', timePath);
    const timeResponse = await fetch(`${NODE_URL}${timePath}`);
    const timeJSON = await timeResponse.json();
    const networkTime = Math.floor(timeJSON.receiveTimeStamp / 1000);
    console.log('  Network time:', networkTime,
        's since the nemesis block');

    // Derived fields from network time
    const timestamp = new NetworkTimestamp(networkTime);
    const deadline = timestamp.addHours(2);

    // Build the mosaic ID
    const namespaceName = process.env.NAMESPACE || 'my_namespace';
    const mosaicName = process.env.MOSAIC ||
        `token_${Math.floor(Date.now() / 1000)}`;
    const mosaicId = `${namespaceName}:${mosaicName}`;
    console.log('Creating mosaic:', mosaicId);

    // Define the mosaic
    const mosaicDefinition = {
        ownerPublicKey: signerKeyPair.publicKey.toString(),
        id: {
            namespaceId: { name: namespaceName },
            name: mosaicName
        },
        description: 'My tutorial mosaic',
        properties: [
            { property: { name: 'divisibility', value: '2' } },
            { property: { name: 'initialSupply', value: '1000' } },
            { property: { name: 'supplyMutable', value: 'true' } },
            { property: { name: 'transferable', value: 'true' } }
        ]
    };

    // Build the mosaic definition transaction
    const rentalFee = calculateMosaicRentalFee();
    console.log('  Mosaic creation fee:',
        `${Number(rentalFee) / 1_000_000} XEM`);

    const transaction = facade.transactionFactory.create({
        type: 'mosaic_definition_transaction_v1',
        signerPublicKey: signerKeyPair.publicKey.toString(),
        timestamp: timestamp.timestamp,
        deadline: deadline.timestamp,
        rentalFeeSink: 'TBMOSAICOD4F54EE5CDMR23CCBGOAM2XSJBR5OLC',
        rentalFee,
        mosaicDefinition
    });

    // Calculate and attach the transaction fee
    const fee = calculateTransactionFee(transaction);
    transaction.fee = new models.Amount(fee);
    console.log('  Transaction fee:', `${Number(fee) / 1_000_000} XEM`);

    // Sign and generate final payload
    const signature = facade.signTransaction(signerKeyPair, transaction);
    const jsonPayload = facade.transactionFactory.static.attachSignature(
        transaction, signature);
    console.log('Built mosaic definition transaction:');
    console.dir(transaction.toJson(), { colors: true });

    // Announce the transaction
    const announcePath = '/transaction/announce';
    console.log('Announcing mosaic definition to', announcePath);
    const announceResponse = await fetch(`${NODE_URL}${announcePath}`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: jsonPayload
    });
    const announceResult = await announceResponse.json();
    console.log('  Result:', announceResult.message);

    // Wait for confirmation
    if ('SUCCESS' === announceResult.message) {
        const transactionHash = facade.hashTransaction(transaction)
            .toString();
        const statusPath = `/transaction/get?hash=${transactionHash}`;
        console.log('Waiting for confirmation from', statusPath);

        let isConfirmed = false;
        for (let attempt = 1; 120 >= attempt; ++attempt) {
            const response = await fetch(`${NODE_URL}${statusPath}`);

            if (response.ok) {
                const confirmed = await response.json();
                console.log('Transaction confirmed in block',
                    confirmed.meta.height);
                isConfirmed = true;
                break;
            }
            console.log('  Transaction status: pending');
            await new Promise(resolve => { setTimeout(resolve, 1000); });
        }
        if (!isConfirmed)
            console.warn('Confirmation took too long.');
    } else {
        console.log('Transaction rejected:', announceResult.message);
    }

    // Retrieve the mosaic
    const definitionPath = `/mosaic/definition?mosaicId=${mosaicId}`;
    console.log('Fetching mosaic information from', definitionPath);
    const definitionResponse = await fetch(
        `${NODE_URL}${definitionPath}`);
    const mosaicInfo = await definitionResponse.json();
    const properties = Object.fromEntries(
        mosaicInfo.properties.map(prop => [prop.name, prop.value]));
    console.log('Mosaic information:');
    console.log('  Creator:', mosaicInfo.creator);
    console.log('  Divisibility:', properties.divisibility);
    console.log('  Initial supply:', properties.initialSupply);
    console.log('  Supply mutable:', properties.supplyMutable);
    console.log('  Transferable:', properties.transferable);

} catch (e) {
    console.error(e.message, '| Cause:', e.cause?.code ?? 'unknown');
}

Download source

コードの説明⚓︎

アカウントをセットアップする⚓︎

SIGNER_PRIVATE_KEY = os.getenv(
    'SIGNER_PRIVATE_KEY',
    '0000000000000000000000000000000000000000000000000000000000000000')
signer_key_pair = NemFacade.KeyPair(PrivateKey(SIGNER_PRIVATE_KEY))

facade = NemFacade('testnet')
signer_address = facade.network.public_key_to_address(
    signer_key_pair.public_key)
print(f'Signer address: {signer_address}')
const SIGNER_PRIVATE_KEY = process.env.SIGNER_PRIVATE_KEY ||
    '0000000000000000000000000000000000000000000000000000000000000000';
const signerKeyPair = new NemFacade.KeyPair(
    new PrivateKey(SIGNER_PRIVATE_KEY));

const facade = new NemFacade('testnet');
const signerAddress = facade.network.publicKeyToAddress(
    signerKeyPair.publicKey);
console.log('Signer address:', signerAddress.toString());

スニペットは、署名者の秘密鍵を SIGNER_PRIVATE_KEY 環境変数から読み込みます。 設定されていない場合は、テスト用のデフォルトキーを使用します。 署名者のアドレスは公開鍵から導出されます。 このアカウントが作成したモザイクを所有し、モザイクが属するネームスペースも所有していなければなりません。

ネットワーク時刻を取得する⚓︎

    # Fetch current network time
    time_path = '/time-sync/network-time'
    print(f'Fetching current network time from {time_path}')
    with urllib.request.urlopen(f'{NODE_URL}{time_path}') as response:
        response_json = json.loads(response.read().decode())
        network_time = response_json['receiveTimeStamp'] // 1000
        print(f'  Network time: {network_time} s since the nemesis block')

    # Derived fields from network time
    timestamp = NetworkTimestamp(network_time)
    deadline = timestamp.add_hours(2)
    // Fetch current network time
    const timePath = '/time-sync/network-time';
    console.log('Fetching current network time from', timePath);
    const timeResponse = await fetch(`${NODE_URL}${timePath}`);
    const timeJSON = await timeResponse.json();
    const networkTime = Math.floor(timeJSON.receiveTimeStamp / 1000);
    console.log('  Network time:', networkTime,
        's since the nemesis block');

    // Derived fields from network time
    const timestamp = new NetworkTimestamp(networkTime);
    const deadline = timestamp.addHours(2);

ネットワーク時刻は /time-sync/network-time GET から取得し、XEM を送信する チュートリアルで説明されている手順に従って、トランザクションの timestampdeadline フィールドを導出します。

モザイク名を設定する⚓︎

    # Build the mosaic ID
    namespace_name = os.getenv('NAMESPACE', 'my_namespace')
    mosaic_name = os.getenv('MOSAIC', f'token_{int(time.time())}')
    mosaic_id = f'{namespace_name}:{mosaic_name}'
    print(f'Creating mosaic: {mosaic_id}')
    // Build the mosaic ID
    const namespaceName = process.env.NAMESPACE || 'my_namespace';
    const mosaicName = process.env.MOSAIC ||
        `token_${Math.floor(Date.now() / 1000)}`;
    const mosaicId = `${namespaceName}:${mosaicName}`;
    console.log('Creating mosaic:', mosaicId);

モザイク ID は、既存のネームスペースとモザイク名から組み立てられます。 命名規則については、テキストブックの 名前 を参照してください。

チュートリアルを複数回実行したときの衝突を避けるため、モザイク名にタイムスタンプを追加します。 ただし、実際のプログラムでは、モザイクに固定名を使用します。 NAMESPACEMOSAIC 環境変数を使うと、チュートリアルで固定名を使用できます。

署名者が所有するネームスペースを使用してください

デフォルトでは、コードは SIGNER_PRIVATE_KEY が参照するテストアカウントと、my_namespace という名前のネームスペースを使用します。

ルートネームスペースを登録する チュートリアルから続けている場合は、SIGNER_PRIVATE_KEYNAMESPACE 環境変数を、そこで作成したアカウントとネームスペース、または署名者が所有する別のネームスペースに合わせて設定してください。

モザイクを定義する⚓︎

    # Define the mosaic
    mosaic_definition = {
        'owner_public_key': signer_key_pair.public_key,
        'id': {
            'namespace_id': {'name': namespace_name},
            'name': mosaic_name
        },
        'description': 'My tutorial mosaic',
        'properties': [
            {'property_': {
                'name': b'divisibility', 'value': b'2'}},
            {'property_': {
                'name': b'initialSupply', 'value': b'1000'}},
            {'property_': {
                'name': b'supplyMutable', 'value': b'true'}},
            {'property_': {
                'name': b'transferable', 'value': b'true'}}
        ]
    }
    // Define the mosaic
    const mosaicDefinition = {
        ownerPublicKey: signerKeyPair.publicKey.toString(),
        id: {
            namespaceId: { name: namespaceName },
            name: mosaicName
        },
        description: 'My tutorial mosaic',
        properties: [
            { property: { name: 'divisibility', value: '2' } },
            { property: { name: 'initialSupply', value: '1000' } },
            { property: { name: 'supplyMutable', value: 'true' } },
            { property: { name: 'transferable', value: 'true' } }
        ]
    };

モザイクの定義では、資産そのものを、それを登録するトランザクションとは区別して記述しています:

  • : モザイクを作成するアカウントの 公開鍵 と一致しなければなりません。 2 つが異なるトランザクションはネットワークに拒否されます。

  • : ネームスペースとモザイク名から作られるモザイク識別子。

  • : モザイクを 説明する テキスト。

  • : モザイクの動作を設定するキーと値の組。

    • : モザイクがサポートする小数桁数(可分性)。 例えば 2 は、1 全体単位を 100(102)原子単位に分割できることを意味します。 テキストブックの 可分性 を参照してください。
    • : モザイク定義時に作成者へ発行される全体単位の数。 テキストブックの 初期供給量 を参照してください。
    • : 作成後に総供給量を変更できるかどうか。 テキストブックの 供給量の可変性 を参照してください。
    • : 作成者以外の任意の 2 アカウント間でモザイクを送信できるかどうか。 テキストブックの 転送可能性 を参照してください。

    この例では、モザイクは小数点以下 2 桁まで分割可能で、1000.00 全体単位の供給量で始まります。 作成後に供給量を変更でき、アカウント間で自由に転送できます。

オプションの徴収手数料

モザイク定義には、オプションの 徴収手数料 も含められます。 詳細については、徴収手数料付きモザイクを作成する チュートリアルを参照してください。

モザイク定義トランザクションを構築する⚓︎

    # Build the mosaic definition transaction
    rental_fee = calculate_mosaic_rental_fee()
    print(f'  Mosaic creation fee: {rental_fee / 1_000_000} XEM')

    transaction = facade.transaction_factory.create({
        'type': 'mosaic_definition_transaction_v1',
        'signer_public_key': signer_key_pair.public_key,
        'timestamp': timestamp.timestamp,
        'deadline': deadline.timestamp,
        'rental_fee_sink': 'TBMOSAICOD4F54EE5CDMR23CCBGOAM2XSJBR5OLC',
        'rental_fee': rental_fee,
        'mosaic_definition': mosaic_definition
    })
    // Build the mosaic definition transaction
    const rentalFee = calculateMosaicRentalFee();
    console.log('  Mosaic creation fee:',
        `${Number(rentalFee) / 1_000_000} XEM`);

    const transaction = facade.transactionFactory.create({
        type: 'mosaic_definition_transaction_v1',
        signerPublicKey: signerKeyPair.publicKey.toString(),
        timestamp: timestamp.timestamp,
        deadline: deadline.timestamp,
        rentalFeeSink: 'TBMOSAICOD4F54EE5CDMR23CCBGOAM2XSJBR5OLC',
        rentalFee,
        mosaicDefinition
    });

モザイク定義トランザクションでは、次の項目を指定してネットワークにモザイクを登録します。

  • : モザイク定義トランザクションでは、タイプ MosaicDefinitionTransactionV1 を使用します。

  • : トランザクションに署名して手数料を支払うアカウント。モザイクが属するネームスペースの所有者でなければなりません。 作成したモザイクの所有者になります。

  • : ネットワーク時刻の手順で計算した値。

  • : モザイクの 作成手数料 を集める特別なアカウント。 各ネットワークには固定されたシンクアドレスがあります。

    ネットワークは、作成手数料を他のアドレスへ送るトランザクションを拒否します。

  • : 10 XEM の作成手数料。 SDK の ヘルパーは、必要な金額を返します。

    ネットワークは、この手数料を下回る金額を支払うトランザクションを拒否します。 より大きい金額は受け付けられますが、全額がシンクアカウントへ送られます。

  • : 前の手順で構築したモザイク定義。

    # Calculate and attach the transaction fee
    fee = calculate_transaction_fee(transaction)
    transaction.fee = Amount(fee)
    print(f'  Transaction fee: {fee / 1_000_000} XEM')
    // Calculate and attach the transaction fee
    const fee = calculateTransactionFee(transaction);
    transaction.fee = new models.Amount(fee);
    console.log('  Transaction fee:', `${Number(fee) / 1_000_000} XEM`);

最後に、 でトランザクション手数料を計算し、トランザクションに付加します。 作成手数料とは異なり、トランザクション手数料は ハーベスターアカウント に支払われます。 モザイク定義トランザクションの固定手数料は 0.15 XEM で、手数料表 に示されています。

モザイク定義を送信する⚓︎

    # Sign and generate final payload
    signature = facade.sign_transaction(signer_key_pair, transaction)
    json_payload = facade.transaction_factory.attach_signature(
        transaction, signature)
    print('Built mosaic definition transaction:')
    print(json.dumps(transaction.to_json(), indent=2))

    # Announce the transaction
    announce_path = '/transaction/announce'
    print(f'Announcing mosaic definition to {announce_path}')
    announce_request = urllib.request.Request(
        f'{NODE_URL}{announce_path}',
        data=json_payload.encode(),
        headers={'Content-Type': 'application/json'},
        method='POST'
    )
    with urllib.request.urlopen(announce_request) as response:
        announce_result = json.loads(response.read().decode())
    print(f'  Result: {announce_result["message"]}')
    // Sign and generate final payload
    const signature = facade.signTransaction(signerKeyPair, transaction);
    const jsonPayload = facade.transactionFactory.static.attachSignature(
        transaction, signature);
    console.log('Built mosaic definition transaction:');
    console.dir(transaction.toJson(), { colors: true });

    // Announce the transaction
    const announcePath = '/transaction/announce';
    console.log('Announcing mosaic definition to', announcePath);
    const announceResponse = await fetch(`${NODE_URL}${announcePath}`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: jsonPayload
    });
    const announceResult = await announceResponse.json();
    console.log('  Result:', announceResult.message);

XEM を送信する チュートリアルと同じ手順で、モザイク定義トランザクションに署名してアナウンスします。

    # Wait for confirmation
    if 'SUCCESS' == announce_result['message']:
        transaction_hash = facade.hash_transaction(transaction)
        status_path = f'/transaction/get?hash={transaction_hash}'
        print(f'Waiting for confirmation from {status_path}')
        is_confirmed = False
        for attempt in range(120):
            try:
                with urllib.request.urlopen(
                    f'{NODE_URL}{status_path}'
                ) as response:
                    confirmed = json.loads(response.read().decode())
                    height = confirmed['meta']['height']
                    print(f'Transaction confirmed in block {height}')
                    is_confirmed = True
                    break
            except urllib.error.HTTPError:
                print('  Transaction status: pending')
            time.sleep(1)
        if not is_confirmed:
            print('Confirmation took too long.')
    else:
        print(f'Transaction rejected: {announce_result["message"]}')
    // Wait for confirmation
    if ('SUCCESS' === announceResult.message) {
        const transactionHash = facade.hashTransaction(transaction)
            .toString();
        const statusPath = `/transaction/get?hash=${transactionHash}`;
        console.log('Waiting for confirmation from', statusPath);

        let isConfirmed = false;
        for (let attempt = 1; 120 >= attempt; ++attempt) {
            const response = await fetch(`${NODE_URL}${statusPath}`);

            if (response.ok) {
                const confirmed = await response.json();
                console.log('Transaction confirmed in block',
                    confirmed.meta.height);
                isConfirmed = true;
                break;
            }
            console.log('  Transaction status: pending');
            await new Promise(resolve => { setTimeout(resolve, 1000); });
        }
        if (!isConfirmed)
            console.warn('Confirmation took too long.');
    } else {
        console.log('Transaction rejected:', announceResult.message);
    }

次にコードは、トランザクションがブロックに含まれるまで /transaction/get GET エンドポイントをポーリングし、承認を待ちます。

モザイクを取得する⚓︎

    # Retrieve the mosaic
    definition_path = f'/mosaic/definition?mosaicId={mosaic_id}'
    print(f'Fetching mosaic information from {definition_path}')
    with urllib.request.urlopen(
        f'{NODE_URL}{definition_path}'
    ) as response:
        mosaic_info = json.loads(response.read().decode())
        properties = {
            prop['name']: prop['value']
            for prop in mosaic_info['properties']
        }
        print('Mosaic information:')
        print(f'  Creator: {mosaic_info["creator"]}')
        print(f'  Divisibility: {properties["divisibility"]}')
        print(f'  Initial supply: {properties["initialSupply"]}')
        print(f'  Supply mutable: {properties["supplyMutable"]}')
        print(f'  Transferable: {properties["transferable"]}')
    // Retrieve the mosaic
    const definitionPath = `/mosaic/definition?mosaicId=${mosaicId}`;
    console.log('Fetching mosaic information from', definitionPath);
    const definitionResponse = await fetch(
        `${NODE_URL}${definitionPath}`);
    const mosaicInfo = await definitionResponse.json();
    const properties = Object.fromEntries(
        mosaicInfo.properties.map(prop => [prop.name, prop.value]));
    console.log('Mosaic information:');
    console.log('  Creator:', mosaicInfo.creator);
    console.log('  Divisibility:', properties.divisibility);
    console.log('  Initial supply:', properties.initialSupply);
    console.log('  Supply mutable:', properties.supplyMutable);
    console.log('  Transferable:', properties.transferable);

モザイクが正常に作成されたことを確認するため、コードは /mosaic/definition GET エンドポイントから定義を取得し、そのプロパティを表示します。

レスポンスが成功すると、そのモザイクがネットワーク上に存在し、期待したプロパティを持っていることを確認できます。

モザイクの有効期間

モザイク自身に有効期間はなく、親ネームスペースの有効期限が切れると非アクティブになります。 ルートネームスペースを延長する と、モザイクが使用可能な状態を維持できます。 詳細については、テキストブックの 有効期間 を参照してください。

出力⚓︎

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

Using node http://libertalia.nemtest.net:7890
Signer address: TBONKWCOWBZYZB2I5JD3LSDBQVBYHB757VN3SKPP
Fetching current network time from /time-sync/network-time
  Network time: 356000940 s since the nemesis block
Creating mosaic: my_namespace:token_1783588525
  Mosaic creation fee: 10.0 XEM
  Transaction fee: 0.15 XEM
Built mosaic definition transaction:
{
  "type": 16385,
  "version": 1,
  "network": 152,
  "timestamp": 356000940,
  "signer_public_key": "462EE976890916E54FA825D26BDD0235F5EB5B6A143C199AB0AE5EE9328E08CE",
  "signature": "A874D12F42F1339F1DD86E1FF484C66711E24DEC215DD1279F59BC11B9E5BB65F1F912A6F498A1B7EFADFEFFE5815F8BE0C561D1DDC8952510176A3667C4850C",
  "fee": "150000",
  "deadline": 356008140,
  "mosaic_definition": {
    "owner_public_key": "462EE976890916E54FA825D26BDD0235F5EB5B6A143C199AB0AE5EE9328E08CE",
    "id": {
      "namespace_id": {
        "name": "6d795f6e616d657370616365"
      },
      "name": "746f6b656e5f31373833353838353235"
    },
    "description": "4d79207475746f7269616c206d6f73616963",
    "properties": [
      {
        "property": {
          "name": "64697669736962696c697479",
          "value": "32"
        }
      },
      {
        "property": {
          "name": "696e697469616c537570706c79",
          "value": "31303030"
        }
      },
      {
        "property": {
          "name": "737570706c794d757461626c65",
          "value": "74727565"
        }
      },
      {
        "property": {
          "name": "7472616e7366657261626c65",
          "value": "74727565"
        }
      }
    ]
  },
  "rental_fee_sink": "54424D4F534149434F443446353445453543444D523233434342474F414D3258534A4252354F4C43",
  "rental_fee": "10000000"
}
Announcing mosaic definition to /transaction/announce
  Result: SUCCESS
Waiting for confirmation from /transaction/get?hash=E52F569372C6079A7A0A35B40A503F68BAF6FBEE455B84182EDA7AEC9F0608C1
  Transaction status: pending
  Transaction status: pending
  Transaction status: pending
Transaction confirmed in block 693562
Fetching mosaic information from /mosaic/definition?mosaicId=my_namespace:token_1783588525
Mosaic information:
  Creator: 462ee976890916e54fa825d26bdd0235f5eb5b6a143c199ab0ae5ee9328e08ce
  Divisibility: 2
  Initial supply: 1000
  Supply mutable: true
  Transferable: true

出力の要点は次のとおりです。

  • モザイク ID(5 行目): モザイクは、ネームスペース my_namespace とタイムスタンプ付きモザイク名を組み合わせた完全修飾名で識別されます。 NEM テストネットエクスプローラー でこの名前を検索すると、モザイクの詳細を確認できます。

  • 作成手数料とトランザクション手数料(6~7 行目): 作成手数料は 10 XEM、トランザクション手数料は 0.15 XEM です。

  • 確認されたプロパティ(67~70 行目): ネットワークからモザイクを取得し、可分性、初期供給量 1000、供給量が可変で転送可能であることを確認します。

まとめ⚓︎

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

手順 関連ドキュメント
モザイクを定義する MosaicDefinitionTransactionV1
作成手数料を計算する
モザイクを取得する /mosaic/definition GET

次のステップ⚓︎

モザイクを作成できたので、次のことを行えます。