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

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.
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RetryableContractOpener = void 0;
4
4
  exports.createDefaultRetryableOpener = createDefaultRetryableOpener;
5
5
  const ton_1 = require("@ton/ton");
6
+ const errors_1 = require("../errors");
6
7
  const instances_1 = require("../errors/instances");
7
8
  const Consts_1 = require("../sdk/Consts");
8
9
  const Struct_1 = require("../structs/Struct");
@@ -16,24 +17,40 @@ class RetryableContractOpener {
16
17
  }
17
18
  this.openerConfigs = openerConfigs;
18
19
  this.logger = logger;
20
+ if (logger) {
21
+ this.applyLoggerToOpeners(logger);
22
+ }
23
+ }
24
+ setLogger(logger) {
25
+ if (!this.logger) {
26
+ this.logger = logger;
27
+ }
28
+ this.applyLoggerToOpeners(logger);
29
+ }
30
+ applyLoggerToOpeners(logger) {
31
+ for (const config of this.openerConfigs) {
32
+ config.opener.setLogger(logger);
33
+ }
19
34
  }
20
35
  async getTransactions(address, opts) {
21
- const result = await this.executeWithFallback((config) => config.opener.getTransactions(address, opts));
22
- if (result.success && result.data) {
36
+ const result = await this.executeWithFallback((config) => config.opener.getTransactions(address, opts), {
37
+ operationName: 'getTransactions',
38
+ });
39
+ if (result.success && result.data !== undefined) {
23
40
  return result.data;
24
41
  }
25
42
  throw result.lastError || (0, instances_1.allContractOpenerFailedError)('Failed to get transactions');
26
43
  }
27
44
  async getTransactionByHash(address, hash, opts) {
28
- const result = await this.executeWithFallback((config) => config.opener.getTransactionByHash(address, hash, opts));
29
- if (result.success && result.data) {
30
- return result.data;
45
+ const result = await this.executeWithFallback((config) => config.opener.getTransactionByHash(address, hash, opts), { operationName: 'getTransactionByHash' });
46
+ if (result.success) {
47
+ return result.data ?? null;
31
48
  }
32
49
  throw result.lastError || (0, instances_1.allContractOpenerFailedError)('Failed to get transaction by hash');
33
50
  }
34
51
  async getAdjacentTransactions(address, hash, opts) {
35
- const result = await this.executeWithFallback((config) => config.opener.getAdjacentTransactions(address, hash, opts));
36
- if (result.success && result.data) {
52
+ const result = await this.executeWithFallback((config) => config.opener.getAdjacentTransactions(address, hash, opts), { operationName: 'getAdjacentTransactions' });
53
+ if (result.success && result.data !== undefined) {
37
54
  return result.data;
38
55
  }
39
56
  throw result.lastError || (0, instances_1.allContractOpenerFailedError)('Failed to get adjacent transactions');
@@ -44,42 +61,48 @@ class RetryableContractOpener {
44
61
  return this.createRetryableContract(contract, src);
45
62
  }
46
63
  async getContractState(address) {
47
- const result = await this.executeWithFallback((config) => config.opener.getContractState(address));
48
- if (result.success && result.data) {
64
+ const result = await this.executeWithFallback((config) => config.opener.getContractState(address), {
65
+ operationName: 'getContractState',
66
+ });
67
+ if (result.success && result.data !== undefined) {
49
68
  return result.data;
50
69
  }
51
70
  throw result.lastError || (0, instances_1.allContractOpenerFailedError)('Failed to get contract state');
52
71
  }
53
72
  async getAddressInformation(address) {
54
- const result = await this.executeWithFallback((config) => config.opener.getAddressInformation(address));
55
- if (result.success && result.data) {
73
+ const result = await this.executeWithFallback((config) => config.opener.getAddressInformation(address), {
74
+ operationName: 'getAddressInformation',
75
+ });
76
+ if (result.success && result.data !== undefined) {
56
77
  return result.data;
57
78
  }
58
79
  throw result.lastError || (0, instances_1.allContractOpenerFailedError)('Failed to get address information');
59
80
  }
60
81
  async getConfig() {
61
- const result = await this.executeWithFallback((config) => config.opener.getConfig());
62
- if (result.success && result.data) {
82
+ const result = await this.executeWithFallback((config) => config.opener.getConfig(), {
83
+ operationName: 'getConfig',
84
+ });
85
+ if (result.success && result.data !== undefined) {
63
86
  return result.data;
64
87
  }
65
88
  throw result.lastError || (0, instances_1.allContractOpenerFailedError)('Failed to get blockchain config');
66
89
  }
67
90
  async getTransactionByTxHash(address, txHash, opts) {
68
- const result = await this.executeWithFallback((config) => config.opener.getTransactionByTxHash(address, txHash, opts));
91
+ const result = await this.executeWithFallback((config) => config.opener.getTransactionByTxHash(address, txHash, opts), { operationName: 'getTransactionByTxHash' });
69
92
  if (result.success) {
70
93
  return result.data ?? null;
71
94
  }
72
95
  throw result.lastError || (0, instances_1.allContractOpenerFailedError)('Failed to get transaction by transaction hash');
73
96
  }
74
97
  async getTransactionByInMsgHash(address, msgHash, opts) {
75
- const result = await this.executeWithFallback((config) => config.opener.getTransactionByInMsgHash(address, msgHash, opts));
98
+ const result = await this.executeWithFallback((config) => config.opener.getTransactionByInMsgHash(address, msgHash, opts), { operationName: 'getTransactionByInMsgHash' });
76
99
  if (result.success) {
77
100
  return result.data ?? null;
78
101
  }
79
102
  throw result.lastError || (0, instances_1.allContractOpenerFailedError)('Failed to get transaction by message hash');
80
103
  }
81
104
  async getTransactionByOutMsgHash(address, msgHash, opts) {
82
- const result = await this.executeWithFallback((config) => config.opener.getTransactionByOutMsgHash(address, msgHash, opts));
105
+ const result = await this.executeWithFallback((config) => config.opener.getTransactionByOutMsgHash(address, msgHash, opts), { operationName: 'getTransactionByOutMsgHash' });
83
106
  if (result.success) {
84
107
  return result.data ?? null;
85
108
  }
@@ -91,34 +114,76 @@ class RetryableContractOpener {
91
114
  }
92
115
  }
93
116
  async trackTransactionTree(address, hash, params) {
94
- const result = await this.executeWithFallback(async (config) => {
95
- return config.opener.trackTransactionTree(address, hash, params);
117
+ const result = await this.executeWithFallback(async (config) => config.opener.trackTransactionTree(address, hash, params), {
118
+ shouldFallbackOnError: (error) => this.isTransportError(error),
119
+ operationName: 'trackTransactionTree',
96
120
  });
97
121
  if (!result.success) {
122
+ if (result.lastError instanceof errors_1.TransactionError) {
123
+ throw result.lastError;
124
+ }
98
125
  throw result.lastError || (0, instances_1.allContractOpenerFailedError)('Failed to track transaction tree');
99
126
  }
100
127
  }
101
128
  async trackTransactionTreeWithResult(address, hash, params) {
102
- const result = await this.executeWithFallback(async (config) => {
103
- return config.opener.trackTransactionTreeWithResult(address, hash, params);
129
+ const result = await this.executeWithFallback(async (config) => config.opener.trackTransactionTreeWithResult(address, hash, params), {
130
+ shouldFallbackOnError: (error) => this.isTransportError(error),
131
+ operationName: 'trackTransactionTreeWithResult',
104
132
  });
105
133
  if (!result.success) {
134
+ if (result.lastError instanceof errors_1.TransactionError) {
135
+ throw result.lastError;
136
+ }
106
137
  throw result.lastError || (0, instances_1.allContractOpenerFailedError)('Failed to track transaction tree with result');
107
138
  }
108
139
  return result.data;
109
140
  }
110
- async executeWithFallback(operation) {
141
+ async executeWithFallback(operation, options = {}) {
142
+ const { useRetries = true, shouldFallbackOnError, operationName = 'operation' } = options;
111
143
  let lastError;
112
- for (const config of this.openerConfigs) {
113
- const result = await this.tryWithRetries(() => operation(config), config);
144
+ for (let index = 0; index < this.openerConfigs.length; index++) {
145
+ const config = this.openerConfigs[index];
146
+ const openerLabel = `opener ${index + 1}/${this.openerConfigs.length}`;
147
+ this.logger?.debug(`[RetryableContractOpener] ${operationName}: trying ${openerLabel}${useRetries ? ` (max retries ${config.retries})` : ' (single attempt)'}`);
148
+ const result = useRetries
149
+ ? await this.tryWithRetries(() => operation(config), config, `${operationName} ${openerLabel}`)
150
+ : await this.trySingleAttempt(() => operation(config));
114
151
  if (result.success) {
152
+ this.logger?.debug(`[RetryableContractOpener] ${operationName}: ${openerLabel} succeeded`);
115
153
  return { success: true, data: result.data };
116
154
  }
117
155
  lastError = result.lastError;
156
+ if (lastError) {
157
+ this.logger?.debug(`[RetryableContractOpener] ${operationName}: ${openerLabel} failed: ${lastError.message}`);
158
+ }
159
+ if (lastError instanceof errors_1.TransactionError) {
160
+ this.logger?.debug(`[RetryableContractOpener] ${operationName}: stopping fallback because of TransactionError`);
161
+ return { success: false, lastError };
162
+ }
163
+ if (lastError && this.isContractExecutionError(lastError)) {
164
+ this.logger?.debug(`[RetryableContractOpener] ${operationName}: stopping fallback because of contract execution error`);
165
+ return { success: false, lastError };
166
+ }
167
+ if (lastError && shouldFallbackOnError) {
168
+ const shouldFallback = shouldFallbackOnError(lastError);
169
+ if (!shouldFallback) {
170
+ this.logger?.debug(`[RetryableContractOpener] ${operationName}: stopping fallback due to non-transport error`);
171
+ return { success: false, lastError };
172
+ }
173
+ }
118
174
  }
119
175
  return { success: false, lastError };
120
176
  }
121
- async tryWithRetries(operation, config) {
177
+ async trySingleAttempt(operation) {
178
+ try {
179
+ const data = await operation();
180
+ return { success: true, data };
181
+ }
182
+ catch (error) {
183
+ return { success: false, lastError: error };
184
+ }
185
+ }
186
+ async tryWithRetries(operation, config, operationContext = 'operation') {
122
187
  let lastError;
123
188
  for (let attempt = 0; attempt <= config.retries; attempt++) {
124
189
  try {
@@ -127,13 +192,45 @@ class RetryableContractOpener {
127
192
  }
128
193
  catch (error) {
129
194
  lastError = error;
195
+ if (lastError instanceof errors_1.TransactionError || this.isContractExecutionError(lastError)) {
196
+ return { success: false, lastError };
197
+ }
130
198
  if (attempt < config.retries) {
199
+ this.logger?.debug(`[RetryableContractOpener] ${operationContext}: attempt ${attempt + 1}/${config.retries + 1} failed, retrying in ${config.retryDelay}ms`);
131
200
  await sleep(config.retryDelay);
132
201
  }
133
202
  }
134
203
  }
135
204
  return { success: false, lastError };
136
205
  }
206
+ isContractExecutionError(error) {
207
+ const errorWithExit = error;
208
+ if (typeof errorWithExit.exitCode === 'number' ||
209
+ typeof errorWithExit.vmExitCode === 'number' ||
210
+ typeof errorWithExit.exit_code === 'number') {
211
+ return true;
212
+ }
213
+ const message = String(error.message ?? error);
214
+ return (/unable to execute get method/i.test(message) ||
215
+ /exit[_\s-]*code\s*:\s*\d+/i.test(message) ||
216
+ /vm exit code\s*:\s*\d+/i.test(message));
217
+ }
218
+ isTransportError(error) {
219
+ if (error instanceof errors_1.TransactionError) {
220
+ return false;
221
+ }
222
+ const errorWithResponse = error;
223
+ const status = errorWithResponse.response?.status ?? errorWithResponse.response?.statusCode;
224
+ if (typeof status === 'number') {
225
+ return status === 429 || status >= 500;
226
+ }
227
+ const code = errorWithResponse.code ?? '';
228
+ if (['ETIMEDOUT', 'ECONNRESET', 'ENOTFOUND', 'EAI_AGAIN'].includes(code)) {
229
+ return true;
230
+ }
231
+ const message = String(error.message ?? error);
232
+ return /too many requests|timeout|timed out|network|connection/i.test(message);
233
+ }
137
234
  createRetryableContract(contract, src) {
138
235
  return new Proxy(contract, {
139
236
  get: (target, prop) => {
@@ -147,6 +244,7 @@ class RetryableContractOpener {
147
244
  });
148
245
  }
149
246
  async callMethodAcrossOpeners(methodName, args, src) {
247
+ const addressLabel = src.address ? src.address.toString() : 'unknown';
150
248
  const result = await this.executeWithFallback((config) => {
151
249
  const contract = config.opener.open(src);
152
250
  const method = Reflect.get(contract, methodName);
@@ -154,7 +252,7 @@ class RetryableContractOpener {
154
252
  throw new Error(`Method ${String(methodName)} is not a function`);
155
253
  }
156
254
  return method.call(contract, ...args);
157
- });
255
+ }, { operationName: `${String(methodName)} (address=${addressLabel})` });
158
256
  if (result.success)
159
257
  return result.data;
160
258
  throw result.lastError || (0, instances_1.allContractOpenerFailedError)('failed to call method in contract');
@@ -163,19 +261,22 @@ class RetryableContractOpener {
163
261
  exports.RetryableContractOpener = RetryableContractOpener;
164
262
  async function createDefaultRetryableOpener(tonRpcEndpoint, networkType, maxRetries = Consts_1.DEFAULT_RETRY_MAX_COUNT, retryDelay = Consts_1.DEFAULT_RETRY_DELAY_MS, logger) {
165
263
  const openers = [];
166
- const tonClient = new ton_1.TonClient({ endpoint: new URL('api/v2/jsonRPC', tonRpcEndpoint).toString() });
167
- const opener = (0, TonClientOpener_1.tonClientOpener)(tonClient);
264
+ const tonClient = new ton_1.TonClient({
265
+ endpoint: new URL('api/v2/jsonRPC', tonRpcEndpoint).toString(),
266
+ timeout: Consts_1.DEFAULT_HTTP_CLIENT_TIMEOUT_MS,
267
+ });
268
+ const opener = (0, TonClientOpener_1.tonClientOpener)(tonClient, logger);
168
269
  openers.push({ opener, retries: maxRetries, retryDelay });
169
270
  if (networkType !== Struct_1.Network.DEV) {
170
271
  try {
171
- const opener = await (0, TonClientOpener_1.orbsOpener)(networkType);
272
+ const opener = await (0, TonClientOpener_1.orbsOpener)(networkType, logger);
172
273
  openers.push({ opener: opener, retries: maxRetries, retryDelay });
173
274
  }
174
275
  catch {
175
276
  // skip opener in case of failure
176
277
  }
177
278
  try {
178
- const opener4 = await (0, TonClient4Opener_1.orbsOpener4)(networkType);
279
+ const opener4 = await (0, TonClient4Opener_1.orbsOpener4)(networkType, Consts_1.DEFAULT_HTTP_CLIENT_TIMEOUT_MS, logger);
179
280
  openers.push({ opener: opener4, retries: maxRetries, retryDelay });
180
281
  }
181
282
  catch {
@@ -1,10 +1,11 @@
1
1
  import { Address, Contract, OpenedContract, TonClient4, Transaction } from '@ton/ton';
2
+ import { ILogger } from '../interfaces';
2
3
  import { AddressInformation, ContractState, GetTransactionsOptions, Network } from '../structs/Struct';
3
4
  import { BaseContractOpener } from './BaseContractOpener';
4
5
  export declare class TonClient4Opener extends BaseContractOpener {
5
6
  private readonly client4;
6
- constructor(client4: TonClient4);
7
- static create(endpoint: string, timeout?: number): TonClient4Opener;
7
+ constructor(client4: TonClient4, logger?: ILogger);
8
+ static create(endpoint: string, timeout?: number, logger?: ILogger): TonClient4Opener;
8
9
  open<T extends Contract>(contract: T): OpenedContract<T>;
9
10
  getContractState(address: Address): Promise<ContractState>;
10
11
  getTransactions(address: Address, opts: GetTransactionsOptions): Promise<Transaction[]>;
@@ -15,7 +16,8 @@ export declare class TonClient4Opener extends BaseContractOpener {
15
16
  * Creates a TonClient4Opener instance using TonHub public API
16
17
  * @param network Network to connect to (mainnet or testnet)
17
18
  * @param timeout Request timeout in milliseconds
19
+ * @param logger
18
20
  */
19
- export declare function tonHubApi4Opener(network: Network, timeout?: number): TonClient4Opener;
20
- export declare function tonClient4Opener(client: TonClient4): TonClient4Opener;
21
- export declare function orbsOpener4(network: Network, timeout?: number): Promise<TonClient4Opener>;
21
+ export declare function tonHubApi4Opener(network: Network, timeout?: number, logger?: ILogger): TonClient4Opener;
22
+ export declare function tonClient4Opener(client: TonClient4, logger?: ILogger): TonClient4Opener;
23
+ export declare function orbsOpener4(network: Network, timeout?: number, logger?: ILogger): Promise<TonClient4Opener>;
@@ -10,13 +10,13 @@ const Struct_1 = require("../structs/Struct");
10
10
  const BaseContractOpener_1 = require("./BaseContractOpener");
11
11
  const OpenerUtils_1 = require("./OpenerUtils");
12
12
  class TonClient4Opener extends BaseContractOpener_1.BaseContractOpener {
13
- constructor(client4) {
14
- super();
13
+ constructor(client4, logger) {
14
+ super(logger);
15
15
  this.client4 = client4;
16
16
  }
17
- static create(endpoint, timeout = 10000) {
17
+ static create(endpoint, timeout = 10000, logger) {
18
18
  const client4 = new ton_1.TonClient4({ endpoint, timeout });
19
- return new TonClient4Opener(client4);
19
+ return new TonClient4Opener(client4, logger);
20
20
  }
21
21
  open(contract) {
22
22
  return this.client4.open(contract);
@@ -71,16 +71,17 @@ exports.TonClient4Opener = TonClient4Opener;
71
71
  * Creates a TonClient4Opener instance using TonHub public API
72
72
  * @param network Network to connect to (mainnet or testnet)
73
73
  * @param timeout Request timeout in milliseconds
74
+ * @param logger
74
75
  */
75
- function tonHubApi4Opener(network, timeout = Consts_1.DEFAULT_HTTP_CLIENT_TIMEOUT_MS) {
76
+ function tonHubApi4Opener(network, timeout = Consts_1.DEFAULT_HTTP_CLIENT_TIMEOUT_MS, logger) {
76
77
  const endpoint = network === Struct_1.Network.MAINNET ? 'https://mainnet-v4.tonhubapi.com' : 'https://testnet-v4.tonhubapi.com';
77
- return TonClient4Opener.create(endpoint, timeout);
78
+ return TonClient4Opener.create(endpoint, timeout, logger);
78
79
  }
79
- function tonClient4Opener(client) {
80
- return new TonClient4Opener(client);
80
+ function tonClient4Opener(client, logger) {
81
+ return new TonClient4Opener(client, logger);
81
82
  }
82
- async function orbsOpener4(network, timeout = Consts_1.DEFAULT_HTTP_CLIENT_TIMEOUT_MS) {
83
+ async function orbsOpener4(network, timeout = Consts_1.DEFAULT_HTTP_CLIENT_TIMEOUT_MS, logger) {
83
84
  const endpoint = await (0, OpenerUtils_1.getHttpV4EndpointWithRetry)(network);
84
85
  const client = new ton_1.TonClient4({ endpoint, timeout });
85
- return new TonClient4Opener(client);
86
+ return new TonClient4Opener(client, logger);
86
87
  }
@@ -1,16 +1,17 @@
1
1
  import { Address, Contract, OpenedContract, TonClient, Transaction } from '@ton/ton';
2
+ import { ILogger } from '../interfaces';
2
3
  import { AddressInformation, ContractState, GetTransactionsOptions, Network } from '../structs/Struct';
3
4
  import { BaseContractOpener } from './BaseContractOpener';
4
5
  export declare class TonClientOpener extends BaseContractOpener {
5
6
  private readonly client;
6
7
  private readonly httpClient;
7
- constructor(client: TonClient);
8
- static create(endpoint: string, timeout?: number): TonClientOpener;
8
+ constructor(client: TonClient, logger?: ILogger);
9
+ static create(endpoint: string, timeout?: number, logger?: ILogger): TonClientOpener;
9
10
  open<T extends Contract>(contract: T): OpenedContract<T>;
10
11
  getContractState(address: Address): Promise<ContractState>;
11
12
  getTransactions(address: Address, opts: GetTransactionsOptions): Promise<Transaction[]>;
12
13
  getAddressInformation(addr: Address): Promise<AddressInformation>;
13
14
  getConfig(): Promise<string>;
14
15
  }
15
- export declare function tonClientOpener(client: TonClient): TonClientOpener;
16
- export declare function orbsOpener(network: Network): Promise<TonClientOpener>;
16
+ export declare function tonClientOpener(client: TonClient, logger?: ILogger): TonClientOpener;
17
+ export declare function orbsOpener(network: Network, logger?: ILogger): Promise<TonClientOpener>;
@@ -6,17 +6,18 @@ exports.orbsOpener = orbsOpener;
6
6
  const ton_1 = require("@ton/ton");
7
7
  const AxiosHttpClient_1 = require("../sdk/AxiosHttpClient");
8
8
  const Consts_1 = require("../sdk/Consts");
9
+ const Utils_1 = require("../sdk/Utils");
9
10
  const BaseContractOpener_1 = require("./BaseContractOpener");
10
11
  const OpenerUtils_1 = require("./OpenerUtils");
11
12
  class TonClientOpener extends BaseContractOpener_1.BaseContractOpener {
12
- constructor(client) {
13
- super();
13
+ constructor(client, logger) {
14
+ super(logger);
14
15
  this.client = client;
15
16
  this.httpClient = new AxiosHttpClient_1.AxiosHttpClient({ timeout: Consts_1.DEFAULT_HTTP_CLIENT_TIMEOUT_MS });
16
17
  }
17
- static create(endpoint, timeout = Consts_1.DEFAULT_HTTP_CLIENT_TIMEOUT_MS) {
18
+ static create(endpoint, timeout = Consts_1.DEFAULT_HTTP_CLIENT_TIMEOUT_MS, logger) {
18
19
  const client = new ton_1.TonClient({ endpoint, timeout });
19
- return new TonClientOpener(client);
20
+ return new TonClientOpener(client, logger);
20
21
  }
21
22
  open(contract) {
22
23
  return this.client.open(contract);
@@ -29,7 +30,8 @@ class TonClientOpener extends BaseContractOpener_1.BaseContractOpener {
29
30
  const clientOpts = {
30
31
  limit: opts.limit ?? Consts_1.DEFAULT_FIND_TX_LIMIT,
31
32
  lt: opts.lt,
32
- hash: opts.hash,
33
+ // TonClient API expects base64 transaction hash and converts it to hex internally.
34
+ hash: opts.hash ? (0, Utils_1.normalizeHashToBase64)(opts.hash) : undefined,
33
35
  to_lt: opts.to_lt,
34
36
  inclusive: opts.inclusive,
35
37
  archival: opts.archival,
@@ -60,11 +62,11 @@ class TonClientOpener extends BaseContractOpener_1.BaseContractOpener {
60
62
  }
61
63
  }
62
64
  exports.TonClientOpener = TonClientOpener;
63
- function tonClientOpener(client) {
64
- return new TonClientOpener(client);
65
+ function tonClientOpener(client, logger) {
66
+ return new TonClientOpener(client, logger);
65
67
  }
66
- async function orbsOpener(network) {
68
+ async function orbsOpener(network, logger) {
67
69
  const endpoint = await (0, OpenerUtils_1.getHttpEndpointWithRetry)(network);
68
- const client = new ton_1.TonClient({ endpoint });
69
- return new TonClientOpener(client);
70
+ const client = new ton_1.TonClient({ endpoint, timeout: Consts_1.DEFAULT_HTTP_CLIENT_TIMEOUT_MS });
71
+ return new TonClientOpener(client, logger);
70
72
  }
@@ -1,6 +1,7 @@
1
1
  import { SandboxContract } from '@ton/sandbox';
2
2
  import type { Address, Contract, OpenedContract, Transaction } from '@ton/ton';
3
3
  import { AddressInformation, ContractState, GetTransactionsOptions, TrackTransactionTreeParams, TrackTransactionTreeResult } from '../structs/Struct';
4
+ import { ILogger } from './ILogger';
4
5
  export interface ContractOpener {
5
6
  /**
6
7
  * Opens a contract for interaction using the underlying client (lite client, sandbox, etc.).
@@ -78,7 +79,7 @@ export interface ContractOpener {
78
79
  * @param address Root account address
79
80
  * @param hash Root transaction or message hash
80
81
  * @param params Tracking parameters (maxDepth, ignoreOpcodeList, etc.)
81
- * @throws Error if any transaction in the tree failed
82
+ * @throws Error if any transaction in the tree failed or if a hash is not found
82
83
  */
83
84
  trackTransactionTree(address: string, hash: string, params?: TrackTransactionTreeParams): Promise<void>;
84
85
  /**
@@ -87,7 +88,8 @@ export interface ContractOpener {
87
88
  * @param address Root account address
88
89
  * @param hash Root transaction or message hash
89
90
  * @param params Tracking parameters (maxDepth, ignoreOpcodeList, etc.)
90
- * @returns Result object with success flag and error details if validation failed
91
+ * @returns Result object with success flag and error details if validation failed or a hash was not found
91
92
  */
92
93
  trackTransactionTreeWithResult(address: string, hash: string, params?: TrackTransactionTreeParams): Promise<TrackTransactionTreeResult>;
94
+ setLogger(logger: ILogger): void;
93
95
  }
@@ -2,7 +2,7 @@ import { Wallet } from 'ethers';
2
2
  import { JettonMinterData, NFTItemData } from '../../artifacts/tonTypes';
3
3
  import { FT, NFT } from '../assets';
4
4
  import type { SenderAbstraction } from '../sender';
5
- import { AssetFromFTArg, AssetFromNFTCollectionArg, AssetFromNFTItemArg, AssetLike, BatchCrossChainTxWithAssetLike, CrossChainPayloadResult, CrossChainTransactionOptions, CrossChainTransactionsOptions, CrosschainTx, EVMAddress, EvmProxyMsg, ExecutionFeeEstimationResult, NFTAddressType, SuggestedTVMExecutorFee, TACSimulationParams, TACSimulationResult, TransactionLinkerWithOperationId, TVMAddress, UserWalletBalanceExtended, WaitOptions } from '../structs/Struct';
5
+ import { AssetFromFTArg, AssetFromNFTCollectionArg, AssetFromNFTItemArg, AssetLike, BatchCrossChainTxWithAssetLike, CrossChainPayloadResult, CrossChainTransactionOptions, CrossChainTransactionsOptions, CrosschainTx, EVMAddress, EvmProxyMsg, ExecutionFeeEstimationResult, NFTAddressType, SuggestedTVMExecutorFee, TacGasPrice, TACSimulationParams, TACSimulationResult, TransactionLinkerWithOperationId, TVMAddress, UserWalletBalanceExtended, WaitOptions } from '../structs/Struct';
6
6
  import { Asset } from './Asset';
7
7
  import { ContractOpener } from './ContractOpener';
8
8
  import { IConfiguration } from './IConfiguration';
@@ -201,4 +201,9 @@ export interface ITacSDK {
201
201
  * @returns Promise with the prepared transaction payloads.
202
202
  */
203
203
  prepareCrossChainTransactionPayload(evmProxyMsg: EvmProxyMsg, senderAddress: string, assets?: AssetLike[], options?: CrossChainTransactionOptions): Promise<CrossChainPayloadResult[]>;
204
+ /**
205
+ * Returns TAC gas price for the current network.
206
+ * @returns Promise resolving to the gas price in wei (bigint).
207
+ */
208
+ getTACGasPrice(): Promise<TacGasPrice>;
204
209
  }
@@ -55,6 +55,7 @@ class Configuration {
55
55
  (await (0, adapters_1.createDefaultRetryableOpener)(artifacts.TON_RPC_ENDPOINT_BY_TAC, network, 5, delay, logger));
56
56
  settingsAddress = TONParams?.settingsAddress ?? artifacts.TON_SETTINGS_ADDRESS;
57
57
  }
58
+ contractOpener.setLogger(logger);
58
59
  const settings = contractOpener.open(artifacts.ton.wrappers.Settings.createFromAddress(ton_1.Address.parse(settingsAddress)));
59
60
  const allSettingsSlice = (await settings.getAll()).beginParse();
60
61
  const allSettings = allSettingsSlice.loadDictDirect(ton_1.Dictionary.Keys.BigUint(256), ton_1.Dictionary.Values.Cell());
@@ -14,13 +14,17 @@ export declare const ONE_YEAR_SECONDS: number;
14
14
  export declare const TON_DECIMALS = 9;
15
15
  export declare const TAC_DECIMALS = 18;
16
16
  export declare const FIVE_MINUTES: number;
17
+ export declare const MINUTE: number;
17
18
  export declare const TON_BURN_ADDRESS = "EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c";
18
19
  export declare const DEFAULT_HTTP_CLIENT_TIMEOUT_MS = 30000;
19
- export declare const DEFAULT_FIND_TX_TIMEOUT_MS = 180000;
20
20
  export declare const DEFAULT_RETRY_MAX_COUNT = 5;
21
21
  export declare const DEFAULT_RETRY_DELAY_MS = 1000;
22
- export declare const DEFAULT_FIND_TX_LIMIT = 10;
22
+ export declare const DEFAULT_FIND_TX_LIMIT = 100;
23
+ export declare const DEFAULT_MAX_SCANNED_TRANSACTIONS = 100;
23
24
  export declare const DEFAULT_FIND_TX_ARCHIVAL = true;
24
25
  export declare const DEFAULT_FIND_TX_MAX_DEPTH = 10;
26
+ export declare const DEFAULT_WAIT_FOR_ROOT_TRANSACTION = true;
27
+ export declare const DEFAULT_WAIT_FOR_ROOT_TRANSACTION_TIMEOUT_MS: number;
28
+ export declare const DEFAULT_WAIT_FOR_ROOT_TRANSACTION_RETRY_DELAY_MS = 1000;
25
29
  export declare const IGNORE_MSG_VALUE_1_NANO = 1n;
26
30
  export declare const IGNORE_OPCODE: number[];
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.IGNORE_OPCODE = exports.IGNORE_MSG_VALUE_1_NANO = exports.DEFAULT_FIND_TX_MAX_DEPTH = exports.DEFAULT_FIND_TX_ARCHIVAL = exports.DEFAULT_FIND_TX_LIMIT = exports.DEFAULT_RETRY_DELAY_MS = exports.DEFAULT_RETRY_MAX_COUNT = exports.DEFAULT_FIND_TX_TIMEOUT_MS = exports.DEFAULT_HTTP_CLIENT_TIMEOUT_MS = exports.TON_BURN_ADDRESS = exports.FIVE_MINUTES = exports.TAC_DECIMALS = exports.TON_DECIMALS = exports.ONE_YEAR_SECONDS = exports.FIFTEEN_MINUTES = exports.TAC_SYMBOL = exports.TON_SYMBOL = exports.MAX_MSG_DEPTH = exports.MAX_HIGHLOAD_GROUP_MSG_NUM = exports.MAX_EXT_MSG_SIZE = exports.SOLIDITY_METHOD_NAME_REGEX = exports.SOLIDITY_SIGNATURE_REGEX = exports.DEFAULT_DELAY = exports.MAX_ITERATION_COUNT = exports.NFT_TRANSFER_FORWARD_TON_AMOUNT = exports.JETTON_TRANSFER_FORWARD_TON_AMOUNT = void 0;
3
+ exports.IGNORE_OPCODE = exports.IGNORE_MSG_VALUE_1_NANO = exports.DEFAULT_WAIT_FOR_ROOT_TRANSACTION_RETRY_DELAY_MS = exports.DEFAULT_WAIT_FOR_ROOT_TRANSACTION_TIMEOUT_MS = exports.DEFAULT_WAIT_FOR_ROOT_TRANSACTION = exports.DEFAULT_FIND_TX_MAX_DEPTH = exports.DEFAULT_FIND_TX_ARCHIVAL = exports.DEFAULT_MAX_SCANNED_TRANSACTIONS = exports.DEFAULT_FIND_TX_LIMIT = exports.DEFAULT_RETRY_DELAY_MS = exports.DEFAULT_RETRY_MAX_COUNT = exports.DEFAULT_HTTP_CLIENT_TIMEOUT_MS = exports.TON_BURN_ADDRESS = exports.MINUTE = exports.FIVE_MINUTES = exports.TAC_DECIMALS = exports.TON_DECIMALS = exports.ONE_YEAR_SECONDS = exports.FIFTEEN_MINUTES = exports.TAC_SYMBOL = exports.TON_SYMBOL = exports.MAX_MSG_DEPTH = exports.MAX_HIGHLOAD_GROUP_MSG_NUM = exports.MAX_EXT_MSG_SIZE = exports.SOLIDITY_METHOD_NAME_REGEX = exports.SOLIDITY_SIGNATURE_REGEX = exports.DEFAULT_DELAY = exports.MAX_ITERATION_COUNT = exports.NFT_TRANSFER_FORWARD_TON_AMOUNT = exports.JETTON_TRANSFER_FORWARD_TON_AMOUNT = void 0;
4
4
  const ton_1 = require("@ton/ton");
5
5
  exports.JETTON_TRANSFER_FORWARD_TON_AMOUNT = (0, ton_1.toNano)(0.2);
6
6
  exports.NFT_TRANSFER_FORWARD_TON_AMOUNT = (0, ton_1.toNano)(0.3);
@@ -18,14 +18,18 @@ exports.ONE_YEAR_SECONDS = 365 * 24 * 3600;
18
18
  exports.TON_DECIMALS = 9;
19
19
  exports.TAC_DECIMALS = 18;
20
20
  exports.FIVE_MINUTES = 5 * 60 * 1000;
21
+ exports.MINUTE = 60 * 1000;
21
22
  exports.TON_BURN_ADDRESS = 'EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c';
22
23
  exports.DEFAULT_HTTP_CLIENT_TIMEOUT_MS = 30000;
23
- exports.DEFAULT_FIND_TX_TIMEOUT_MS = 180000;
24
24
  exports.DEFAULT_RETRY_MAX_COUNT = 5;
25
25
  exports.DEFAULT_RETRY_DELAY_MS = 1000;
26
- exports.DEFAULT_FIND_TX_LIMIT = 10;
26
+ exports.DEFAULT_FIND_TX_LIMIT = 100;
27
+ exports.DEFAULT_MAX_SCANNED_TRANSACTIONS = 100;
27
28
  exports.DEFAULT_FIND_TX_ARCHIVAL = true;
28
29
  exports.DEFAULT_FIND_TX_MAX_DEPTH = 10;
30
+ exports.DEFAULT_WAIT_FOR_ROOT_TRANSACTION = true;
31
+ exports.DEFAULT_WAIT_FOR_ROOT_TRANSACTION_TIMEOUT_MS = exports.MINUTE;
32
+ exports.DEFAULT_WAIT_FOR_ROOT_TRANSACTION_RETRY_DELAY_MS = 1000;
29
33
  exports.IGNORE_MSG_VALUE_1_NANO = 1n;
30
34
  exports.IGNORE_OPCODE = [
31
35
  0xd53276db, // Excess
@@ -1,4 +1,5 @@
1
1
  import { ContractOpener, ILogger } from '../interfaces';
2
+ import { ITxFinalizer } from '../interfaces/ITxFinalizer';
2
3
  import { ExecutionStages, Network, TransactionLinker } from '../structs/Struct';
3
4
  export declare function startTracking(transactionLinker: TransactionLinker, network: Network, options?: {
4
5
  customLiteSequencerEndpoints?: string[];
@@ -7,6 +8,7 @@ export declare function startTracking(transactionLinker: TransactionLinker, netw
7
8
  returnValue?: boolean;
8
9
  tableView?: boolean;
9
10
  logger?: ILogger;
11
+ txFinalizer?: ITxFinalizer;
10
12
  contractOpener?: ContractOpener;
11
13
  cclAddress?: string;
12
14
  }): Promise<void | ExecutionStages>;
@@ -17,6 +19,8 @@ export declare function startTrackingMultiple(transactionLinkers: TransactionLin
17
19
  returnValue?: boolean;
18
20
  tableView?: boolean;
19
21
  logger?: ILogger;
22
+ txFinalizer?: ITxFinalizer;
20
23
  contractOpener?: ContractOpener;
24
+ cclAddress?: string;
21
25
  }): Promise<void | ExecutionStages[]>;
22
26
  export declare function printExecutionStagesTable(stages: ExecutionStages, logger: ILogger): void;
@@ -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
  }