@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,6 +9,7 @@ import { ContractState, TrackTransactionTreeParams, TrackTransactionTreeResult }
9
9
  export declare abstract class BaseContractOpener implements ContractOpener {
10
10
  protected logger?: ILogger;
11
11
  protected constructor(logger?: ILogger);
12
+ setLogger(logger: ILogger): void;
12
13
  abstract open<T extends Contract>(contract: T): OpenedContract<T> | SandboxContract<T>;
13
14
  abstract getContractState(address: Address): Promise<ContractState>;
14
15
  abstract getTransactions(address: Address, opts: GetTransactionsOptions): Promise<Transaction[]>;
@@ -60,6 +61,10 @@ export declare abstract class BaseContractOpener implements ContractOpener {
60
61
  * Find transaction by hash type
61
62
  */
62
63
  private findTransactionByHashType;
64
+ /**
65
+ * Retry lookup for root transaction because it may appear in indexers with a delay.
66
+ */
67
+ private findRootTransactionWithRetry;
63
68
  /**
64
69
  * Track transaction tree and validate all transactions
65
70
  */
@@ -12,6 +12,11 @@ class BaseContractOpener {
12
12
  constructor(logger) {
13
13
  this.logger = logger;
14
14
  }
15
+ setLogger(logger) {
16
+ if (!this.logger) {
17
+ this.logger = logger;
18
+ }
19
+ }
15
20
  /**
16
21
  * Common pagination logic for scanning transaction history
17
22
  * @param addr Account address
@@ -21,28 +26,84 @@ class BaseContractOpener {
21
26
  */
22
27
  async scanTransactionHistory(addr, opts, predicate) {
23
28
  const limit = opts?.limit ?? Consts_1.DEFAULT_FIND_TX_LIMIT;
29
+ const inclusive = opts?.inclusive ?? true;
30
+ const toLt = opts?.to_lt ? BigInt(opts.to_lt) : undefined;
24
31
  let currentLt = opts?.lt;
25
- let currentHash = opts?.hash;
32
+ let currentHash = opts?.hash ? (0, Utils_1.normalizeHashToBase64)(opts.hash) : undefined;
33
+ const seenCursors = new Set();
34
+ let scannedTransactions = 0;
35
+ const maxScannedTransactions = opts?.maxScannedTransactions ?? Consts_1.DEFAULT_MAX_SCANNED_TRANSACTIONS;
26
36
  while (true) {
27
- const batch = await this.getTransactions(addr, {
37
+ const list = await this.getTransactions(addr, {
28
38
  limit,
29
39
  lt: currentLt,
30
40
  hash: currentHash,
41
+ to_lt: opts?.to_lt,
42
+ inclusive,
31
43
  archival: opts?.archival ?? Consts_1.DEFAULT_FIND_TX_ARCHIVAL,
44
+ timeoutMs: opts?.timeoutMs,
45
+ retryDelayMs: opts?.retryDelayMs,
32
46
  });
33
- if (batch.length === 0)
47
+ if (list.length === 0) {
34
48
  break;
35
- for (const tx of batch) {
49
+ }
50
+ let startIndex = 0;
51
+ const firstTx = list[0];
52
+ const firstTxMatchesCursor = currentLt &&
53
+ currentHash &&
54
+ firstTx.lt.toString() === currentLt &&
55
+ firstTx.hash().toString('base64') === currentHash;
56
+ if (firstTxMatchesCursor) {
57
+ startIndex = 1;
58
+ if (list.length === 1) {
59
+ if (firstTx.prevTransactionLt === 0n) {
60
+ break;
61
+ }
62
+ const nextLt = firstTx.prevTransactionLt.toString();
63
+ const nextHash = Buffer.from(firstTx.prevTransactionHash.toString(16).padStart(64, '0'), 'hex').toString('base64');
64
+ if (currentLt && BigInt(nextLt) >= BigInt(currentLt)) {
65
+ break;
66
+ }
67
+ const cursorKey = `${nextLt}:${nextHash}`;
68
+ if (seenCursors.has(cursorKey)) {
69
+ break;
70
+ }
71
+ seenCursors.add(cursorKey);
72
+ currentLt = nextLt;
73
+ currentHash = nextHash;
74
+ continue;
75
+ }
76
+ }
77
+ for (let i = startIndex; i < list.length; i++) {
78
+ const tx = list[i];
79
+ scannedTransactions += 1;
80
+ if (scannedTransactions > maxScannedTransactions) {
81
+ this.logger?.debug(`Scan limit reached (${maxScannedTransactions} transactions), stopping history scan`);
82
+ return null;
83
+ }
36
84
  if (predicate(tx)) {
37
85
  return tx;
38
86
  }
39
87
  }
40
- const oldestTx = batch[batch.length - 1];
88
+ const oldestTx = list[list.length - 1];
41
89
  if (oldestTx.prevTransactionLt === 0n)
42
90
  break;
43
- currentLt = oldestTx.prevTransactionLt.toString();
44
- const hashHex = oldestTx.prevTransactionHash.toString(16).padStart(64, '0');
45
- currentHash = Buffer.from(hashHex, 'hex').toString('base64');
91
+ if (toLt !== undefined) {
92
+ if (inclusive ? oldestTx.lt <= toLt : oldestTx.lt < toLt)
93
+ break;
94
+ }
95
+ const nextLt = oldestTx.lt.toString();
96
+ const nextHash = oldestTx.hash().toString('base64');
97
+ if (currentLt && BigInt(nextLt) >= BigInt(currentLt)) {
98
+ break;
99
+ }
100
+ const cursorKey = `${nextLt}:${nextHash}`;
101
+ if (seenCursors.has(cursorKey)) {
102
+ break;
103
+ }
104
+ seenCursors.add(cursorKey);
105
+ currentLt = nextLt;
106
+ currentHash = nextHash;
46
107
  }
47
108
  return null;
48
109
  }
@@ -65,6 +126,10 @@ class BaseContractOpener {
65
126
  if (msgHash === targetHashB64) {
66
127
  return true;
67
128
  }
129
+ const rawMsgHash = (0, ton_1.beginCell)().store((0, ton_1.storeMessage)(tx.inMessage)).endCell().hash().toString('base64');
130
+ if (rawMsgHash === targetHashB64) {
131
+ return true;
132
+ }
68
133
  }
69
134
  // 3. check incoming message(internal)
70
135
  if (tx.inMessage && tx.inMessage.info.type === 'internal') {
@@ -74,6 +139,14 @@ class BaseContractOpener {
74
139
  return true;
75
140
  }
76
141
  }
142
+ // 4. check outcoming message
143
+ for (const outMsg of tx.outMessages.values()) {
144
+ const messageCell = (0, ton_1.beginCell)().store((0, ton_1.storeMessage)(outMsg)).endCell();
145
+ const hash = messageCell.hash();
146
+ if (hash.toString('base64') === targetHashB64) {
147
+ return true;
148
+ }
149
+ }
77
150
  return false;
78
151
  });
79
152
  }
@@ -100,6 +173,10 @@ class BaseContractOpener {
100
173
  if (hash === targetHashB64) {
101
174
  return true;
102
175
  }
176
+ const rawHash = (0, ton_1.beginCell)().store((0, ton_1.storeMessage)(tx.inMessage)).endCell().hash().toString('base64');
177
+ if (rawHash === targetHashB64) {
178
+ return true;
179
+ }
103
180
  }
104
181
  // Check incoming message(internal) - uses full message cell hash
105
182
  if (tx.inMessage && tx.inMessage.info.type === 'internal') {
@@ -166,53 +243,98 @@ class BaseContractOpener {
166
243
  * Validate transaction phases and return error details
167
244
  */
168
245
  validateTransactionWithResult(tx, ignoreOpcodeList) {
169
- if (tx.description.type !== 'generic' || !tx.inMessage)
170
- return null;
171
- // Skip validation for 1 nano messages
172
- if (tx.inMessage.info.type === 'internal' && tx.inMessage.info.value.coins === Consts_1.IGNORE_MSG_VALUE_1_NANO) {
173
- return null;
174
- }
175
- const bodySlice = tx.inMessage.body.beginParse();
176
- if (bodySlice.remainingBits < 32)
246
+ if (tx.description.type !== 'generic')
177
247
  return null;
178
- const opcode = bodySlice.loadUint(32);
179
- if (ignoreOpcodeList.includes(opcode)) {
180
- this.logger?.debug(`Skipping validation for tx: ${tx.hash().toString('base64')} (opcode in ignore list)`);
181
- return null;
182
- }
183
248
  const { aborted, computePhase, actionPhase } = tx.description;
184
249
  const txHash = tx.hash().toString('base64');
185
250
  const exitCode = computePhase && computePhase.type !== 'skipped' ? computePhase.exitCode : 'N/A';
186
251
  const resultCode = actionPhase ? actionPhase.resultCode : 'N/A';
187
252
  if (aborted) {
188
- if (!computePhase || computePhase.type === 'skipped') {
189
- return { txHash, exitCode, resultCode, reason: 'compute_phase_missing' };
190
- }
191
- if (!computePhase.success || computePhase.exitCode !== 0) {
192
- return { txHash, exitCode, resultCode, reason: 'compute_phase_failed' };
193
- }
194
- if (actionPhase && (!actionPhase.success || actionPhase.resultCode !== 0)) {
195
- return { txHash, exitCode, resultCode, reason: 'action_phase_failed' };
196
- }
197
253
  return { txHash, exitCode, resultCode, reason: 'aborted' };
198
254
  }
255
+ if (!computePhase) {
256
+ return { txHash, exitCode, resultCode, reason: 'compute_phase_missing' };
257
+ }
258
+ if (computePhase.type !== 'skipped' && (!computePhase.success || computePhase.exitCode !== 0)) {
259
+ return { txHash, exitCode, resultCode, reason: 'compute_phase_failed' };
260
+ }
261
+ if (actionPhase && (!actionPhase.success || actionPhase.resultCode !== 0)) {
262
+ return { txHash, exitCode, resultCode, reason: 'action_phase_failed' };
263
+ }
264
+ if (!tx.inMessage)
265
+ return null;
266
+ // Log optional skip hints (does not bypass phase validation)
267
+ if (tx.inMessage.info.type === 'internal' && tx.inMessage.info.value.coins === Consts_1.IGNORE_MSG_VALUE_1_NANO) {
268
+ this.logger?.debug(`Skipping extra checks for tx: ${txHash} (1 nano message)`);
269
+ return null;
270
+ }
271
+ const bodySlice = tx.inMessage.body.beginParse();
272
+ if (bodySlice.remainingBits >= 32) {
273
+ const opcode = bodySlice.loadUint(32);
274
+ if (ignoreOpcodeList.includes(opcode)) {
275
+ this.logger?.debug(`Skipping extra checks for tx: ${txHash} (opcode in ignore list)`);
276
+ }
277
+ }
199
278
  return null;
200
279
  }
201
280
  /**
202
281
  * Find transaction by hash type
203
282
  */
204
- async findTransactionByHashType(address, hash, hashType, limit) {
205
- const opts = { limit, archival: true };
283
+ async findTransactionByHashType(address, hash, hashType, opts) {
284
+ const searchOpts = { archival: true, ...opts };
206
285
  if (hashType === 'in') {
207
- return this.getTransactionByInMsgHash(address, hash, opts);
286
+ return this.getTransactionByInMsgHash(address, hash, searchOpts);
208
287
  }
209
288
  else if (hashType === 'out') {
210
- return this.getTransactionByOutMsgHash(address, hash, opts);
289
+ return this.getTransactionByOutMsgHash(address, hash, searchOpts);
211
290
  }
212
291
  else {
213
- return this.getTransactionByHash(address, hash, opts);
292
+ return this.getTransactionByHash(address, hash, searchOpts);
214
293
  }
215
294
  }
295
+ /**
296
+ * Retry lookup for root transaction because it may appear in indexers with a delay.
297
+ */
298
+ async findRootTransactionWithRetry(address, hash, hashType, limit, maxScannedTransactions, waitForRootTransaction) {
299
+ if (!waitForRootTransaction) {
300
+ return this.findTransactionByHashType(address, hash, hashType, {
301
+ limit,
302
+ archival: true,
303
+ maxScannedTransactions,
304
+ });
305
+ }
306
+ const attempts = Math.ceil(Consts_1.DEFAULT_WAIT_FOR_ROOT_TRANSACTION_TIMEOUT_MS / Consts_1.DEFAULT_WAIT_FOR_ROOT_TRANSACTION_RETRY_DELAY_MS);
307
+ const broadSearchOpts = { limit, archival: true, maxScannedTransactions };
308
+ const baselineInfo = await this.getAddressInformation(address);
309
+ let seenLt = baselineInfo.lastTransaction.lt || undefined;
310
+ // Capture baseline before first search.
311
+ const firstTx = await this.findTransactionByHashType(address, hash, hashType, broadSearchOpts);
312
+ if (firstTx) {
313
+ this.logger?.debug(`Root transaction found on attempt 1/${attempts}`);
314
+ return firstTx;
315
+ }
316
+ this.logger?.debug(`Root transaction not found yet (attempt 1/${attempts}), retrying in ${Consts_1.DEFAULT_WAIT_FOR_ROOT_TRANSACTION_RETRY_DELAY_MS}ms`);
317
+ await (0, Utils_1.sleep)(Consts_1.DEFAULT_WAIT_FOR_ROOT_TRANSACTION_RETRY_DELAY_MS);
318
+ for (let attempt = 2; attempt <= attempts; attempt++) {
319
+ const info = await this.getAddressInformation(address);
320
+ const currentLt = info.lastTransaction.lt || undefined;
321
+ if (!currentLt || currentLt === seenLt) {
322
+ this.logger?.debug(`Root transaction not found yet (attempt ${attempt}/${attempts}), lastTx unchanged, retrying in ${Consts_1.DEFAULT_WAIT_FOR_ROOT_TRANSACTION_RETRY_DELAY_MS}ms`);
323
+ await (0, Utils_1.sleep)(Consts_1.DEFAULT_WAIT_FOR_ROOT_TRANSACTION_RETRY_DELAY_MS);
324
+ continue;
325
+ }
326
+ seenLt = currentLt;
327
+ const tx = await this.findTransactionByHashType(address, hash, hashType, broadSearchOpts);
328
+ if (tx) {
329
+ this.logger?.debug(`Root transaction found on attempt ${attempt}/${attempts}`);
330
+ return tx;
331
+ }
332
+ this.logger?.debug(`Root transaction not found yet (attempt ${attempt}/${attempts}), retrying in ${Consts_1.DEFAULT_WAIT_FOR_ROOT_TRANSACTION_RETRY_DELAY_MS}ms`);
333
+ await (0, Utils_1.sleep)(Consts_1.DEFAULT_WAIT_FOR_ROOT_TRANSACTION_RETRY_DELAY_MS);
334
+ }
335
+ this.logger?.debug(`Root transaction not found after ${attempts} attempts`);
336
+ return null;
337
+ }
216
338
  /**
217
339
  * Track transaction tree and validate all transactions
218
340
  */
@@ -221,11 +343,18 @@ class BaseContractOpener {
221
343
  ignoreOpcodeList: Consts_1.IGNORE_OPCODE,
222
344
  limit: Consts_1.DEFAULT_FIND_TX_LIMIT,
223
345
  direction: 'both',
346
+ waitForRootTransaction: Consts_1.DEFAULT_WAIT_FOR_ROOT_TRANSACTION,
224
347
  }) {
225
348
  const result = await this.trackTransactionTreeWithResult(address, hash, params);
349
+ if (this.logger && typeof result.checkedCount === 'number') {
350
+ this.logger.debug(`Transaction tree checked: ${result.checkedCount} unique transaction(s)`);
351
+ }
226
352
  if (!result.success && result.error) {
227
- const { txHash, exitCode, resultCode, reason } = result.error;
228
- throw (0, errors_1.txFinalizationError)(`${txHash}: reason= ${reason} (exitCode=${exitCode}, resultCode=${resultCode})`);
353
+ const { txHash, exitCode, resultCode, reason, address: errorAddress, hashType } = result.error;
354
+ const context = reason === 'not_found'
355
+ ? ` address=${errorAddress ?? 'unknown'} hashType=${hashType ?? 'unknown'}`
356
+ : '';
357
+ throw (0, errors_1.txFinalizationError)(`${txHash}: reason=${reason} (exitCode=${exitCode}, resultCode=${resultCode})${context}`);
229
358
  }
230
359
  }
231
360
  /**
@@ -236,29 +365,56 @@ class BaseContractOpener {
236
365
  ignoreOpcodeList: Consts_1.IGNORE_OPCODE,
237
366
  limit: Consts_1.DEFAULT_FIND_TX_LIMIT,
238
367
  direction: 'both',
368
+ waitForRootTransaction: Consts_1.DEFAULT_WAIT_FOR_ROOT_TRANSACTION,
239
369
  }) {
240
- const { maxDepth = Consts_1.DEFAULT_FIND_TX_MAX_DEPTH, ignoreOpcodeList = Consts_1.IGNORE_OPCODE, limit = Consts_1.DEFAULT_FIND_TX_LIMIT, direction = 'both', } = params;
370
+ const { maxDepth = Consts_1.DEFAULT_FIND_TX_MAX_DEPTH, ignoreOpcodeList = Consts_1.IGNORE_OPCODE, limit = Consts_1.DEFAULT_FIND_TX_LIMIT, maxScannedTransactions = Consts_1.DEFAULT_MAX_SCANNED_TRANSACTIONS, direction = 'both', waitForRootTransaction = Consts_1.DEFAULT_WAIT_FOR_ROOT_TRANSACTION, } = params;
241
371
  const parsedAddress = ton_1.Address.parse(address);
242
- const visitedHashes = new Set();
243
- const queue = [{ address: parsedAddress, hash, depth: 0, hashType: 'unknown' }];
372
+ const normalizedRootHash = (0, Utils_1.normalizeHashToBase64)(hash);
373
+ const visitedSearchKeys = new Set();
374
+ const processedTxHashes = new Set();
375
+ let checkedCount = 0;
376
+ const searchOpts = { limit, archival: true, maxScannedTransactions };
377
+ const queue = [
378
+ { address: parsedAddress, hash: normalizedRootHash, depth: 0, hashType: 'unknown' },
379
+ ];
244
380
  while (queue.length > 0) {
245
381
  const { hash: currentHash, depth: currentDepth, address: currentAddress, hashType } = queue.shift();
246
- if (visitedHashes.has(currentHash))
382
+ const visitedKey = `${currentAddress.toString()}:${currentHash}:${hashType}`;
383
+ if (visitedSearchKeys.has(visitedKey))
247
384
  continue;
248
- visitedHashes.add(currentHash);
249
- this.logger?.debug(`Checking hash (depth ${currentDepth}): ${currentHash}`);
250
- const tx = await this.findTransactionByHashType(currentAddress, currentHash, hashType, limit);
385
+ visitedSearchKeys.add(visitedKey);
386
+ const tx = currentDepth === 0
387
+ ? await this.findRootTransactionWithRetry(currentAddress, currentHash, hashType, limit, maxScannedTransactions, waitForRootTransaction)
388
+ : await this.findTransactionByHashType(currentAddress, currentHash, hashType, searchOpts);
251
389
  if (!tx) {
252
- this.logger?.debug(`Transaction not found for hash: ${currentHash}`);
390
+ this.logger?.debug(`Transaction not found for hash: ${currentHash} (address=${currentAddress?.toString()}, hashType=${hashType ?? 'unknown'})`);
391
+ return {
392
+ success: false,
393
+ checkedCount,
394
+ error: {
395
+ txHash: currentHash,
396
+ exitCode: 'N/A',
397
+ resultCode: 'N/A',
398
+ reason: 'not_found',
399
+ address: currentAddress?.toString(),
400
+ hashType: hashType ?? 'unknown',
401
+ },
402
+ };
403
+ }
404
+ const txHash = tx.hash().toString('base64');
405
+ if (processedTxHashes.has(txHash)) {
253
406
  continue;
254
407
  }
408
+ processedTxHashes.add(txHash);
409
+ checkedCount += 1;
410
+ this.logger?.debug(`Checking tx (depth ${currentDepth}): ${txHash}`);
255
411
  // Validate transaction and return error if found
256
412
  const validationError = this.validateTransactionWithResult(tx, ignoreOpcodeList);
257
413
  if (validationError) {
258
- return { success: false, error: validationError };
414
+ return { success: false, checkedCount, error: validationError };
259
415
  }
260
416
  // Add adjacent transactions to queue
261
- if (currentDepth + 1 < maxDepth) {
417
+ if (currentDepth < maxDepth) {
262
418
  if ((direction === 'forward' || direction === 'both') && tx.outMessages.size > 0) {
263
419
  for (const msg of tx.outMessages.values()) {
264
420
  const dst = msg.info.dest;
@@ -282,9 +438,8 @@ class BaseContractOpener {
282
438
  });
283
439
  }
284
440
  }
285
- this.logger?.debug(`Finished checking hash (depth ${currentDepth}): ${currentHash}`);
286
441
  }
287
- return { success: true };
442
+ return { success: true, checkedCount };
288
443
  }
289
444
  }
290
445
  exports.BaseContractOpener = BaseContractOpener;
@@ -1,4 +1,5 @@
1
1
  import { Address, Contract, OpenedContract, 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
  type LiteServer = {
@@ -12,13 +13,17 @@ type LiteServer = {
12
13
  export declare class LiteClientOpener extends BaseContractOpener {
13
14
  private readonly client;
14
15
  private readonly engine;
16
+ private readonly singleEngines;
17
+ private isClosing;
18
+ private isClosed;
15
19
  private constructor();
16
20
  static create(options: {
17
21
  liteservers: LiteServer[];
18
22
  } | {
19
23
  network: Network;
20
- }): Promise<LiteClientOpener>;
24
+ }, logger?: ILogger): Promise<LiteClientOpener>;
21
25
  open<T extends Contract>(contract: T): OpenedContract<T>;
26
+ private disableReconnect;
22
27
  closeConnections(): void;
23
28
  getContractState(addr: Address): Promise<ContractState>;
24
29
  getTransactions(address: Address, opts: GetTransactionsOptions): Promise<Transaction[]>;
@@ -29,5 +34,5 @@ export declare function liteClientOpener(options: {
29
34
  liteservers: LiteServer[];
30
35
  } | {
31
36
  network: Network;
32
- }): Promise<LiteClientOpener>;
37
+ }, logger?: ILogger): Promise<LiteClientOpener>;
33
38
  export {};
@@ -24,12 +24,15 @@ async function getDefaultLiteServers(network) {
24
24
  return liteClients.liteservers;
25
25
  }
26
26
  class LiteClientOpener extends BaseContractOpener_1.BaseContractOpener {
27
- constructor(client, engine) {
28
- super();
27
+ constructor(client, engine, logger, singleEngines = []) {
28
+ super(logger);
29
29
  this.client = client;
30
30
  this.engine = engine;
31
+ this.isClosing = false;
32
+ this.isClosed = false;
33
+ this.singleEngines = singleEngines;
31
34
  }
32
- static async create(options) {
35
+ static async create(options, logger) {
33
36
  const liteservers = 'liteservers' in options ? options.liteservers : await getDefaultLiteServers(options.network);
34
37
  const engines = [];
35
38
  for (const server of liteservers) {
@@ -41,13 +44,32 @@ class LiteClientOpener extends BaseContractOpener_1.BaseContractOpener {
41
44
  }
42
45
  const engine = new ton_lite_client_1.LiteRoundRobinEngine(engines);
43
46
  const client = new ton_lite_client_1.LiteClient({ engine });
44
- return new LiteClientOpener(client, engine);
47
+ return new LiteClientOpener(client, engine, logger, engines);
45
48
  }
46
49
  open(contract) {
47
50
  return this.client.open(contract);
48
51
  }
52
+ disableReconnect(singleEngine) {
53
+ const engine = singleEngine;
54
+ engine.connect = async () => undefined;
55
+ engine.onClosed = () => undefined;
56
+ }
49
57
  closeConnections() {
50
- this.engine.close();
58
+ if (this.isClosing || this.isClosed) {
59
+ return;
60
+ }
61
+ this.isClosing = true;
62
+ try {
63
+ for (const singleEngine of this.singleEngines) {
64
+ this.disableReconnect(singleEngine);
65
+ singleEngine.close();
66
+ }
67
+ this.engine.close();
68
+ this.isClosed = true;
69
+ }
70
+ finally {
71
+ this.isClosing = false;
72
+ }
51
73
  }
52
74
  async getContractState(addr) {
53
75
  const block = await this.client.getMasterchainInfo();
@@ -98,10 +120,12 @@ class LiteClientOpener extends BaseContractOpener_1.BaseContractOpener {
98
120
  async getAddressInformation(addr) {
99
121
  const block = await this.client.getMasterchainInfo();
100
122
  const state = await this.client.getAccountState(addr, block.last);
123
+ const lastHashHex = state.lastTx ? state.lastTx.hash.toString(16).padStart(64, '0') : '';
124
+ const lastHashB64 = lastHashHex ? Buffer.from(lastHashHex, 'hex').toString('base64') : '';
101
125
  return {
102
126
  lastTransaction: {
103
127
  lt: state.lastTx?.lt.toString() ?? '',
104
- hash: Buffer.from(state.lastTx?.hash.toString(16) ?? '', 'hex').toString('base64'),
128
+ hash: lastHashB64,
105
129
  },
106
130
  };
107
131
  }
@@ -112,6 +136,6 @@ class LiteClientOpener extends BaseContractOpener_1.BaseContractOpener {
112
136
  }
113
137
  }
114
138
  exports.LiteClientOpener = LiteClientOpener;
115
- async function liteClientOpener(options) {
116
- return LiteClientOpener.create(options);
139
+ async function liteClientOpener(options, logger) {
140
+ return LiteClientOpener.create(options, logger);
117
141
  }
@@ -14,6 +14,8 @@ export declare class RetryableContractOpener implements ContractOpener {
14
14
  private readonly openerConfigs;
15
15
  private logger?;
16
16
  constructor(openerConfigs: OpenerConfig[], logger?: ILogger);
17
+ setLogger(logger: ILogger): void;
18
+ private applyLoggerToOpeners;
17
19
  getTransactions(address: Address, opts: GetTransactionsOptions): Promise<Transaction[]>;
18
20
  getTransactionByHash(address: Address, hash: string, opts?: GetTransactionsOptions): Promise<Transaction | null>;
19
21
  getAdjacentTransactions(address: Address, hash: string, opts?: GetTransactionsOptions): Promise<Transaction[]>;
@@ -28,7 +30,10 @@ export declare class RetryableContractOpener implements ContractOpener {
28
30
  trackTransactionTree(address: string, hash: string, params?: TrackTransactionTreeParams): Promise<void>;
29
31
  trackTransactionTreeWithResult(address: string, hash: string, params?: TrackTransactionTreeParams): Promise<TrackTransactionTreeResult>;
30
32
  private executeWithFallback;
33
+ private trySingleAttempt;
31
34
  private tryWithRetries;
35
+ private isContractExecutionError;
36
+ private isTransportError;
32
37
  private createRetryableContract;
33
38
  private callMethodAcrossOpeners;
34
39
  }