@tonappchain/sdk 0.7.2-alpha-13 → 0.7.2-alpha-15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,7 +9,7 @@ const Logger_1 = require("./Logger");
9
9
  const OperationTracker_1 = require("./OperationTracker");
10
10
  const Utils_1 = require("./Utils");
11
11
  async function startTracking(transactionLinker, network, options) {
12
- const { customLiteSequencerEndpoints, delay = 10, maxIterationCount = Consts_1.MAX_ITERATION_COUNT, returnValue = false, tableView = true, logger = new Logger_1.NoopLogger(), contractOpener, cclAddress, } = options || {};
12
+ const { customLiteSequencerEndpoints, delay = 10, maxIterationCount = Consts_1.MAX_ITERATION_COUNT, returnValue = false, tableView = true, logger = new Logger_1.NoopLogger(), txFinalizer, contractOpener, cclAddress, } = options || {};
13
13
  const tracker = new OperationTracker_1.OperationTracker(network, customLiteSequencerEndpoints, logger);
14
14
  logger.debug(`Start tracking operation\n` +
15
15
  `caller: ${transactionLinker.caller}\n` +
@@ -66,12 +66,15 @@ async function startTracking(transactionLinker, network, options) {
66
66
  // Check if EXECUTED_IN_TON stage exists and use ContractOpener to verify transaction success
67
67
  if (profilingData.executedInTON.exists && profilingData.executedInTON.stageData?.transactions) {
68
68
  logger.debug('EXECUTED_IN_TON stage found, verifying transaction success in TON...');
69
- if (contractOpener && cclAddress) {
69
+ const finalizer = txFinalizer || contractOpener;
70
+ if (finalizer && cclAddress) {
70
71
  const transactions = profilingData.executedInTON.stageData.transactions;
71
72
  for (const tx of transactions) {
72
73
  try {
73
74
  logger.debug(`Verifying transaction: ${tx.hash}`);
74
- await contractOpener.trackTransactionTree(cclAddress, tx.hash, { maxDepth: Consts_1.DEFAULT_FIND_TX_MAX_DEPTH });
75
+ await finalizer.trackTransactionTree(cclAddress, tx.hash, {
76
+ maxDepth: Consts_1.DEFAULT_FIND_TX_MAX_DEPTH,
77
+ });
75
78
  logger.debug(`Transaction ${tx.hash} verified successfully in TON`);
76
79
  }
77
80
  catch (error) {
@@ -83,7 +86,7 @@ async function startTracking(transactionLinker, network, options) {
83
86
  }
84
87
  }
85
88
  else {
86
- logger.debug('ContractOpener or CCL address is not provided, skipping TON transaction verification');
89
+ logger.debug('Finalizer, ContractOpener or CCL address is not provided, skipping TON transaction verification');
87
90
  }
88
91
  }
89
92
  if (returnValue) {
@@ -99,7 +102,7 @@ async function startTracking(transactionLinker, network, options) {
99
102
  }
100
103
  }
101
104
  async function startTrackingMultiple(transactionLinkers, network, options) {
102
- const { customLiteSequencerEndpoints, delay = 10, maxIterationCount = Consts_1.MAX_ITERATION_COUNT, returnValue = false, tableView = true, contractOpener, logger = new Logger_1.NoopLogger(), } = options || {};
105
+ const { customLiteSequencerEndpoints, delay = 10, maxIterationCount = Consts_1.MAX_ITERATION_COUNT, returnValue = false, tableView = true, txFinalizer, contractOpener, cclAddress, logger = new Logger_1.NoopLogger(), } = options || {};
103
106
  logger.debug(`Start tracking ${transactionLinkers.length} operations`);
104
107
  const results = await Promise.all(transactionLinkers.map((linker, index) => {
105
108
  logger.debug(`\nProcessing operation ${index + 1}/${transactionLinkers.length}`);
@@ -109,7 +112,9 @@ async function startTrackingMultiple(transactionLinkers, network, options) {
109
112
  maxIterationCount,
110
113
  returnValue: true,
111
114
  tableView: false,
115
+ txFinalizer,
112
116
  contractOpener,
117
+ cclAddress,
113
118
  logger,
114
119
  });
115
120
  }));
@@ -159,12 +159,12 @@ class TONTransactionManager {
159
159
  async sendCrossChainTransaction(evmProxyMsg, sender, tx) {
160
160
  const { transaction, transactionLinker } = await this.prepareCrossChainTransaction(evmProxyMsg, sender, tx.assets, tx.options);
161
161
  await assets_1.TON.checkBalance(sender, this.config, [transaction]);
162
+ const shouldWaitForOperationId = tx.options?.waitOperationId ?? true;
162
163
  this.logger.debug(`Sending transaction: ${(0, Utils_1.formatObjectForLogging)(transactionLinker)}`);
163
164
  const sendTransactionResult = await sender.sendShardTransaction(transaction, this.config.network, this.config.TONParams.contractOpener);
164
165
  if (!sendTransactionResult.success || sendTransactionResult.error) {
165
166
  throw (0, instances_1.sendCrossChainTransactionFailedError)(sendTransactionResult.error?.message ?? 'Transaction failed to send');
166
167
  }
167
- const shouldWaitForOperationId = tx.options?.waitOperationId ?? true;
168
168
  if (!shouldWaitForOperationId) {
169
169
  return { sendTransactionResult, ...transactionLinker };
170
170
  }
@@ -17,10 +17,11 @@ class TonTxFinalizer {
17
17
  for (let i = retries; i >= 0; i--) {
18
18
  try {
19
19
  const url = this.apiConfig.urlBuilder(hash);
20
+ const authHeaders = this.apiConfig.authorization
21
+ ? { [this.apiConfig.authorization.header]: this.apiConfig.authorization.value }
22
+ : undefined;
20
23
  const response = await this.httpClient.get(url, {
21
- headers: {
22
- [this.apiConfig.authorization.header]: this.apiConfig.authorization.value,
23
- },
24
+ ...(authHeaders ? { headers: authHeaders } : {}),
24
25
  transformResponse: [Utils_1.toCamelCaseTransformer],
25
26
  });
26
27
  return response.data.transactions || [];
@@ -42,6 +42,11 @@ export declare function muldivr(a: bigint, b: bigint, c: bigint): bigint;
42
42
  * Accepts: base64, hex, or raw string
43
43
  */
44
44
  export declare function normalizeHashToBase64(hash: string): string;
45
+ /**
46
+ * Normalize hash string to hex format
47
+ * Accepts: base64, hex
48
+ */
49
+ export declare function normalizeHashToHex(hash: string): string;
45
50
  export declare function getNormalizedExtMessageHash(message: Message): string;
46
51
  export declare function retry<T>(fn: () => Promise<T>, options: {
47
52
  retries: number;
@@ -21,6 +21,7 @@ exports.getNumber = getNumber;
21
21
  exports.getString = getString;
22
22
  exports.muldivr = muldivr;
23
23
  exports.normalizeHashToBase64 = normalizeHashToBase64;
24
+ exports.normalizeHashToHex = normalizeHashToHex;
24
25
  exports.getNormalizedExtMessageHash = getNormalizedExtMessageHash;
25
26
  exports.retry = retry;
26
27
  exports.recurisivelyCollectCellStats = recurisivelyCollectCellStats;
@@ -322,23 +323,51 @@ function muldivr(a, b, c) {
322
323
  * Accepts: base64, hex, or raw string
323
324
  */
324
325
  function normalizeHashToBase64(hash) {
325
- // If already base64 (contains +, /, = or matches base64 pattern)
326
- if (/^[A-Za-z0-9+/]+={0,2}$/.test(hash)) {
327
- try {
328
- // Validate it's valid base64
329
- Buffer.from(hash, 'base64');
330
- return hash;
331
- }
332
- catch {
333
- // Fall through to hex conversion
334
- }
326
+ const input = hash.trim();
327
+ if (!input)
328
+ return input;
329
+ const hex = input.startsWith('0x') ? input.slice(2) : input;
330
+ if (/^[0-9a-fA-F]{64}$/.test(hex)) {
331
+ return Buffer.from(hex, 'hex').toString('base64');
332
+ }
333
+ const decoded = decodeBase64Like(input);
334
+ if (decoded) {
335
+ return decoded.toString('base64');
336
+ }
337
+ return input;
338
+ }
339
+ /**
340
+ * Normalize hash string to hex format
341
+ * Accepts: base64, hex
342
+ */
343
+ function normalizeHashToHex(hash) {
344
+ const input = hash.trim();
345
+ if (!input)
346
+ return input;
347
+ const maybeHex = input.startsWith('0x') ? input.slice(2) : input;
348
+ if (/^[0-9a-fA-F]+$/.test(maybeHex) && maybeHex.length % 2 === 0) {
349
+ return maybeHex.toLowerCase();
350
+ }
351
+ const decoded = decodeBase64Like(input);
352
+ if (decoded) {
353
+ return decoded.toString('hex');
354
+ }
355
+ return input;
356
+ }
357
+ function decodeBase64Like(input) {
358
+ const normalized = input.replace(/-/g, '+').replace(/_/g, '/');
359
+ const padLength = normalized.length % 4;
360
+ const padded = padLength === 0 ? normalized : normalized + '='.repeat(4 - padLength);
361
+ if (!/^[A-Za-z0-9+/]+={0,2}$/.test(padded)) {
362
+ return null;
363
+ }
364
+ try {
365
+ const buf = Buffer.from(padded, 'base64');
366
+ return buf.length > 0 ? buf : null;
335
367
  }
336
- // Try as hex
337
- if (/^[0-9a-fA-F]+$/.test(hash)) {
338
- return Buffer.from(hash, 'hex').toString('base64');
368
+ catch {
369
+ return null;
339
370
  }
340
- // Assume it's already base64
341
- return hash;
342
371
  }
343
372
  function getNormalizedExtMessageHash(message) {
344
373
  if (message.info.type !== 'external-in') {
@@ -106,7 +106,7 @@ export type AdjacentTransactionsResponse = {
106
106
  };
107
107
  export type TxFinalizerConfig = {
108
108
  urlBuilder: (hash: string) => string;
109
- authorization: {
109
+ authorization?: {
110
110
  header: string;
111
111
  value: string;
112
112
  };
@@ -456,13 +456,19 @@ export type TrackTransactionTreeParams = {
456
456
  */
457
457
  limit?: number;
458
458
  /**
459
- * Maximum depth to traverse in the transaction tree (prevents infinite loops)
459
+ * Maximum depth to traverse in the transaction tree, inclusive (prevents infinite loops)
460
460
  * @default 10
461
461
  */
462
462
  maxDepth?: number;
463
463
  /**
464
- * List of operation codes (opcodes) to skip during validation.
465
- * Transactions with these opcodes in their incoming message will not be validated.
464
+ * Maximum number of transactions to scan while searching by hash in account history.
465
+ * Prevents excessive full-history scans on high-activity accounts.
466
+ * @default 100
467
+ */
468
+ maxScannedTransactions?: number;
469
+ /**
470
+ * List of operation codes (opcodes) to skip for extra checks.
471
+ * Core phase validation is still applied.
466
472
  * @default [ 0xd53276db ] // Excess
467
473
  */
468
474
  ignoreOpcodeList?: number[];
@@ -474,13 +480,19 @@ export type TrackTransactionTreeParams = {
474
480
  * @default 'both'
475
481
  */
476
482
  direction?: 'forward' | 'backward' | 'both';
483
+ /**
484
+ * Internal option: wait for the root transaction to appear before failing with `not_found`.
485
+ * Useful right after sending a message when transaction indexing is eventually consistent.
486
+ * @default true
487
+ */
488
+ waitForRootTransaction?: boolean;
477
489
  };
478
490
  /**
479
491
  * Details about a transaction validation error
480
492
  */
481
493
  export type TransactionValidationError = {
482
494
  /**
483
- * Base64-encoded hash of the failed transaction
495
+ * Base64-encoded hash of the failed transaction (or the searched hash if not found)
484
496
  */
485
497
  txHash: string;
486
498
  /**
@@ -494,11 +506,20 @@ export type TransactionValidationError = {
494
506
  /**
495
507
  * Reason for validation failure:
496
508
  * - 'aborted': default: transaction was aborted
497
- * - 'compute_phase_missing': compute phase is missing or skipped
509
+ * - 'compute_phase_missing': compute phase is missing
498
510
  * - 'compute_phase_failed': compute phase failed (exitCode !== 0)
499
511
  * - 'action_phase_failed': action phase failed (resultCode !== 0)
512
+ * - 'not_found': transaction or message hash not found during traversal
500
513
  */
501
- reason: 'aborted' | 'compute_phase_missing' | 'compute_phase_failed' | 'action_phase_failed';
514
+ reason: 'aborted' | 'compute_phase_missing' | 'compute_phase_failed' | 'action_phase_failed' | 'not_found';
515
+ /**
516
+ * Address where the lookup was performed (for reason: 'not_found')
517
+ */
518
+ address?: string;
519
+ /**
520
+ * Hash type used in lookup (for reason: 'not_found')
521
+ */
522
+ hashType?: 'unknown' | 'in' | 'out';
502
523
  };
503
524
  /**
504
525
  * Result of transaction tree tracking and validation
@@ -508,6 +529,10 @@ export type TrackTransactionTreeResult = {
508
529
  * Whether all transactions in the tree passed validation
509
530
  */
510
531
  success: boolean;
532
+ /**
533
+ * Count of unique transactions checked during traversal
534
+ */
535
+ checkedCount?: number;
511
536
  /**
512
537
  * Details about the first validation error encountered (if any)
513
538
  */
@@ -530,6 +555,8 @@ export type GetTransactionsOptions = {
530
555
  timeoutMs?: number;
531
556
  /** Delay between retry attempts in milliseconds */
532
557
  retryDelayMs?: number;
558
+ /** Internal scan guard: maximum transactions to inspect while traversing history */
559
+ maxScannedTransactions?: number;
533
560
  };
534
561
  export type AddressInformation = {
535
562
  /** Information about the last transaction of the address */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tonappchain/sdk",
3
- "version": "0.7.2-alpha-13",
3
+ "version": "0.7.2-alpha-15",
4
4
  "repository": "https://github.com/TacBuild/tac-sdk.git",
5
5
  "author": "TAC. <developers@tac>",
6
6
  "license": "MIT",