コンテンツにスキップ
中級

ネームスペースのアドレスへのリンクと解除⚓︎

ネームスペースアドレス にリンクさせることができます。これにより、トランザクション において長い16進数のアドレスの代わりに、人間が読み取り可能なエイリアス(別名)を使用できるようになります。

このチュートリアルでは、ネームスペースをアカウントのアドレスにリンクする方法と、不要になった際にリンクを解除する方法を説明します。

前提条件⚓︎

開始する前に、以下を確認してください。

さらに、トランザクションがどのようにアナウンスされ承認されるかを理解するために、転送トランザクション のチュートリアルを復習しておいてください。

ネームスペースの所有権が必要

ネームスペースをアドレスにリンクできるのは、そのネームスペースを所有しているアカウントのみです。 ターゲットとなるアドレスによる連署や承認は必要ありません。

完全なコード⚓︎

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

import json
import os
import time
import urllib.request

from symbolchain.CryptoTypes import PrivateKey
from symbolchain.facade.SymbolFacade import SymbolFacade
from symbolchain.symbol.Network import NetworkTimestamp
from symbolchain.symbol.IdGenerator import (
    generate_namespace_path, generate_mosaic_alias_id)
from symbolchain.sc import Amount, NamespaceId
from symbolchain.symbol.Network import Address

NODE_URL = os.getenv(
    'NODE_URL', 'https://reference.symboltest.net:3001')
print(f'Using node {NODE_URL}')

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

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

namespace_name = os.getenv('NAMESPACE_NAME', 'my_namespace')
print(f'Namespace name: {namespace_name}')

namespace_id = generate_namespace_path(namespace_name)[-1]
print(f'Namespace ID: {namespace_id} ({hex(namespace_id)})')

# Target address to link the namespace to
target_address = Address(
    os.getenv('TARGET_ADDRESS',
    'TCWYXKVYBMO4NBCUF3AXKJMXCGVSYQOS7ZG2TLI'))
print(f'Target address: {target_address}')

try:
    # Fetch current network time
    time_path = '/node/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())
        receive_timestamp = (
            response_json['communicationTimestamps']['receiveTimestamp'])
        timestamp = NetworkTimestamp(int(receive_timestamp))
        print(f'  Network time: {timestamp.timestamp} ms since nemesis')

    # Fetch recommended fees
    fee_path = '/network/fees/transaction'
    print(f'Fetching recommended fees from {fee_path}')
    with urllib.request.urlopen(f'{NODE_URL}{fee_path}') as response:
        response_json = json.loads(response.read().decode())
        median_mult = response_json['medianFeeMultiplier']
        minimum_mult = response_json['minFeeMultiplier']
        fee_mult = max(median_mult, minimum_mult)
        print(f'  Fee multiplier: {fee_mult}')

    # Build the alias transaction
    transaction = facade.transaction_factory.create({
        'type': 'address_alias_transaction_v1',
        'signer_public_key': signer_key_pair.public_key,
        'deadline': timestamp.add_hours(2).timestamp,
        'namespace_id': namespace_id,
        'address': target_address,
        'alias_action': 'link'
    })
    transaction.fee = Amount(fee_mult * transaction.size)

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

    transaction_hash = facade.hash_transaction(transaction)
    print(f'Transaction hash: {transaction_hash}')

    # Announce transaction
    print('Announcing address alias transaction to /transactions')
    request = urllib.request.Request(
        f'{NODE_URL}/transactions',
        data=json_payload.encode(),
        headers={'Content-Type': 'application/json'},
        method='PUT'
    )
    with urllib.request.urlopen(request) as response:
        print(f'  Response: {response.read().decode()}')

    # Wait for confirmation
    print('Waiting for transaction confirmation...')
    for attempt in range(60):
        time.sleep(1)
        try:
            status_url = (
                f'{NODE_URL}/transactionStatus/{transaction_hash}')
            with urllib.request.urlopen(status_url) as response:
                status = json.loads(response.read().decode())
                print(f'  Transaction status: {status["group"]}')
            if status['group'] == 'confirmed':
                print('Address alias transaction confirmed in',
                    attempt, 'seconds')
                break
            if status['group'] == 'failed':
                raise Exception('Address alias transaction failed:',
                    status['code'])
        except urllib.error.HTTPError:
            print('  Transaction status: unknown')

    # Retrieve the namespace to verify the alias
    namespace_path = f'/namespaces/{namespace_id:x}'
    print(f'Fetching namespace information from {namespace_path}')
    with urllib.request.urlopen(
            f'{NODE_URL}{namespace_path}') as response:
        response_json = json.loads(response.read().decode())
        namespace_info = response_json['namespace']
        print('Alias information:')
        alias_type = namespace_info['alias']['type']
        print(f'  Alias type: {alias_type}')
        if alias_type == 2:  # ADDRESS type
            aliased_address = Address.from_decoded_address_hex_string(
                namespace_info['alias']['address'])
            print(f'  Linked address: {aliased_address}')

    # Send a transfer using the alias instead of a raw address
    print(f'Using alias in transfer: {namespace_name}')

    # Encode the namespace ID as a recipient address
    recipient_id = generate_namespace_path(namespace_name)[-1]
    recipient_address = Address.from_namespace_id(
        NamespaceId(recipient_id), facade.network.identifier)

    transfer_tx = facade.transaction_factory.create({
        'type': 'transfer_transaction_v1',
        'signer_public_key': signer_key_pair.public_key,
        'deadline': timestamp.add_hours(2).timestamp,
        'recipient_address': recipient_address,
        'mosaics': [{
            'mosaic_id': generate_mosaic_alias_id('symbol.xym'),
            'amount': 1_000_000  # 1 XYM
        }]
    })
    print('Transfer transaction:')
    print(f'  Recipient address (alias): {recipient_address}')

except Exception as e:
    print(e)

Download source

import { PrivateKey } from 'symbol-sdk';
import {
    SymbolFacade,
    NetworkTimestamp,
    Address,
    models,
    generateNamespacePath,
    generateMosaicAliasId
} from 'symbol-sdk/symbol';

const NODE_URL = process.env.NODE_URL ||
    'https://reference.symboltest.net:3001';
console.log('Using node', NODE_URL);

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

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

const namespaceName = process.env.NAMESPACE_NAME || 'my_namespace';
console.log('Namespace name:', namespaceName);

const nsPath = generateNamespacePath(namespaceName);
const namespaceId = nsPath[nsPath.length - 1];
console.log(
    'Namespace ID:',
    `${namespaceId} (0x${namespaceId.toString(16)})`);

// Target address to link the namespace to
const targetAddress = new SymbolFacade.Address(
    process.env.TARGET_ADDRESS ||
    'TCWYXKVYBMO4NBCUF3AXKJMXCGVSYQOS7ZG2TLI');
console.log('Target address:', targetAddress.toString());

try {
    // Fetch current network time
    const timePath = '/node/time';
    console.log('Fetching current network time from', timePath);
    const timeResponse = await fetch(`${NODE_URL}${timePath}`);
    const timeJSON = await timeResponse.json();
    const receiveTimestamp =
        timeJSON.communicationTimestamps.receiveTimestamp;
    const timestamp = new NetworkTimestamp(receiveTimestamp);
    console.log('  Network time:', timestamp.timestamp,
        'ms since nemesis');

    // Fetch recommended fees
    const feePath = '/network/fees/transaction';
    console.log('Fetching recommended fees from', feePath);
    const feeResponse = await fetch(`${NODE_URL}${feePath}`);
    const feeJSON = await feeResponse.json();
    const medianMult = feeJSON.medianFeeMultiplier;
    const minimumMult = feeJSON.minFeeMultiplier;
    const feeMult = Math.max(medianMult, minimumMult);
    console.log('  Fee multiplier:', feeMult);

    // Build the alias transaction
    const transaction = facade.transactionFactory.create({
        type: 'address_alias_transaction_v1',
        signerPublicKey: signerKeyPair.publicKey.toString(),
        deadline: timestamp.addHours(2).timestamp,
        namespaceId: namespaceId,
        address: targetAddress,
        aliasAction: 'link'
    });
    transaction.fee = new models.Amount(feeMult * transaction.size);

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

    const transactionHash =
        facade.hashTransaction(transaction).toString();
    console.log('Transaction hash:', transactionHash);

    // Announce transaction
    console.log('Announcing address alias transaction to /transactions');
    const announceResponse = await fetch(`${NODE_URL}/transactions`, {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: jsonPayload
    });
    console.log('  Response:', await announceResponse.text());

    // Wait for confirmation
    console.log('Waiting for transaction confirmation...');
    for (let attempt = 0; attempt < 60; attempt++) {
        await new Promise(resolve => setTimeout(resolve, 1000));

        try {
            const statusUrl =
                `${NODE_URL}/transactionStatus/${transactionHash}`;
            const statusResponse = await fetch(statusUrl);

            if (!statusResponse.ok) {
                console.log('  Transaction status: unknown');
                continue;
            }

            const status = await statusResponse.json();
            console.log('  Transaction status:', status.group);

            if (status.group === 'confirmed') {
                console.log('Address alias transaction confirmed in',
                    attempt, 'seconds');
                break;
            }

            if (status.group === 'failed') {
                throw new Error(
                    `Address alias transaction failed: ${status.code}`);
            }
        } catch (error) {
            if (error.message.includes('failed:')) {
                throw error;
            }
            console.log('  Transaction status: unknown');
        }
    }

    // Retrieve the namespace to verify the alias
    const namespacePath =
        `/namespaces/${namespaceId.toString(16)}`;
    console.log(
        'Fetching namespace information from', namespacePath);
    const namespaceResponse =
        await fetch(`${NODE_URL}${namespacePath}`);
    const namespaceJSON = await namespaceResponse.json();
    const namespaceInfo = namespaceJSON.namespace;
    console.log('Alias information:');
    console.log('  Alias type:', namespaceInfo.alias.type);
    if (namespaceInfo.alias.type === 2) { // ADDRESS type
        const aliasedAddress = Address.fromDecodedAddressHexString(
            namespaceInfo.alias.address);
        console.log('  Linked address:', aliasedAddress.toString());
    }

    // Send a transfer using the alias instead of a raw address
    console.log('Using alias in transfer:', namespaceName);

    // Encode the namespace ID as a recipient address
    const recipientPath = generateNamespacePath(namespaceName);
    const recipientId = recipientPath[recipientPath.length - 1];
    const recipientAddress = Address.fromNamespaceId(
        new models.NamespaceId(recipientId), facade.network.identifier);

    const transferTx = facade.transactionFactory.create({
        type: 'transfer_transaction_v1',
        signerPublicKey: signerKeyPair.publicKey.toString(),
        deadline: timestamp.addHours(2).timestamp,
        recipientAddress: recipientAddress,
        mosaics: [{
            mosaicId: generateMosaicAliasId('symbol.xym'),
            amount: 1_000_000n // 1 XYM
        }]
    });
    console.log('Transfer transaction:');
    console.log('  Recipient address (alias):',
        recipientAddress.toString());
} catch (e) {
    console.error(e.message);
}

Download source

コード解説⚓︎

アカウントの設定⚓︎

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

facade = SymbolFacade('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 SymbolFacade.KeyPair(
    new PrivateKey(SIGNER_PRIVATE_KEY));

const facade = new SymbolFacade('testnet');
const signerAddress = facade.network.publicKeyToAddress(
    signerKeyPair.publicKey);

このスニペットは、署名者の秘密鍵を SIGNER_PRIVATE_KEY 環境変数から読み取ります。設定されていない場合はデフォルトのテストキーが使用されます。 署名者のアドレスは公開鍵から派生します。 このアカウントは、リンク対象のネームスペースを所有している必要があります。

ネームスペースとターゲットアドレスの定義⚓︎

namespace_name = os.getenv('NAMESPACE_NAME', 'my_namespace')
print(f'Namespace name: {namespace_name}')

namespace_id = generate_namespace_path(namespace_name)[-1]
print(f'Namespace ID: {namespace_id} ({hex(namespace_id)})')

# Target address to link the namespace to
target_address = Address(
    os.getenv('TARGET_ADDRESS',
    'TCWYXKVYBMO4NBCUF3AXKJMXCGVSYQOS7ZG2TLI'))
print(f'Target address: {target_address}')
const namespaceName = process.env.NAMESPACE_NAME || 'my_namespace';
console.log('Namespace name:', namespaceName);

const nsPath = generateNamespacePath(namespaceName);
const namespaceId = nsPath[nsPath.length - 1];
console.log(
    'Namespace ID:',
    `${namespaceId} (0x${namespaceId.toString(16)})`);

// Target address to link the namespace to
const targetAddress = new SymbolFacade.Address(
    process.env.TARGET_ADDRESS ||
    'TCWYXKVYBMO4NBCUF3AXKJMXCGVSYQOS7ZG2TLI');

コードでは以下を定義しています。

  • ネームスペース名: リンクするネームスペース。 NAMESPACE_NAME 環境変数から読み込まれ、設定されていない場合は my_namespace がデフォルトとなります。 この名前は、自身のアカウントがすでに所有しているネームスペースと一致している必要があります。
  • ネームスペース ID: ID は、 を使用してネームスペース名から生成されます。 これは階層内の各レベルの ID 配列を返します。 最終的なネームスペース ID を取得するために、最後の要素が選択されます。 最後の要素を取得する方法は、ルートネームスペースとサブネームスペースの両方で機能します。

    foo のようなルートネームスペースの場合、配列には要素が1つ含まれます。 symbol.xym のようなサブネームスペースの場合、要素は2つ含まれ、最後の要素が symbol 配下の xym の ID となります。

    サブネームスペース ID は一意

    サブネームスペース ID は階層的に派生するため、末尾の名前が同じでも親が異なる2つのサブネームスペースは、異なる ID を生成します。 例えば、 foo.xymbar.xym のパスの最後の要素は異なります。

  • ターゲットアドレス: ネームスペースが指し示す先のアドレス。 TARGET_ADDRESS 環境変数から読み込まれます。 設定されていない場合は、デフォルトのテストアドレスが使用されます。

ネットワーク時間と手数料の取得⚓︎

    # Fetch current network time
    time_path = '/node/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())
        receive_timestamp = (
            response_json['communicationTimestamps']['receiveTimestamp'])
        timestamp = NetworkTimestamp(int(receive_timestamp))
        print(f'  Network time: {timestamp.timestamp} ms since nemesis')

    # Fetch recommended fees
    fee_path = '/network/fees/transaction'
    print(f'Fetching recommended fees from {fee_path}')
    with urllib.request.urlopen(f'{NODE_URL}{fee_path}') as response:
        response_json = json.loads(response.read().decode())
        median_mult = response_json['medianFeeMultiplier']
        minimum_mult = response_json['minFeeMultiplier']
        fee_mult = max(median_mult, minimum_mult)
        print(f'  Fee multiplier: {fee_mult}')
try {
    // Fetch current network time
    const timePath = '/node/time';
    console.log('Fetching current network time from', timePath);
    const timeResponse = await fetch(`${NODE_URL}${timePath}`);
    const timeJSON = await timeResponse.json();
    const receiveTimestamp =
        timeJSON.communicationTimestamps.receiveTimestamp;
    const timestamp = new NetworkTimestamp(receiveTimestamp);
    console.log('  Network time:', timestamp.timestamp,
        'ms since nemesis');

    // Fetch recommended fees
    const feePath = '/network/fees/transaction';
    console.log('Fetching recommended fees from', feePath);
    const feeResponse = await fetch(`${NODE_URL}${feePath}`);
    const feeJSON = await feeResponse.json();
    const medianMult = feeJSON.medianFeeMultiplier;
    const minimumMult = feeJSON.minFeeMultiplier;
    const feeMult = Math.max(medianMult, minimumMult);

転送トランザクション チュートリアルで説明されているプロセスに従い、ネットワーク時間と推奨手数料をそれぞれ /node/time GET および /network/fees/transaction GET から取得します。

トランザクションの構築⚓︎

    # Build the alias transaction
    transaction = facade.transaction_factory.create({
        'type': 'address_alias_transaction_v1',
        'signer_public_key': signer_key_pair.public_key,
        'deadline': timestamp.add_hours(2).timestamp,
        'namespace_id': namespace_id,
        'address': target_address,
        'alias_action': 'link'
    })
    transaction.fee = Amount(fee_mult * transaction.size)
    // Build the alias transaction
    const transaction = facade.transactionFactory.create({
        type: 'address_alias_transaction_v1',
        signerPublicKey: signerKeyPair.publicKey.toString(),
        deadline: timestamp.addHours(2).timestamp,
        namespaceId: namespaceId,
        address: targetAddress,
        aliasAction: 'link'
    });

アドレスエイリアストランザクションでは以下を指定します。

  • Type: アドレスエイリアストランザクションにはタイプ address_alias_transaction_v1 を使用します。

  • 署名者の公開鍵: ネームスペースを所有し、トランザクション手数料を支払うアカウント。

  • ネームスペース ID: リンクされるネームスペースの識別子。

  • アドレス: ネームスペースにリンクするターゲットアドレス。

  • エイリアスアクション: link という値はエイリアスを作成します。後にエイリアスを削除するには、代わりに unlink を使用します。

エイリアスのリンク解除

ネームスペースのアドレスへのリンクを解除するには、同じネームスペース ID とアドレスを指定し、 alias_action フィールドを unlink に設定した別の address_alias_transaction_v1 トランザクションをアナウンスします。

リンク解除プロセスによってネームスペース自体が削除されるわけではなく、ネームスペースとアドレスの関連付けのみが削除されます。 リンク解除後、そのネームスペースは別のアドレスや モザイク にリンクさせることができます。

トランザクションの送信⚓︎

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

    transaction_hash = facade.hash_transaction(transaction)
    print(f'Transaction hash: {transaction_hash}')

    # Announce transaction
    print('Announcing address alias transaction to /transactions')
    request = urllib.request.Request(
        f'{NODE_URL}/transactions',
        data=json_payload.encode(),
        headers={'Content-Type': 'application/json'},
        method='PUT'
    )
    with urllib.request.urlopen(request) as response:
        print(f'  Response: {response.read().decode()}')
    // Sign transaction and generate final payload
    const signature = facade.signTransaction(
        signerKeyPair, transaction);
    const jsonPayload =
        facade.transactionFactory.static.attachSignature(
            transaction, signature);
    console.log('Built transaction:');
    console.dir(transaction.toJson(), { colors: true });

    const transactionHash =
        facade.hashTransaction(transaction).toString();
    console.log('Transaction hash:', transactionHash);

    // Announce transaction
    console.log('Announcing address alias transaction to /transactions');
    const announceResponse = await fetch(`${NODE_URL}/transactions`, {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: jsonPayload
    });

トランザクションは、転送トランザクションの作成 と同じプロセスに従って署名され、アナウンスされます。

    # Wait for confirmation
    print('Waiting for transaction confirmation...')
    for attempt in range(60):
        time.sleep(1)
        try:
            status_url = (
                f'{NODE_URL}/transactionStatus/{transaction_hash}')
            with urllib.request.urlopen(status_url) as response:
                status = json.loads(response.read().decode())
                print(f'  Transaction status: {status["group"]}')
            if status['group'] == 'confirmed':
                print('Address alias transaction confirmed in',
                    attempt, 'seconds')
                break
            if status['group'] == 'failed':
                raise Exception('Address alias transaction failed:',
                    status['code'])
        except urllib.error.HTTPError:
            print('  Transaction status: unknown')
    // Wait for confirmation
    console.log('Waiting for transaction confirmation...');
    for (let attempt = 0; attempt < 60; attempt++) {
        await new Promise(resolve => setTimeout(resolve, 1000));

        try {
            const statusUrl =
                `${NODE_URL}/transactionStatus/${transactionHash}`;
            const statusResponse = await fetch(statusUrl);

            if (!statusResponse.ok) {
                console.log('  Transaction status: unknown');
                continue;
            }

            const status = await statusResponse.json();
            console.log('  Transaction status:', status.group);

            if (status.group === 'confirmed') {
                console.log('Address alias transaction confirmed in',
                    attempt, 'seconds');
                break;
            }

            if (status.group === 'failed') {
                throw new Error(
                    `Address alias transaction failed: ${status.code}`);
            }
        } catch (error) {
            if (error.message.includes('failed:')) {
                throw error;
            }
            console.log('  Transaction status: unknown');
        }

コードはその後、ステータスが confirmed に変わるまで /transactionStatus/{hash} GET エンドポイントをポーリングして、トランザクションが承認されるのを待ちます。

エイリアスの検証⚓︎

    # Retrieve the namespace to verify the alias
    namespace_path = f'/namespaces/{namespace_id:x}'
    print(f'Fetching namespace information from {namespace_path}')
    with urllib.request.urlopen(
            f'{NODE_URL}{namespace_path}') as response:
        response_json = json.loads(response.read().decode())
        namespace_info = response_json['namespace']
        print('Alias information:')
        alias_type = namespace_info['alias']['type']
        print(f'  Alias type: {alias_type}')
        if alias_type == 2:  # ADDRESS type
            aliased_address = Address.from_decoded_address_hex_string(
                namespace_info['alias']['address'])
            print(f'  Linked address: {aliased_address}')
    // Retrieve the namespace to verify the alias
    const namespacePath =
        `/namespaces/${namespaceId.toString(16)}`;
    console.log(
        'Fetching namespace information from', namespacePath);
    const namespaceResponse =
        await fetch(`${NODE_URL}${namespacePath}`);
    const namespaceJSON = await namespaceResponse.json();
    const namespaceInfo = namespaceJSON.namespace;
    console.log('Alias information:');
    console.log('  Alias type:', namespaceInfo.alias.type);
    if (namespaceInfo.alias.type === 2) { // ADDRESS type
        const aliasedAddress = Address.fromDecodedAddressHexString(
            namespaceInfo.alias.address);
        console.log('  Linked address:', aliasedAddress.toString());
    }

エイリアスが作成されたことを確認するために、コードは /namespaces/{namespaceId} GET エンドポイントを使用してネットワークからネームスペース情報を取得します。

レスポンスにはエイリアスタイプ( address )とリンクされたアドレスが含まれ、ネームスペースが指定したアドレスを指していることが確認されます。

エイリアスの使用⚓︎

    # Send a transfer using the alias instead of a raw address
    print(f'Using alias in transfer: {namespace_name}')

    # Encode the namespace ID as a recipient address
    recipient_id = generate_namespace_path(namespace_name)[-1]
    recipient_address = Address.from_namespace_id(
        NamespaceId(recipient_id), facade.network.identifier)

    transfer_tx = facade.transaction_factory.create({
        'type': 'transfer_transaction_v1',
        'signer_public_key': signer_key_pair.public_key,
        'deadline': timestamp.add_hours(2).timestamp,
        'recipient_address': recipient_address,
        'mosaics': [{
            'mosaic_id': generate_mosaic_alias_id('symbol.xym'),
            'amount': 1_000_000  # 1 XYM
        }]
    })
    print('Transfer transaction:')
    print(f'  Recipient address (alias): {recipient_address}')
    // Send a transfer using the alias instead of a raw address
    console.log('Using alias in transfer:', namespaceName);

    // Encode the namespace ID as a recipient address
    const recipientPath = generateNamespacePath(namespaceName);
    const recipientId = recipientPath[recipientPath.length - 1];
    const recipientAddress = Address.fromNamespaceId(
        new models.NamespaceId(recipientId), facade.network.identifier);

    const transferTx = facade.transactionFactory.create({
        type: 'transfer_transaction_v1',
        signerPublicKey: signerKeyPair.publicKey.toString(),
        deadline: timestamp.addHours(2).timestamp,
        recipientAddress: recipientAddress,
        mosaics: [{
            mosaicId: generateMosaicAliasId('symbol.xym'),
            amount: 1_000_000n // 1 XYM
        }]
    });
    console.log('Transfer transaction:');
    console.log('  Recipient address (alias):',
        recipientAddress.toString());

ネームスペースがアドレスにリンクされると、トランザクションにおいてアドレスの代わりにネームスペースを使用できるようになります。 コードは、完全な16進数アドレスの代わりにエイリアスを受信者アドレスとして使用する 転送トランザクション の作成を示しています。

簡略化のため、この例ではトランザクションを作成しますが、アナウンスや承認の待機は行いません。

ネームスペースを受信者アドレスとして使用するには、 を使用してネームスペースIDを24バイトのアドレスに変換します。 前のセクションで説明したように、ネームスペースパスの最後のコンポーネントがネームスペースIDとして使用されます。

転送トランザクションのアナウンス方法の詳細については、転送トランザクション チュートリアルを参照してください。

アドレス解決レシート

ネットワークがネームスペースエイリアスを受信者アドレスとして使用するトランザクションを処理すると、アドレス解決レシート(Address Resolution Receipt) が生成されます。 このレシートには、トランザクションが承認された時点でエイリアスが実際に指し示していたアドレスが記録されます。

これは過去の監査可能性にとって重要です。エイリアスはいつでも変更または削除できるため、たとえエイリアスがその後更新されていたとしても、解決されたアドレスを常に検証できることがレシートによって保証されます。

解決レシートは /statements/resolutions/address GET エンドポイントを使用して照会できます。 レシートの詳細については、テキストブックの 解決ステートメント セクションを参照してください。

出力⚓︎

以下に示す出力は、プログラムの典型的な実行結果に対応しています。

Using node https://reference.symboltest.net:3001
Signer address: TCHBDENCLKEBILBPWP3JPB2XNY64OE7PYHHE32I
Namespace name: nsaddr_1770541301
Namespace ID: 12901002700615428842 (0xb309919a9089aaea)
Target address: TCWYXKVYBMO4NBCUF3AXKJMXCGVSYQOS7ZG2TLI
Fetching current network time from /node/time
  Network time: 103291999249 ms since nemesis
Fetching recommended fees from /network/fees/transaction
  Fee multiplier: 100
Built transaction:
{
  "signature": "88D2148FB1938012BE845D221363768ECBE903032940F37E7227728FD655B73D02B1A78E03F23C25E8F6F1B746C61E8BE3623C5D5F1E5D57BEBBA9A7F56B6604",
  "signer_public_key": "3B6A27BCCEB6A42D62A3A8D02A6F0D73653215771DE243A63AC048A18B59DA29",
  "version": 1,
  "network": 152,
  "type": 16974,
  "fee": "16100",
  "deadline": "103299199249",
  "namespace_id": "12901002700615428842",
  "address": "98AD8BAAB80B1DC684542EC175259711AB2C41D2FE4DA9AD",
  "alias_action": 1
}
Transaction hash: 8312500644961DA08EF4899AE5160C2B810A8B506B74C58D3F4FCF257C309827
Announcing address alias transaction to /transactions
  Response: {"message":"packet 9 was pushed to the network via /transactions"}
Waiting for transaction confirmation...
  Transaction status: unconfirmed
  Transaction status: confirmed
Address alias transaction confirmed in 17 seconds
Fetching namespace information from /namespaces/b309919a9089aaea
Alias information:
  Alias type: 2
  Linked address: TCWYXKVYBMO4NBCUF3AXKJMXCGVSYQOS7ZG2TLI
Using alias in transfer: nsaddr_1770541301
Transfer transaction:
  Recipient address (alias): THVKVCMQTKIQTMYAAAAAAAAAAAAAAAAAAAAAAAA

出力の主なポイント:

  • ネームスペースとターゲット (3、5行目): ネームスペース nsaddr_1770541301 がターゲットアドレス TCWYXKVYBMO4NBCUF3AXKJMXCGVSYQOS7ZG2TLI にリンクされています。

  • トランザクションハッシュ (23行目): トランザクションハッシュを使用して、 Symbol Testnet Explorer でトランザクションを検索できます。

  • エイリアスの検証 (32-33行目): ネームスペース情報により、エイリアスタイプが 2 (アドレス) であることが確認され、リンクされたアドレスが表示されています。

  • エイリアスの使用 (36行目): エイリアスを受信者として使用して転送トランザクションが作成されており、完全なアドレスの代わりに使用できることが実証されています。

    異なる受信者アドレス

    受信者アドレスがターゲットアドレスと異なるのは、それがターゲットアドレス自体ではなく エンコードされたネームスペース ID であるためです。 ネットワークはトランザクションを処理する際に、エイリアスをリンクされたアドレスに解決します。

結論⚓︎

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

ステップ 関連ドキュメント
ネームスペース ID を生成する
アドレスエイリアストランザクションを構築する
エイリアスを検証する /namespaces/{namespaceId} GET
エイリアスを使用する
アドレス解決レシートを照会する /statements/resolutions/address GET