The TAC SDK provides sophisticated tracking capabilities to monitor cross-chain transactions from initiation to completion. Whether you need simple status updates or detailed execution profiling, the tracking system offers multiple approaches to fit your application’s needs.
Transaction tracking is essential for providing good user experience in
cross-chain applications. Always implement appropriate error handling and
retry logic to handle network issues gracefully.
Overview of Transaction Tracking
Cross-chain transactions go through multiple stages across different blockchain networks. The TAC SDK’s tracking system provides real-time visibility into this complex process through several interfaces:
- TransactionLinker: Initial tracking handle returned from transaction submission
- OperationTracker: Comprehensive tracking with detailed status information
- Simplified Tracking: Helper functions for common tracking scenarios
- Stage Profiling: Detailed execution timeline for advanced monitoring
Basic Tracking with TransactionLinker
Every cross-chain transaction returns a TransactionLinker
object that serves as the primary tracking handle:
// Send transaction and get tracking handle
const transactionLinker = await tacSdk.sendCrossChainTransaction(
evmProxyMsg,
sender,
assets
);
// Operation ID may not be immediately available
// Use OperationTracker to retrieve it
console.log("Shard Key:", transactionLinker.shardsKey);
console.log("Timestamp:", transactionLinker.timestamp);
console.log("Caller:", transactionLinker.caller);
TransactionLinker Properties
interface TransactionLinker {
caller: string; // Sender's wallet address
shardCount: number; // Number of shards involved
shardsKey: string; // Unique shard identifier
timestamp: number; // Transaction timestamp
operationId?: string; // Operation ID (available after processing)
sendTransactionResult?: any; // Raw transaction result
}
OperationTracker Class
The OperationTracker
provides comprehensive tracking capabilities with multiple monitoring methods:
import { OperationTracker, Network } from "@tonappchain/sdk";
// Initialize tracker
const tracker = new OperationTracker(Network.TESTNET);
// Get operation status
const status = await tracker.getOperationStatus(operationId);
console.log("Transaction status:", status);
// Get operation status with wait options for automatic waiting
const statusWithWait = await tracker.getOperationStatus(operationId, {
timeout: 300000, // 5 minutes total timeout
maxAttempts: 30, // Maximum 30 attempts
delay: 10000, // 10 seconds between attempts
successCheck: (result) => result.success === true,
onSuccess: (result) => console.log("Operation completed:", result),
});
console.log("Final status:", statusWithWait);
Simplified Status Tracking
For applications that need basic status information:
const getSimpleStatus = async (transactionLinker) => {
const tracker = new OperationTracker(Network.TESTNET);
const status = await tracker.getSimplifiedOperationStatus(transactionLinker);
switch (status) {
case "PENDING":
console.log("Transaction is being processed");
break;
case "SUCCESSFUL":
console.log("Transaction completed successfully");
break;
case "FAILED":
console.log("Transaction failed");
break;
case "OPERATION_ID_NOT_FOUND":
console.log("Operation not found - may still be propagating");
break;
}
return status;
};
const getSimpleStatusWithWaiting = async (transactionLinker) => {
const tracker = new OperationTracker(Network.TESTNET);
// Wait for final status with custom wait options
const finalStatus = await tracker.getSimplifiedOperationStatus(
transactionLinker
);
return finalStatus;
};
For applications requiring comprehensive status data:
const getDetailedStatus = async (operationId) => {
const tracker = new OperationTracker(Network.TESTNET);
const statusInfo = await tracker.getOperationStatus(operationId);
console.log("Status Information:");
console.log("- Stage:", statusInfo.stage);
console.log("- Success:", statusInfo.success);
console.log("- Success:", statusInfo.success);
console.log("- Timestamp:", statusInfo.timestamp);
console.log("- Transactions:", statusInfo.transactions);
return statusInfo;
};
Advanced Tracking with Custom Success Callbacks
For complex applications that need automatic profiling and finalization tracking:
const getDetailedStatusWithCallback = async (operationId) => {
const tracker = new OperationTracker(Network.TESTNET);
// Advanced tracking with onSuccess callback for profiling
const statusInfo = await tracker.getOperationStatus(operationId, {
timeout: 300000,
maxAttempts: 30,
delay: 10000,
context: {
operationTracker: tracker,
enableProfiling: true,
},
successCheck: (result) => result.success === true,
onSuccess: async (result, context) => {
if (context?.enableProfiling && context.operationTracker) {
console.log("📊 Retrieving detailed profiling data...");
// Get comprehensive profiling information
const profilingData = await context.operationTracker.getStageProfiling(
operationId
);
console.log("\n📈 OPERATION TRACKING COMPLETE");
console.log(`🔹 Operation ID: ${operationId}`);
console.log(
`🔹 Final Status: ${result.success ? "✅ Success" : "❌ Failed"}`
);
console.log(
`🔹 Profiling Stages: ${Object.keys(profilingData).length}`
);
// Display stage-by-stage breakdown
for (const [stageName, stageInfo] of Object.entries(profilingData)) {
if (stageName !== "operationType" && stageName !== "metaInfo") {
const status = stageInfo.exists
? "✅ Completed"
: "⏸️ Not executed";
const timestamp = stageInfo.timestamp
? new Date(stageInfo.timestamp).toISOString()
: "N/A";
console.log(` • ${stageName}: ${status} (${timestamp})`);
}
}
// Send notifications, update database, trigger workflows, etc.
console.log("🔔 Triggering post-completion workflows...");
return profilingData;
}
},
});
return statusInfo;
};
Automated Tracking Functions
The SDK provides utility functions for automated tracking with built-in polling and table display:
Single Transaction Tracking
import { startTracking } from "@tonappchain/sdk";
// Track with console output (default)
await startTracking(transactionLinker, Network.TESTNET);
// Track and return results
const executionStages = await startTracking(
transactionLinker,
Network.TESTNET,
{
returnValue: true,
tableView: false,
delay: 5000, // Poll every 5 seconds
maxIterationCount: 60, // Maximum 60 iterations (5 minutes)
}
);
console.log("Final execution stages:", executionStages);
Multiple Transaction Tracking
import { startTrackingMultiple } from "@tonappchain/sdk";
// Track multiple transactions simultaneously
const transactionLinkers = [linker1, linker2, linker3];
await startTrackingMultiple(transactionLinkers, Network.TESTNET, {
tableView: true,
delay: 10000, // Poll every 10 seconds
maxIterationCount: 30, // Maximum 30 iterations
});
Stage Profiling
For detailed execution analysis, use stage profiling to understand exactly where time is spent:
const getExecutionProfiling = async (operationId) => {
const tracker = new OperationTracker(Network.TESTNET);
const stages = await tracker.getStageProfiling(operationId);
console.log("Execution Timeline:");
console.log("- TON Transaction Sent:", stages.tonTransactionSent);
console.log("- Message Validated:", stages.messageValidated);
console.log("- Assets Locked:", stages.assetsLocked);
console.log("- Cross-Chain Routed:", stages.crossChainRouted);
console.log("- EVM Execution Started:", stages.evmExecutionStarted);
console.log("- EVM Execution Completed:", stages.evmExecutionCompleted);
console.log("- Final Confirmation:", stages.finalConfirmation);
// Calculate durations
const totalTime = stages.finalConfirmation - stages.tonTransactionSent;
const evmExecutionTime =
stages.evmExecutionCompleted - stages.evmExecutionStarted;
console.log(`Total execution time: ${totalTime / 1000} seconds`);
console.log(`EVM execution time: ${evmExecutionTime / 1000} seconds`);
return stages;
};
Batch Tracking
Track multiple transactions efficiently:
const trackMultipleTransactions = async (operationIds) => {
const tracker = new OperationTracker(Network.TESTNET);
// Get statuses in batch (more efficient than individual calls)
const statuses = await tracker.getOperationStatuses(operationIds);
const results = {};
for (const [operationId, statusInfo] of Object.entries(statuses)) {
results[operationId] = {
status: statusInfo.operationType,
success: statusInfo.success,
error: statusInfo.error,
};
}
return results;
};
// Usage
const operationIds = ["op1", "op2", "op3"];
const batchResults = await trackMultipleTransactions(operationIds);
Object.entries(batchResults).forEach(([id, result]) => {
console.log(`Operation ${id}: ${result.status}`);
});
Error Handling in Tracking
Handle tracking errors gracefully:
const robustTracking = async (transactionLinker, maxRetries = 3) => {
const tracker = new OperationTracker(Network.TESTNET);
let retries = 0;
while (retries < maxRetries) {
try {
const status = await tracker.getSimplifiedOperationStatus(
transactionLinker
);
if (status === "OPERATION_ID_NOT_FOUND" && retries < maxRetries - 1) {
console.log(`Operation not found, retry ${retries + 1}/${maxRetries}`);
await new Promise((resolve) => setTimeout(resolve, 15000));
retries++;
continue;
}
return status;
} catch (error) {
console.error(`Tracking attempt ${retries + 1} failed:`, error);
retries++;
if (retries >= maxRetries) {
throw new Error(
`Tracking failed after ${maxRetries} attempts: ${error.message}`
);
}
await new Promise((resolve) => setTimeout(resolve, 10000));
}
}
};
Transaction States and Types
Understanding transaction states helps with proper status handling:
Operation Types
Simplified States
Execution Stages
Wait Options
enum OperationType {
PENDING = 'PENDING', // Transaction being processed
TON_TAC_TON = 'TON-TAC-TON', // Round-trip transaction
ROLLBACK = 'ROLLBACK', // Transaction rolled back
TON_TAC = 'TON-TAC', // One-way to TAC
TAC_TON = 'TAC-TON', // Return to TON
UNKNOWN = 'UNKNOWN' // Unknown state
}
TON Transaction Finalization
For applications that need to verify the complete success of TON transaction trees, the TonTxFinalizer
utility provides comprehensive transaction finality verification by traversing the entire transaction tree.
import { TonTxFinalizer, ConsoleLogger } from "@tonappchain/sdk";
// Initialize TonTxFinalizer with TON Center API configuration
const finalizer = new TonTxFinalizer(
{
urlBuilder: (hash) =>
`https://testnet.toncenter.com/api/v3/adjacentTransactions?hash=${encodeURIComponent(
hash
)}&direction=out`,
authorization: { header: "X-API-Key", value: "your-api-key" },
},
new ConsoleLogger()
);
// Track transaction tree for complete verification
await finalizer.trackTransactionTree("TON_TX_HASH");
Constructor Options
new TonTxFinalizer(
apiConfig: {
urlBuilder: (hash: string) => string;
authorization: { header: string; value: string };
},
logger?: ILogger,
httpClient?: IHttpClient
)
Parameters:
apiConfig.urlBuilder
: Function to build the API URL for fetching adjacent transactions by hash
apiConfig.authorization
: Object specifying the header and value for API authorization
logger
: Optional logger implementing ILogger. Pass a ConsoleLogger to enable verbose output; defaults to NoopLogger
httpClient
: Optional HTTP client for making API requests; defaults to AxiosHttpClient
Methods
trackTransactionTree(hash: string, maxDepth?: number): Promise
Traverses the transaction tree starting from the given hash, following outgoing transactions up to maxDepth (default: 10). Throws an error if any transaction in the tree is not successful.
Usage Examples
// Basic transaction tree verification
const verifyTransactionSuccess = async (tonTxHash) => {
const finalizer = new TonTxFinalizer(
{
urlBuilder: (hash) =>
`https://testnet.toncenter.com/api/v3/adjacentTransactions?hash=${encodeURIComponent(
hash
)}&direction=out`,
authorization: { header: "X-API-Key", value: process.env.TON_API_KEY },
},
new ConsoleLogger()
);
try {
await finalizer.trackTransactionTree(tonTxHash);
console.log("✅ Transaction tree verified successfully");
return true;
} catch (error) {
console.error("❌ Transaction tree verification failed:", error);
return false;
}
};
// Custom depth verification for complex transactions
const verifyDeepTransactionTree = async (tonTxHash) => {
const finalizer = new TonTxFinalizer({
urlBuilder: (hash) =>
`https://mainnet.toncenter.com/api/v3/adjacentTransactions?hash=${encodeURIComponent(
hash
)}&direction=out`,
authorization: { header: "X-API-Key", value: process.env.TON_API_KEY },
});
try {
// Verify up to 15 levels deep for complex transaction trees
await finalizer.trackTransactionTree(tonTxHash, 15);
console.log("✅ Deep transaction tree verification completed");
return true;
} catch (error) {
console.error("❌ Deep verification failed:", error);
return false;
}
};
TonTxFinalizer is particularly useful for verifying the success of complex
multi-step transactions that generate multiple outgoing transactions. It
ensures that the entire transaction tree completed successfully, not just the
initial transaction.