Understanding TON Transactions and Tracking Results

ยท

The TON network operates on a unique message-based, asynchronous model, which can lead to common questions for developers: How do you confirm the final outcome of a transaction? What is the significance of the Bag of Cells (BOC) returned after sending a transaction, and how can you extract meaningful information from it? This guide provides clear answers and practical methods.

What Constitutes a TON Transaction?

A transaction on the TON blockchain is not a single atomic operation but a sequence of related events. Understanding its components is crucial for effective tracking.

In essence, initiating a transfer is the act of sending a message to a wallet's smart contract, which then processes that message to perform the transfer.

Constructing a Valid Transaction Message

Before sending anything, you must construct a valid message. Using the JavaScript TonConnect SDK, transaction parameters are defined within a SendTransactionRequest object.

Basic Transfer Transaction

A simple transfer involves specifying the recipient's address and the amount.

const transaction = {
  validUntil: Math.floor(Date.now() / 1000) + 600, // 10-minute validity window
  messages: [
    {
      address: "0:412410771DA82CBA306A55FA9E0D43C9D245E38133CB58F1457DFB8D5CD8892F",
      amount: "20000000", // Amount in nanotons (0.02 TON)
    },
  ],
};

Transfer Transaction with a Comment

Adding a comment requires a specific payload formatted as a serialized cell and encoded in Base64.

import { beginCell } from "@ton/ton";

// Build the comment payload
const body = beginCell()
  .storeUint(0, 32) // opcode for a text comment
  .storeStringTail("Hello, TON!") // the comment text
  .endCell();

const transaction = {
  validUntil: Math.floor(Date.now() / 1000) + 600,
  messages: [
    {
      address: "0:412410771DA82CBA306A55FA9E0D43C9D245E38133CB58F1457DFB8D5CD8892F",
      amount: "20000000",
      payload: body.toBoc().toString("base64"), // Attach the encoded payload
    },
  ],
};

For advanced message types and detailed construction rules, consult the official TON documentation on message builders.

How to Track Transaction Results

Sending a transaction via TonConnect is just the first step. The sendTransaction method returns a promise that resolves to a SendTransactionResponse object.

const [tonConnectUi] = useTonConnectUI();
const result = await tonConnectUi.sendTransaction(transaction);
// result.boc contains the signed transaction BOC

This boc (Bag of Cells) string represents the signed, serialized transaction message that was broadcast to the network. It is your key to tracking the transaction's status.

Waiting for Transaction Confirmation

To confirm the transaction has been processed and included in a block, you need to poll the network using a TonClient instance and compare your transaction's hash to the latest ones on the recipient's account.

The following function demonstrates how to wait for confirmation by checking the target address for a transaction that matches your sent BOC's hash.

import { Address } from "@ton/ton";

const waitForTransaction = async (options, client) => {
  const { hash, refetchInterval = 1000, refetchLimit, address } = options;
  return new Promise((resolve) => {
    let refetches = 0;
    const walletAddress = Address.parse(address);
    const interval = setInterval(async () => {
      refetches += 1;
      const state = await client.getContractState(walletAddress);
      if (!state || !state.lastTransaction) {
        // Handle cases where no state or transaction is found
        return;
      }
      const { lt: lastLt, hash: lastHash } = state.lastTransaction;
      const lastTx = await client.getTransaction(walletAddress, lastLt, lastHash);
      if (lastTx && lastTx.inMessage) {
        // Calculate the hash of the inbound message of the latest transaction
        const msgCell = beginCell().store(storeMessage(lastTx.inMessage)).endCell();
        const inMsgHash = msgCell.hash().toString("base64");
        // Compare it to the hash of the BOC you sent
        if (inMsgHash === hash) {
          clearInterval(interval);
          resolve(lastTx); // Transaction confirmed!
        }
      }
      if (refetchLimit && refetches >= refetchLimit) {
        clearInterval(interval);
        resolve(null); // Stop after limit is reached
      }
    }, refetchInterval);
  });
};

This method uses getContractState to find the latest transaction's logical time (lt) and hash, then uses getTransaction to fetch its full details. Alternative strategies, such as using getTransactions to list multiple transactions, can also be effective for monitoring. ๐Ÿ‘‰ Explore more strategies for tracking on-chain activity

Frequently Asked Questions

How long does a TON transaction typically take to confirm?
Confirmation time is usually very fast, often within seconds. The asynchronous nature means the transaction is final once it appears in the blockchain, but your dApp must actively check for its inclusion using the methods described.

What does it mean if my transaction BOC hash is never found?
This typically indicates that the transaction failed to be included in a block. This could be due to an error in the message structure, insufficient gas (TON coins) for fees, or the transaction expiring because the validUntil timestamp passed.

Can I get the transaction hash (TxHash) before it is confirmed?
No, the final transaction hash is generated by the blockchain upon successful processing and inclusion in a block. The BOC hash you get from sendTransaction is the hash of the message you sent, which is used to find the corresponding on-chain transaction later.

What's the difference between 'sendTransaction' and 'waitForTransaction'?
sendTransaction is for broadcasting your signed message to the network. waitForTransaction (or your custom function) is for querying the network afterward to verify the message was accepted and processed into a transaction.

Is there a way to get the receipt of a TON transaction?
TON does not have receipts in the same way Ethereum does. You must query the transaction and its associated messages directly from the blockchain using a client like TonClient to get all relevant details and outcomes.

How can I handle errors or failed transactions?
A transaction not appearing is the primary sign of failure. You should implement a timeout logic (like refetchLimit in the example) in your waiting function to avoid infinite polling and then alert the user that the transaction may have failed.

Key Takeaways for Developers

Developing on TON requires a shift in mindset from account-based blockchains. Success hinges on understanding the message-passing paradigm. Tracking a transaction involves using the returned BOC as a reference to poll the network until you find a matching processed transaction. Mastering these concepts is fundamental to building responsive and reliable decentralized applications on TON. For a deeper dive into practical implementation, you can ๐Ÿ‘‰ view real-time tools and code examples in community repositories.