Turning the asynchronous transaction announcement into synchronous

Handy snippet to announce a transaction and wait until this is confirmed.

Use case

Bitxor works differently: when a transaction is announced, the REST API server will always return an OK. As a result, the developer does not have to wait until the server returns a response, being able to make more responsive apps. However, now is the developer’s responsibility to check the status of the transaction and ensure it is confirmed.

The negative aspect of announcing transactions asynchronously is that it adds unnecessary complexity to small projects. The Bitxor SDK TransactionService aims to solve this problem by providing a function that waits for the confirmation or rejection of the transaction.

Prerequisites

Sending a synchronous transaction

Alice is developing an app to send 10 currencytoken to Bob and wants to know if the transaction has reached the network before sending Bob an email.

  1. Create a new .ts file. Then, define and sign a TransferTransaction.

// replace with recipient address
const rawRecipientAddress = 'BXRQ5E-YACWBP-CXKGIL-I6XWCH-DRFLTB-KUK34I-YJQ';
const recipientAddress = Address.createFromRawAddress(rawRecipientAddress);
// replace with network type
const networkType = NetworkType.TEST_NET;
// replace with bitxor id
const networkCurrencyTokenId = new TokenId('5E62990DCAC5BE8A');
// replace with network currency divisibility
const networkCurrencyDivisibility = 6;

const transferTransaction = TransferTransaction.create(
  Deadline.create(epochAdjustment),
  recipientAddress,
  [
    new Token(
      networkCurrencyTokenId,
      UInt64.fromUint(10 * Math.pow(10, networkCurrencyDivisibility)),
    ),
  ],
  EmptyMessage,
  networkType,
  UInt64.fromUint(2000000),
);

// replace with sender private key
const privateKey =
  '1111111111111111111111111111111111111111111111111111111111111111';
const account = Account.createFromPrivateKey(privateKey, networkType);
// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const networkGenerationHash =
  '1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
const signedTransaction = account.sign(
  transferTransaction,
  networkGenerationHash,
);
// replace with recipient address
const rawRecipientAddress = 'BXRQ5E-YACWBP-CXKGIL-I6XWCH-DRFLTB-KUK34I-YJQ';
const recipientAddress = bitxor_sdk_1.Address.createFromRawAddress(
    rawRecipientAddress,
);
// replace with network type
const networkType = bitxor_sdk_1.NetworkType.TEST_NET;
// replace with bitxor id
const networkCurrencyTokenId = new bitxor_sdk_1.TokenId('5E62990DCAC5BE8A');
// replace with network currency divisibility
const networkCurrencyDivisibility = 6;
const transferTransaction = bitxor_sdk_1.TransferTransaction.create(
    bitxor_sdk_1.Deadline.create(epochAdjustment),
    recipientAddress, [
        new bitxor_sdk_1.Token(
            networkCurrencyTokenId,
            bitxor_sdk_1.UInt64.fromUint(
                10 * Math.pow(10, networkCurrencyDivisibility),
            ),
        ),
    ],
    bitxor_sdk_1.EmptyMessage,
    networkType,
    bitxor_sdk_1.UInt64.fromUint(2000000),
);
// replace with sender private key
const privateKey =
    '1111111111111111111111111111111111111111111111111111111111111111';
const account = bitxor_sdk_1.Account.createFromPrivateKey(
    privateKey,
    networkType,
);
// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const networkGenerationHash =
    '1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
const signedTransaction = account.sign(
    transferTransaction,
    networkGenerationHash,
);
  1. Once signed, announce the transaction using TransactionService.announce instead of TransactionHttp.announce.

const nodeUrl = 'NODE_URL';
const repositoryFactory = new RepositoryFactoryHttp(nodeUrl);
const receiptHttp = repositoryFactory.createReceiptRepository();
const transactionHttp = repositoryFactory.createTransactionRepository();
const listener = repositoryFactory.createListener();
const transactionService = new TransactionService(transactionHttp, receiptHttp);

listener.open().then(() => {
  merge(
    transactionService.announce(signedTransaction, listener),
    listener.status(account.address).pipe(
      filter((error) => error.hash === signedTransaction.hash),
      tap((error) => {
        throw new Error(error.code);
      }),
    ),
  ).subscribe(
    (transaction) => {
      console.log(transaction);
      // TODO: send email to recipient
      listener.close();
    },
    (err) => console.error(err),
  );
});
const nodeUrl = 'NODE_URL';
const repositoryFactory = new bitxor_sdk_1.RepositoryFactoryHttp(nodeUrl);
const receiptHttp = repositoryFactory.createReceiptRepository();
const transactionHttp = repositoryFactory.createTransactionRepository();
const listener = repositoryFactory.createListener();
const transactionService = new bitxor_sdk_1.TransactionService(
    transactionHttp,
    receiptHttp,
);
listener.open().then(() => {
    rxjs_1
        .merge(
            transactionService.announce(signedTransaction, listener),
            listener.status(account.address).pipe(
                operators_1.filter((error) => error.hash === signedTransaction.hash),
                operators_1.tap((error) => {
                    throw new Error(error.code);
                }),
            ),
        )
        .subscribe(
            (transaction) => {
                console.log(transaction);
                // TODO: send email to recipient
                listener.close();
            },
            (err) => console.error(err),
        );
});

Note

The function TransactionService.announce() will respond successfully if the transaction reaches the network and does not have validation errors. You might still need to wait for several confirmations before executing additional actions.