コンテンツにスキップ

トランザクションを手動で作成する⚓︎

中級

ほとんどのチュートリアルでは、ファサードを通じてディスクリプタからトランザクションを作成します。 これは推奨される方法です。型付きディスクリプタをサポートする言語では、コンパイル時または型チェックのサポートと優れたエディタ支援が得られ、ファサードは署名者、タイムスタンプ、デッドラインを期間から設定します。

完全性のため、このチュートリアルでは下位レベルの代替手段として、トランザクションファクトリを使ってトランザクションを手動で作成する方法を示します。

この例は XEM を送信する チュートリアルと同じ流れに沿っていますが、ディスクリプタベースの作成を手動のフィールド設定に置き換えます。 ネットワーク時刻を明示的に取得し、タイムスタンプと絶対デッドラインを設定して、NEM のトランザクション手数料を計算します。

前提条件⚓︎

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

完全なコード⚓︎

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

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_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}')


# Helper function to announce a transaction
def announce_transaction(payload, label):
    announce_path = '/transaction/announce'
    print(f'Announcing {label} to {announce_path}')
    request = urllib.request.Request(
        f'{NODE_URL}{announce_path}',
        data=payload.encode(),
        headers={'Content-Type': 'application/json'},
        method='POST'
    )
    with urllib.request.urlopen(request) as announce_response:
        result = json.loads(announce_response.read().decode())
    print(f'  Result: {result["message"]}')
    return result['message']


# Helper function to wait for transaction confirmation
def wait_for_confirmation(tx_hash, label):
    status_path = f'/transaction/get?hash={tx_hash}'
    print(f'Waiting for {label} confirmation from {status_path}')
    is_confirmed = False
    for _ in range(120):
        try:
            with urllib.request.urlopen(
                f'{NODE_URL}{status_path}'
            ) as status_response:
                confirmed = json.loads(status_response.read().decode())
                height = confirmed['meta']['height']
                print(f'{label} confirmed in block {height}')
                is_confirmed = True
                break
        except urllib.error.HTTPError:
            print('  Transaction status: pending')
        time.sleep(1)
    if not is_confirmed:
        print(f'{label} confirmation took too long.')



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

RECIPIENT_ADDRESS = os.getenv(
    'RECIPIENT_ADDRESS',
    'TBULEAUG2CZQISUR442HWA6UAKGWIXHDABJVIPS4')

facade = NemFacade('testnet')

# Define the amount of XEM to transfer
xem = float(os.getenv('XEM_AMOUNT', '1'))
amount = round(xem * 1_000_000)


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 transaction
    transaction = facade.transaction_factory.create({
        'type': 'transfer_transaction_v2',
        'signer_public_key': signer_key_pair.public_key,
        'timestamp': timestamp.timestamp,
        'deadline': deadline.timestamp,
        'recipient_address': RECIPIENT_ADDRESS,
        'amount': amount
    })

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

    # Announce the transaction
    announce_result = announce_transaction(json_payload, 'transaction')

    # Wait for confirmation
    if 'SUCCESS' == announce_result:
        transaction_hash = facade.hash_transaction(transaction)
        print(f'Transaction hash: {transaction_hash}')
        wait_for_confirmation(transaction_hash, 'transaction')
    else:
        print(f'Transaction rejected: {announce_result}')

except Exception as error:
    print(error)

Download source

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

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

// Helper function to announce a transaction
async function announceTransaction(payload, label) {
    const announcePath = '/transaction/announce';
    console.log(`Announcing ${label} to ${announcePath}`);
    const response = await fetch(`${NODE_URL}${announcePath}`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: payload
    });
    if (!response.ok)
        throw new Error(`HTTP ${response.status}`);
    const result = await response.json();
    console.log('  Result:', result.message);
    return result.message;
}

// Helper function to wait for transaction confirmation
async function waitForConfirmation(transactionHash, label) {
    const statusPath = `/transaction/get?hash=${transactionHash}`;
    console.log(`Waiting for ${label} confirmation from`, statusPath);
    let isConfirmed = false;
    for (let attempt = 1; 120 >= attempt; ++attempt) {
        const response = await fetch(`${NODE_URL}${statusPath}`);
        if (!response.ok) {
            console.log('  Transaction status: pending');
            await new Promise(resolve => { setTimeout(resolve, 1000); });
        } else {
            const confirmed = await response.json();
            console.log(`${label} confirmed in block`,
                confirmed.meta.height);
            isConfirmed = true;
            break;
        }
    }
    if (!isConfirmed)
        console.warn(`${label} confirmation took too long.`);
}


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

const RECIPIENT_ADDRESS = process.env.RECIPIENT_ADDRESS ||
    'TBULEAUG2CZQISUR442HWA6UAKGWIXHDABJVIPS4';

const facade = new NemFacade('testnet');

// Define the amount of XEM to transfer
const xem = parseFloat(process.env.XEM_AMOUNT || '1');
const amount = BigInt(Math.round(xem * 1_000_000));


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 transaction
    const transaction = facade.transactionFactory.create({
        type: 'transfer_transaction_v2',
        signerPublicKey: signerKeyPair.publicKey.toString(),
        timestamp: timestamp.timestamp,
        deadline: deadline.timestamp,
        recipientAddress: RECIPIENT_ADDRESS,
        amount
    });

    // 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 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 });

    // Announce the transaction
    const announceResult = await announceTransaction(
        jsonPayload, 'transaction');

    // Wait for confirmation
    if ('SUCCESS' === announceResult) {
        const transactionHash = facade.hashTransaction(transaction)
            .toString();
        console.log('Transaction hash:', transactionHash);
        await waitForConfirmation(transactionHash, 'transaction');
    } else {
        console.log('Transaction rejected:', announceResult);
    }

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

Download source

//JAVA 21+
//DEPS org.symbol:symbol-sdk:3.3.3

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.util.Map;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import org.symbol.sdk.CryptoTypes;
import org.symbol.sdk.facade.NemFacade;
import org.symbol.sdk.nem.FeeCalculator;
import org.symbol.sdk.nem.KeyPair;
import org.symbol.sdk.nem.NemTransactionFactory;
import org.symbol.sdk.nem.NetworkTimestamp;
import org.symbol.sdk.nem.models.Amount;
import org.symbol.sdk.nem.models.Transaction;

public final class ManualTransactionCreation {
    private static final ObjectMapper JSON_MAPPER = new ObjectMapper();

    private static final HttpClient HTTP_CLIENT =
        HttpClient.newHttpClient();

    private final String nodeUrl = System.getenv().getOrDefault(
        "NODE_URL", "http://libertalia.nemtest.net:7890");

    private final NemFacade facade = new NemFacade("testnet");

    // Helper method to announce a transaction
    private String announceTransaction(
        final String payload,
        final String label
    ) throws IOException, InterruptedException {
        final String announcePath = "/transaction/announce";
        System.out.printf("Announcing %s to %s%n", label, announcePath);
        final HttpRequest request = HttpRequest.newBuilder(
            URI.create(nodeUrl + announcePath))
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(payload))
            .build();
        final HttpResponse<String> response = HTTP_CLIENT.send(
            request, BodyHandlers.ofString());
        if (2 != response.statusCode() / 100)
            throw new IOException("HTTP " + response.statusCode());
        final String result = JSON_MAPPER.readTree(response.body())
            .get("message").asText();
        System.out.printf("  Result: %s%n", result);
        return result;
    }

    // Helper method to wait for transaction confirmation
    private void waitForConfirmation(
        final String transactionHash,
        final String label
    ) throws IOException, InterruptedException {
        final String statusPath = String.format(
            "/transaction/get?hash=%s", transactionHash);
        System.out.printf("Waiting for %s confirmation from %s%n",
            label, statusPath);
        boolean isConfirmed = false;
        for (int attempt = 1; 120 >= attempt; ++attempt) {
            final HttpRequest request = HttpRequest.newBuilder(
                URI.create(nodeUrl + statusPath)).GET().build();
            final HttpResponse<String> response = HTTP_CLIENT.send(
                request, BodyHandlers.ofString());
            if (2 != response.statusCode() / 100) {
                System.out.println("  Transaction status: pending");
                Thread.sleep(1000);
            } else {
                final JsonNode confirmed = JSON_MAPPER.readTree(
                    response.body());
                System.out.printf("%s confirmed in block %s%n", label,
                    confirmed.get("meta").get("height").asText());
                isConfirmed = true;
                break;
            }
        }
        if (!isConfirmed)
            System.out.printf("%s confirmation took too long.%n", label);
    }

    public static void main(final String[] args) {
        try {
            new ManualTransactionCreation().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", nodeUrl);

        final String signerPrivateKey = System.getenv().getOrDefault(
            "SIGNER_PRIVATE_KEY", "0".repeat(64));
        final KeyPair signerKeyPair = new KeyPair(
            new CryptoTypes.PrivateKey(signerPrivateKey));
        final String recipientAddress = System.getenv().getOrDefault(
            "RECIPIENT_ADDRESS",
            "TBULEAUG2CZQISUR442HWA6UAKGWIXHDABJVIPS4");

        // Define the amount of XEM to transfer
        final double xem = Double.parseDouble(
            System.getenv().getOrDefault("XEM_AMOUNT", "1"));
        final long amount = Math.round(xem * 1_000_000);

        // Fetch current network time
        final String timePath = "/time-sync/network-time";
        System.out.printf("Fetching current network time from %s%n",
            timePath);
        final HttpRequest timeRequest = HttpRequest.newBuilder(
            URI.create(nodeUrl + timePath)).GET().build();
        final HttpResponse<String> timeResponse = HTTP_CLIENT.send(
            timeRequest, BodyHandlers.ofString());
        final JsonNode timeJson = JSON_MAPPER.readTree(
            timeResponse.body());
        final long networkTime =
            timeJson.get("receiveTimeStamp").asLong() / 1000;
        System.out.printf(
            "  Network time: %d s since the nemesis block%n", networkTime);
        final NetworkTimestamp timestamp = new NetworkTimestamp(
            networkTime);
        final long deadline = timestamp.addHours(2).timestamp;

        // Build the transaction
        final Transaction transaction = facade.transactionFactory.create(
            Map.of(
                "type", "transfer_transaction_v2",
                "signerPublicKey",
                    signerKeyPair.getPublicKey().toString(),
                "timestamp", timestamp.timestamp,
                "deadline", deadline,
                "recipientAddress", recipientAddress,
                "amount", amount));

        // Calculate and attach the transaction fee
        final long fee = FeeCalculator.calculateTransactionFee(
            transaction);
        transaction.setFee(new Amount(fee));
        System.out.printf("  Transaction fee: %s XEM%n",
            (double) fee / 1_000_000);

        // Sign transaction and generate final payload
        final CryptoTypes.Signature signature = facade.signTransaction(
            signerKeyPair, transaction);
        final String jsonPayload = NemTransactionFactory.attachSignature(
            transaction, signature);
        System.out.println("Built transaction:");
        System.out.println(JSON_MAPPER.writerWithDefaultPrettyPrinter()
            .writeValueAsString(transaction.toJson()));

        // Announce the transaction
        final String announceResult = announceTransaction(
            jsonPayload, "transaction");

        // Wait for confirmation
        if ("SUCCESS".equals(announceResult)) {
            final String transactionHash = facade.hashTransaction(
                transaction).toString();
            System.out.printf("Transaction hash: %s%n", transactionHash);
            waitForConfirmation(transactionHash, "transaction");
        } else {
            System.out.printf(
                "Transaction rejected: %s%n", announceResult);
        }
    }
}

Download source

コードの説明⚓︎

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

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

RECIPIENT_ADDRESS = os.getenv(
    'RECIPIENT_ADDRESS',
    'TBULEAUG2CZQISUR442HWA6UAKGWIXHDABJVIPS4')
const SIGNER_PRIVATE_KEY = process.env.SIGNER_PRIVATE_KEY ||
    '0000000000000000000000000000000000000000000000000000000000000000';
const signerKeyPair = new NemFacade.KeyPair(
    new PrivateKey(SIGNER_PRIVATE_KEY));

const RECIPIENT_ADDRESS = process.env.RECIPIENT_ADDRESS ||
    'TBULEAUG2CZQISUR442HWA6UAKGWIXHDABJVIPS4';
        final String signerPrivateKey = System.getenv().getOrDefault(
            "SIGNER_PRIVATE_KEY", "0".repeat(64));
        final KeyPair signerKeyPair = new KeyPair(
            new CryptoTypes.PrivateKey(signerPrivateKey));
        final String recipientAddress = System.getenv().getOrDefault(
            "RECIPIENT_ADDRESS",
            "TBULEAUG2CZQISUR442HWA6UAKGWIXHDABJVIPS4");

署名者と受取人は、XEM を送信する チュートリアルと同じ方法で設定します。

送金額を定義する⚓︎

# Define the amount of XEM to transfer
xem = float(os.getenv('XEM_AMOUNT', '1'))
amount = round(xem * 1_000_000)
// Define the amount of XEM to transfer
const xem = parseFloat(process.env.XEM_AMOUNT || '1');
const amount = BigInt(Math.round(xem * 1_000_000));
        // Define the amount of XEM to transfer
        final double xem = Double.parseDouble(
            System.getenv().getOrDefault("XEM_AMOUNT", "1"));
        final long amount = Math.round(xem * 1_000_000);

金額は原子単位で表します。XEM の 可分性 は 6 なので、デフォルトの 1 XEM の送金は 1_000_000 原子単位です。

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

    # 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);
        // Fetch current network time
        final String timePath = "/time-sync/network-time";
        System.out.printf("Fetching current network time from %s%n",
            timePath);
        final HttpRequest timeRequest = HttpRequest.newBuilder(
            URI.create(nodeUrl + timePath)).GET().build();
        final HttpResponse<String> timeResponse = HTTP_CLIENT.send(
            timeRequest, BodyHandlers.ofString());
        final JsonNode timeJson = JSON_MAPPER.readTree(
            timeResponse.body());
        final long networkTime =
            timeJson.get("receiveTimeStamp").asLong() / 1000;
        System.out.printf(
            "  Network time: %d s since the nemesis block%n", networkTime);
        final NetworkTimestamp timestamp = new NetworkTimestamp(
            networkTime);
        final long deadline = timestamp.addHours(2).timestamp;

手動でトランザクションを作成するには、NEM のネメシスブロックからの経過秒数で表す timestamp と絶対 deadline を明示的に指定する必要があります。

スニペットは /time-sync/network-time GET から現在のネットワーク時刻を取得します。 エンドポイントはミリ秒を返すため、 でラップする前に 1000 で割ります。 デッドラインはタイムスタンプの 2 時間後に設定します。

一方、ディスクリプタベースのファサード作成では、秒単位のデッドライン期間を受け取り、ローカル時計から両方の値を導出するため、このリクエストは不要です。

トランザクションを構築する⚓︎

    # Build the transaction
    transaction = facade.transaction_factory.create({
        'type': 'transfer_transaction_v2',
        'signer_public_key': signer_key_pair.public_key,
        'timestamp': timestamp.timestamp,
        'deadline': deadline.timestamp,
        'recipient_address': RECIPIENT_ADDRESS,
        'amount': amount
    })
    // Build the transaction
    const transaction = facade.transactionFactory.create({
        type: 'transfer_transaction_v2',
        signerPublicKey: signerKeyPair.publicKey.toString(),
        timestamp: timestamp.timestamp,
        deadline: deadline.timestamp,
        recipientAddress: RECIPIENT_ADDRESS,
        amount
    });
        // Build the transaction
        final Transaction transaction = facade.transactionFactory.create(
            Map.of(
                "type", "transfer_transaction_v2",
                "signerPublicKey",
                    signerKeyPair.getPublicKey().toString(),
                "timestamp", timestamp.timestamp,
                "deadline", deadline,
                "recipientAddress", recipientAddress,
                "amount", amount));

トランザクションは、プレーンなディスクリプタ辞書またはオブジェクトを受け取る で作成します。 ファサードの作成メソッドとは異なり、この下位レベルのファクトリは共通のトランザクションフィールドを設定しません。

ディスクリプタには次の値を指定します。

  • : 現在の転送トランザクションバージョンである TransferTransactionV2。
  • : トランザクションに署名し、手数料を支払い、XEM を送るアカウント。
  • : 前の手順で取得した現在のネットワーク時刻のタイムスタンプ。
  • : タイムスタンプの 2 時間後に設定した、ネットワーク時刻の絶対デッドライン。
  • : XEM を受け取るアドレス。
  • : 原子単位で表した送金額。

トランザクション手数料を計算する⚓︎

    # 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`);
        // Calculate and attach the transaction fee
        final long fee = FeeCalculator.calculateTransactionFee(
            transaction);
        transaction.setFee(new Amount(fee));
        System.out.printf("  Transaction fee: %s XEM%n",
            (double) fee / 1_000_000);

下位レベルのファクトリは、NEM の固定手数料表に基づく手数料を計算しません。 スニペットは構築後に を呼び出し、署名前に結果を transaction.fee へ割り当てます。

署名してシリアライズする⚓︎

    # 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))
    // 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 });
        // Sign transaction and generate final payload
        final CryptoTypes.Signature signature = facade.signTransaction(
            signerKeyPair, transaction);
        final String jsonPayload = NemTransactionFactory.attachSignature(
            transaction, signature);
        System.out.println("Built transaction:");
        System.out.println(JSON_MAPPER.writerWithDefaultPrettyPrinter()
            .writeValueAsString(transaction.toJson()));

でトランザクションに署名します。 で署名を付加し、アナウンスできる JSON ペイロードを生成します。

トランザクションをアナウンスする⚓︎

    # Announce the transaction
    announce_result = announce_transaction(json_payload, 'transaction')
    // Announce the transaction
    const announceResult = await announceTransaction(
        jsonPayload, 'transaction');
        // Announce the transaction
        final String announceResult = announceTransaction(
            jsonPayload, "transaction");

署名済みペイロードを /transaction/announce POST に送信します。SUCCESS レスポンスは、トランザクションが未承認プールに入ったことを意味します。

承認を待つ⚓︎

    # Wait for confirmation
    if 'SUCCESS' == announce_result:
        transaction_hash = facade.hash_transaction(transaction)
        print(f'Transaction hash: {transaction_hash}')
        wait_for_confirmation(transaction_hash, 'transaction')
    else:
        print(f'Transaction rejected: {announce_result}')
    // Wait for confirmation
    if ('SUCCESS' === announceResult) {
        const transactionHash = facade.hashTransaction(transaction)
            .toString();
        console.log('Transaction hash:', transactionHash);
        await waitForConfirmation(transactionHash, 'transaction');
    } else {
        console.log('Transaction rejected:', announceResult);
    }
        // Wait for confirmation
        if ("SUCCESS".equals(announceResult)) {
            final String transactionHash = facade.hashTransaction(
                transaction).toString();
            System.out.printf("Transaction hash: %s%n", transactionHash);
            waitForConfirmation(transactionHash, "transaction");
        } else {
            System.out.printf(
                "Transaction rejected: %s%n", announceResult);
        }

トランザクションがブロックに含まれるか、2 分間の再試行上限に達するまで /transaction/get GET でポーリングします。

出力⚓︎

Using node http://libertalia.nemtest.net:7890
Fetching current network time from /time-sync/network-time
  Network time: 351947283 s since the nemesis block
  Transaction fee: 0.05 XEM
Built transaction:
{
  type: 257,
  version: 2,
  network: 152,
  timestamp: 351947283,
  signerPublicKey: '462EE976890916E54FA825D26BDD0235F5EB5B6A143C199AB0AE5EE9328E08CE',
  signature: '17FF7D37A63F3CFC43C790300A7B7F1C8A2A2B1C5D64546007C7D02C771516EE8872A6BBAE414486DC2D4750BBAACB41B3140D45CC319F51B7CBC1D524545C06',
  fee: '50000',
  deadline: 351954483,
  recipientAddress: '5442554C4541554732435A51495355523434324857413655414B47574958484441424A5649505334',
  amount: '1000000',
  mosaics: []
}
Announcing transaction to /transaction/announce
  Result: SUCCESS
Transaction hash: 436A43BDD3CE1F0A5A5EFC40A9027D3732D59AB3C507818A1B436EC7B32BF099
Waiting for transaction confirmation from /transaction/get?hash=436A43BDD3CE1F0A5A5EFC40A9027D3732D59AB3C507818A1B436EC7B32BF099
  Transaction status: pending
  Transaction status: pending
transaction confirmed in block 626588

最初の 2 つのハイライト行は、明示的なネットワーク時刻のリクエストを示します。 さらに、計算した手数料、手動で設定した署名者と絶対デッドライン、アナウンス結果、承認確認に使うトランザクションハッシュも表示します。

まとめ⚓︎

このチュートリアルでは、トランザクションを手動で作成する方法を説明しました。

手順 関連ドキュメント
ネットワーク時刻を取得する /time-sync/network-time GET、
トランザクションを構築する 、TransferTransactionV2
手数料を計算する
署名してシリアライズする
アナウンスして承認を待つ /transaction/announce POST
/transaction/get GET