@tonappchain/sdk 0.7.2 → 0.7.3-rc2
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.
- package/LICENSE +20 -20
- package/README.md +203 -199
- package/dist/artifacts/dev/index.d.ts +2 -2
- package/dist/artifacts/dev/index.js +2 -1
- package/dist/artifacts/dev/ton/endpoints.d.ts +1 -0
- package/dist/artifacts/dev/ton/endpoints.js +2 -1
- package/dist/artifacts/mainnet/index.d.ts +2 -2
- package/dist/artifacts/mainnet/index.js +2 -1
- package/dist/artifacts/mainnet/ton/endpoints.d.ts +1 -0
- package/dist/artifacts/mainnet/ton/endpoints.js +2 -1
- package/dist/artifacts/testnet/index.d.ts +2 -2
- package/dist/artifacts/testnet/index.js +2 -1
- package/dist/artifacts/testnet/ton/endpoints.d.ts +1 -0
- package/dist/artifacts/testnet/ton/endpoints.js +2 -1
- package/dist/src/adapters/BaseContractOpener.js +8 -5
- package/dist/src/adapters/RetryableContractOpener.d.ts +6 -1
- package/dist/src/adapters/RetryableContractOpener.js +35 -8
- package/dist/src/adapters/ToncenterV3Indexer.d.ts +34 -0
- package/dist/src/adapters/ToncenterV3Indexer.js +123 -0
- package/dist/src/adapters/index.d.ts +1 -0
- package/dist/src/adapters/index.js +1 -0
- package/dist/src/assets/AssetFactory.d.ts +4 -1
- package/dist/src/assets/AssetFactory.js +4 -0
- package/dist/src/assets/FT.d.ts +3 -3
- package/dist/src/assets/FT.js +28 -15
- package/dist/src/assets/NFT.d.ts +3 -3
- package/dist/src/assets/NFT.js +10 -4
- package/dist/src/assets/TAC.d.ts +24 -0
- package/dist/src/assets/TAC.js +74 -0
- package/dist/src/assets/TON.d.ts +3 -3
- package/dist/src/assets/TON.js +12 -5
- package/dist/src/assets/index.d.ts +1 -0
- package/dist/src/assets/index.js +3 -1
- package/dist/src/errors/index.d.ts +1 -1
- package/dist/src/errors/index.js +8 -1
- package/dist/src/errors/instances.d.ts +6 -0
- package/dist/src/errors/instances.js +11 -1
- package/dist/src/interfaces/Asset.d.ts +11 -6
- package/dist/src/interfaces/ContractOpener.d.ts +1 -1
- package/dist/src/interfaces/IConfiguration.d.ts +1 -3
- package/dist/src/interfaces/ILiteSequencerClient.d.ts +14 -2
- package/dist/src/interfaces/IOperationTracker.d.ts +26 -1
- package/dist/src/interfaces/ITACTransactionManager.d.ts +20 -1
- package/dist/src/interfaces/ITacSDK.d.ts +52 -10
- package/dist/src/interfaces/IToncenterV3Indexer.d.ts +35 -0
- package/dist/src/interfaces/IToncenterV3Indexer.js +2 -0
- package/dist/src/interfaces/index.d.ts +1 -0
- package/dist/src/interfaces/index.js +1 -0
- package/dist/src/sdk/Consts.d.ts +6 -0
- package/dist/src/sdk/Consts.js +7 -1
- package/dist/src/sdk/LiteSequencerClient.d.ts +5 -2
- package/dist/src/sdk/LiteSequencerClient.js +59 -1
- package/dist/src/sdk/OperationTracker.d.ts +5 -1
- package/dist/src/sdk/OperationTracker.js +76 -0
- package/dist/src/sdk/TACTransactionManager.d.ts +4 -0
- package/dist/src/sdk/TACTransactionManager.js +181 -35
- package/dist/src/sdk/TONTransactionManager.js +28 -6
- package/dist/src/sdk/TacSdk.d.ts +8 -3
- package/dist/src/sdk/TacSdk.js +40 -2
- package/dist/src/sdk/Utils.d.ts +2 -1
- package/dist/src/sdk/Utils.js +5 -1
- package/dist/src/structs/InternalStruct.d.ts +41 -2
- package/dist/src/structs/Struct.d.ts +209 -6
- package/dist/src/structs/Struct.js +12 -1
- package/dist/src/wrappers/ContentUtils.d.ts +36 -13
- package/dist/src/wrappers/ContentUtils.js +197 -98
- package/package.json +121 -121
|
@@ -5,6 +5,7 @@ const ton_1 = require("@ton/ton");
|
|
|
5
5
|
const errors_1 = require("../errors");
|
|
6
6
|
const Consts_1 = require("../sdk/Consts");
|
|
7
7
|
const Utils_1 = require("../sdk/Utils");
|
|
8
|
+
const Struct_1 = require("../structs/Struct");
|
|
8
9
|
/**
|
|
9
10
|
* Base class for ContractOpener implementations with common functionality
|
|
10
11
|
*/
|
|
@@ -327,7 +328,7 @@ class BaseContractOpener {
|
|
|
327
328
|
maxDepth: Consts_1.DEFAULT_FIND_TX_MAX_DEPTH,
|
|
328
329
|
ignoreOpcodeList: Consts_1.IGNORE_OPCODE,
|
|
329
330
|
limit: Consts_1.DEFAULT_FIND_TX_LIMIT,
|
|
330
|
-
direction:
|
|
331
|
+
direction: Struct_1.TransactionTreeDirection.FORWARD,
|
|
331
332
|
retryOnNotFound: Consts_1.DEFAULT_RETRY_ON_NOT_FOUND,
|
|
332
333
|
}) {
|
|
333
334
|
const result = await this.trackTransactionTreeWithResult(address, hash, params);
|
|
@@ -353,10 +354,10 @@ class BaseContractOpener {
|
|
|
353
354
|
maxDepth: Consts_1.DEFAULT_FIND_TX_MAX_DEPTH,
|
|
354
355
|
ignoreOpcodeList: Consts_1.IGNORE_OPCODE,
|
|
355
356
|
limit: Consts_1.DEFAULT_FIND_TX_LIMIT,
|
|
356
|
-
direction:
|
|
357
|
+
direction: Struct_1.TransactionTreeDirection.FORWARD,
|
|
357
358
|
retryOnNotFound: Consts_1.DEFAULT_RETRY_ON_NOT_FOUND,
|
|
358
359
|
}) {
|
|
359
|
-
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 =
|
|
360
|
+
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 = Struct_1.TransactionTreeDirection.FORWARD, retryOnNotFound = Consts_1.DEFAULT_RETRY_ON_NOT_FOUND, retryDelayMs, retries, } = params;
|
|
360
361
|
const parsedAddress = ton_1.Address.parse(address);
|
|
361
362
|
const normalizedRootHash = (0, Utils_1.normalizeHashToBase64)(hash);
|
|
362
363
|
const visitedSearchKeys = new Set();
|
|
@@ -410,7 +411,8 @@ class BaseContractOpener {
|
|
|
410
411
|
}
|
|
411
412
|
// Add adjacent transactions to queue
|
|
412
413
|
if (currentDepth < maxDepth) {
|
|
413
|
-
if ((direction ===
|
|
414
|
+
if ((direction === Struct_1.TransactionTreeDirection.FORWARD || direction === Struct_1.TransactionTreeDirection.BOTH) &&
|
|
415
|
+
tx.outMessages.size > 0) {
|
|
414
416
|
for (const msg of tx.outMessages.values()) {
|
|
415
417
|
const dst = msg.info.dest;
|
|
416
418
|
if (!dst || dst instanceof ton_1.ExternalAddress)
|
|
@@ -424,7 +426,8 @@ class BaseContractOpener {
|
|
|
424
426
|
}
|
|
425
427
|
}
|
|
426
428
|
// Backward: add parent (incoming message source)
|
|
427
|
-
if ((direction ===
|
|
429
|
+
if ((direction === Struct_1.TransactionTreeDirection.BACKWARD || direction === Struct_1.TransactionTreeDirection.BOTH) &&
|
|
430
|
+
tx.inMessage?.info.type === 'internal') {
|
|
428
431
|
queue.push({
|
|
429
432
|
hash: (0, ton_1.beginCell)().store((0, ton_1.storeMessage)(tx.inMessage)).endCell().hash().toString('base64'),
|
|
430
433
|
address: tx.inMessage.info.src,
|
|
@@ -7,8 +7,12 @@ export interface OpenerConfig {
|
|
|
7
7
|
opener: ContractOpener;
|
|
8
8
|
/** Number of retry attempts before falling back to the next opener. */
|
|
9
9
|
retries: number;
|
|
10
|
-
/**
|
|
10
|
+
/** Base delay in milliseconds between retries for this opener. */
|
|
11
11
|
retryDelay: number;
|
|
12
|
+
/** Upper bound for exponential backoff delay in milliseconds. */
|
|
13
|
+
maxRetryDelay?: number;
|
|
14
|
+
/** Exponential backoff multiplier applied after each failed retry. */
|
|
15
|
+
retryBackoffMultiplier?: number;
|
|
12
16
|
}
|
|
13
17
|
export declare class RetryableContractOpener implements ContractOpener {
|
|
14
18
|
private readonly openerConfigs;
|
|
@@ -32,6 +36,7 @@ export declare class RetryableContractOpener implements ContractOpener {
|
|
|
32
36
|
private executeWithFallback;
|
|
33
37
|
private trySingleAttempt;
|
|
34
38
|
private tryWithRetries;
|
|
39
|
+
private getRetryDelay;
|
|
35
40
|
private isContractExecutionError;
|
|
36
41
|
private isTransportError;
|
|
37
42
|
private createRetryableContract;
|
|
@@ -22,9 +22,7 @@ class RetryableContractOpener {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
setLogger(logger) {
|
|
25
|
-
|
|
26
|
-
this.logger = logger;
|
|
27
|
-
}
|
|
25
|
+
this.logger = logger;
|
|
28
26
|
this.applyLoggerToOpeners(logger);
|
|
29
27
|
}
|
|
30
28
|
applyLoggerToOpeners(logger) {
|
|
@@ -199,13 +197,24 @@ class RetryableContractOpener {
|
|
|
199
197
|
return { success: false, lastError };
|
|
200
198
|
}
|
|
201
199
|
if (attempt < config.retries) {
|
|
202
|
-
|
|
203
|
-
|
|
200
|
+
const retryDelay = this.getRetryDelay(config, attempt);
|
|
201
|
+
this.logger?.debug(`[RetryableContractOpener] ${operationContext}: attempt ${attempt + 1}/${config.retries + 1} failed (${lastError.message}), retrying in ${retryDelay}ms`);
|
|
202
|
+
await sleep(retryDelay);
|
|
204
203
|
}
|
|
205
204
|
}
|
|
206
205
|
}
|
|
207
206
|
return { success: false, lastError };
|
|
208
207
|
}
|
|
208
|
+
getRetryDelay(config, attempt) {
|
|
209
|
+
const baseDelay = config.retryDelay ?? Consts_1.DEFAULT_RETRY_DELAY_MS;
|
|
210
|
+
const maxRetryDelay = config.maxRetryDelay ?? Consts_1.DEFAULT_RETRY_MAX_DELAY_MS;
|
|
211
|
+
const backoffMultiplier = config.retryBackoffMultiplier ?? Consts_1.DEFAULT_RETRY_BACKOFF_MULTIPLIER;
|
|
212
|
+
if (baseDelay === 0 || maxRetryDelay === 0) {
|
|
213
|
+
return 0;
|
|
214
|
+
}
|
|
215
|
+
const calculatedDelay = baseDelay * backoffMultiplier ** attempt;
|
|
216
|
+
return Math.min(Math.round(calculatedDelay), maxRetryDelay);
|
|
217
|
+
}
|
|
209
218
|
isContractExecutionError(error) {
|
|
210
219
|
const errorWithExit = error;
|
|
211
220
|
if (typeof errorWithExit.exitCode === 'number' ||
|
|
@@ -269,18 +278,36 @@ async function createDefaultRetryableOpener(tonRpcEndpoint, networkType, maxRetr
|
|
|
269
278
|
timeout: Consts_1.DEFAULT_HTTP_CLIENT_TIMEOUT_MS,
|
|
270
279
|
});
|
|
271
280
|
const opener = (0, TonClientOpener_1.tonClientOpener)(tonClient, logger);
|
|
272
|
-
openers.push({
|
|
281
|
+
openers.push({
|
|
282
|
+
opener,
|
|
283
|
+
retries: maxRetries,
|
|
284
|
+
retryDelay,
|
|
285
|
+
maxRetryDelay: Consts_1.DEFAULT_RETRY_MAX_DELAY_MS,
|
|
286
|
+
retryBackoffMultiplier: Consts_1.DEFAULT_RETRY_BACKOFF_MULTIPLIER,
|
|
287
|
+
});
|
|
273
288
|
if (networkType !== Struct_1.Network.DEV) {
|
|
274
289
|
try {
|
|
275
290
|
const opener = await (0, TonClientOpener_1.orbsOpener)(networkType, logger);
|
|
276
|
-
openers.push({
|
|
291
|
+
openers.push({
|
|
292
|
+
opener: opener,
|
|
293
|
+
retries: maxRetries,
|
|
294
|
+
retryDelay,
|
|
295
|
+
maxRetryDelay: Consts_1.DEFAULT_RETRY_MAX_DELAY_MS,
|
|
296
|
+
retryBackoffMultiplier: Consts_1.DEFAULT_RETRY_BACKOFF_MULTIPLIER,
|
|
297
|
+
});
|
|
277
298
|
}
|
|
278
299
|
catch {
|
|
279
300
|
// skip opener in case of failure
|
|
280
301
|
}
|
|
281
302
|
try {
|
|
282
303
|
const opener4 = await (0, TonClient4Opener_1.orbsOpener4)(networkType, Consts_1.DEFAULT_HTTP_CLIENT_TIMEOUT_MS, logger);
|
|
283
|
-
openers.push({
|
|
304
|
+
openers.push({
|
|
305
|
+
opener: opener4,
|
|
306
|
+
retries: maxRetries,
|
|
307
|
+
retryDelay,
|
|
308
|
+
maxRetryDelay: Consts_1.DEFAULT_RETRY_MAX_DELAY_MS,
|
|
309
|
+
retryBackoffMultiplier: Consts_1.DEFAULT_RETRY_BACKOFF_MULTIPLIER,
|
|
310
|
+
});
|
|
284
311
|
}
|
|
285
312
|
catch {
|
|
286
313
|
// skip opener in case of failure
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { IHttpClient, IToncenterV3Indexer } from '../interfaces';
|
|
2
|
+
import { ToncenterV3IndexedTransaction, ToncenterV3TraversalParams } from '../structs/Struct';
|
|
3
|
+
/**
|
|
4
|
+
* Thin Toncenter v3 indexer client for resolving indexed TON transaction context by hash and
|
|
5
|
+
* traversing a linked chain of TON transactions until it finds the sequencer-collected event transaction on CCL.
|
|
6
|
+
*/
|
|
7
|
+
export declare class ToncenterV3Indexer implements IToncenterV3Indexer {
|
|
8
|
+
readonly endpoint: string;
|
|
9
|
+
private readonly httpClient;
|
|
10
|
+
constructor(endpoint: string, httpClient?: IHttpClient);
|
|
11
|
+
/**
|
|
12
|
+
* Queries Toncenter v3 indexed `/transactions` endpoint by hash and returns the first match.
|
|
13
|
+
* @param transactionHash TON transaction hash accepted by the indexer.
|
|
14
|
+
* @returns Indexed transaction with owning account context, or null on 404 / empty result.
|
|
15
|
+
*/
|
|
16
|
+
getTransactionByHash(transactionHash: string): Promise<ToncenterV3IndexedTransaction | null>;
|
|
17
|
+
/**
|
|
18
|
+
* Queries Toncenter v3 indexed `/adjacentTransactions` endpoint by hash.
|
|
19
|
+
* @param transactionHash TON transaction hash accepted by the indexer.
|
|
20
|
+
* @returns Adjacent indexed transactions, or an empty array on 404 / empty result.
|
|
21
|
+
*/
|
|
22
|
+
getAdjacentTransactions(transactionHash: string): Promise<ToncenterV3IndexedTransaction[]>;
|
|
23
|
+
/**
|
|
24
|
+
* Traverses the linked chain of TON transactions in both directions starting from any TON transaction hash
|
|
25
|
+
* from that linked transaction chain, and returns the transaction on crossChainLayer that the sequencer would collect
|
|
26
|
+
* as a TVM event.
|
|
27
|
+
* @param transactionHash Any TON transaction hash from the target linked chain of TON transactions.
|
|
28
|
+
* @param crossChainLayerAddress crossChainLayer address whose sequencer-collected event transaction should be found in that linked chain of TON transactions.
|
|
29
|
+
* @param params Optional traversal settings. Defaults match the SDK traversal defaults.
|
|
30
|
+
* @returns Indexed crossChainLayer transaction that matches the sequencer collector rules, or null if not found.
|
|
31
|
+
*/
|
|
32
|
+
findCollectibleCrossChainLayerTransaction(transactionHash: string, crossChainLayerAddress: string, params?: ToncenterV3TraversalParams): Promise<ToncenterV3IndexedTransaction | null>;
|
|
33
|
+
private isCollectibleEventTransactionOnAccount;
|
|
34
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ToncenterV3Indexer = void 0;
|
|
4
|
+
const ton_1 = require("@ton/ton");
|
|
5
|
+
const errors_1 = require("../errors");
|
|
6
|
+
const AxiosHttpClient_1 = require("../sdk/AxiosHttpClient");
|
|
7
|
+
const Consts_1 = require("../sdk/Consts");
|
|
8
|
+
const Utils_1 = require("../sdk/Utils");
|
|
9
|
+
/**
|
|
10
|
+
* Thin Toncenter v3 indexer client for resolving indexed TON transaction context by hash and
|
|
11
|
+
* traversing a linked chain of TON transactions until it finds the sequencer-collected event transaction on CCL.
|
|
12
|
+
*/
|
|
13
|
+
class ToncenterV3Indexer {
|
|
14
|
+
constructor(endpoint, httpClient = new AxiosHttpClient_1.AxiosHttpClient()) {
|
|
15
|
+
this.endpoint = endpoint;
|
|
16
|
+
this.httpClient = httpClient;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Queries Toncenter v3 indexed `/transactions` endpoint by hash and returns the first match.
|
|
20
|
+
* @param transactionHash TON transaction hash accepted by the indexer.
|
|
21
|
+
* @returns Indexed transaction with owning account context, or null on 404 / empty result.
|
|
22
|
+
*/
|
|
23
|
+
async getTransactionByHash(transactionHash) {
|
|
24
|
+
const endpoint = this.endpoint.endsWith('/') ? `${this.endpoint}transactions` : `${this.endpoint}/transactions`;
|
|
25
|
+
const requestLabel = `GET ${endpoint}`;
|
|
26
|
+
try {
|
|
27
|
+
const response = await this.httpClient.get(endpoint, {
|
|
28
|
+
params: {
|
|
29
|
+
hash: transactionHash,
|
|
30
|
+
limit: 1,
|
|
31
|
+
},
|
|
32
|
+
transformResponse: [Utils_1.toCamelCaseTransformer],
|
|
33
|
+
});
|
|
34
|
+
return response.data.transactions?.[0] ?? null;
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
38
|
+
if (error?.response?.status === 404) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
throw (0, errors_1.operationFetchError)(`request ${requestLabel} failed to resolve TON indexed transaction`, error);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Queries Toncenter v3 indexed `/adjacentTransactions` endpoint by hash.
|
|
46
|
+
* @param transactionHash TON transaction hash accepted by the indexer.
|
|
47
|
+
* @returns Adjacent indexed transactions, or an empty array on 404 / empty result.
|
|
48
|
+
*/
|
|
49
|
+
async getAdjacentTransactions(transactionHash) {
|
|
50
|
+
const endpoint = this.endpoint.endsWith('/') ? `${this.endpoint}adjacentTransactions` : `${this.endpoint}/adjacentTransactions`;
|
|
51
|
+
const requestLabel = `GET ${endpoint}`;
|
|
52
|
+
try {
|
|
53
|
+
const response = await this.httpClient.get(endpoint, {
|
|
54
|
+
params: {
|
|
55
|
+
hash: transactionHash,
|
|
56
|
+
},
|
|
57
|
+
transformResponse: [Utils_1.toCamelCaseTransformer],
|
|
58
|
+
});
|
|
59
|
+
return response.data.transactions ?? [];
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
63
|
+
if (error?.response?.status === 404) {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
throw (0, errors_1.operationFetchError)(`request ${requestLabel} failed to resolve adjacent TON indexed transactions`, error);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Traverses the linked chain of TON transactions in both directions starting from any TON transaction hash
|
|
71
|
+
* from that linked transaction chain, and returns the transaction on crossChainLayer that the sequencer would collect
|
|
72
|
+
* as a TVM event.
|
|
73
|
+
* @param transactionHash Any TON transaction hash from the target linked chain of TON transactions.
|
|
74
|
+
* @param crossChainLayerAddress crossChainLayer address whose sequencer-collected event transaction should be found in that linked chain of TON transactions.
|
|
75
|
+
* @param params Optional traversal settings. Defaults match the SDK traversal defaults.
|
|
76
|
+
* @returns Indexed crossChainLayer transaction that matches the sequencer collector rules, or null if not found.
|
|
77
|
+
*/
|
|
78
|
+
async findCollectibleCrossChainLayerTransaction(transactionHash, crossChainLayerAddress, params) {
|
|
79
|
+
const { maxDepth = Consts_1.DEFAULT_FIND_TX_MAX_DEPTH, maxScannedTransactions = Consts_1.DEFAULT_MAX_SCANNED_TRANSACTIONS, } = params ?? {};
|
|
80
|
+
const crossChainLayer = ton_1.Address.parse(crossChainLayerAddress);
|
|
81
|
+
const rootTransaction = await this.getTransactionByHash(transactionHash);
|
|
82
|
+
if (!rootTransaction) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
if (this.isCollectibleEventTransactionOnAccount(rootTransaction, crossChainLayer)) {
|
|
86
|
+
return rootTransaction;
|
|
87
|
+
}
|
|
88
|
+
const queue = [{ hash: rootTransaction.hash, depth: 0 }];
|
|
89
|
+
const visitedHashes = new Set([(0, Utils_1.normalizeHashToHex)(rootTransaction.hash)]);
|
|
90
|
+
let scannedTransactions = 0;
|
|
91
|
+
while (queue.length > 0) {
|
|
92
|
+
const { hash: currentHash, depth } = queue.shift();
|
|
93
|
+
if (depth >= maxDepth || scannedTransactions >= maxScannedTransactions) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const adjacentTransactions = await this.getAdjacentTransactions(currentHash);
|
|
97
|
+
for (const transaction of adjacentTransactions) {
|
|
98
|
+
const normalizedHash = (0, Utils_1.normalizeHashToHex)(transaction.hash);
|
|
99
|
+
if (visitedHashes.has(normalizedHash)) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
visitedHashes.add(normalizedHash);
|
|
103
|
+
scannedTransactions += 1;
|
|
104
|
+
if (this.isCollectibleEventTransactionOnAccount(transaction, crossChainLayer)) {
|
|
105
|
+
return transaction;
|
|
106
|
+
}
|
|
107
|
+
if (scannedTransactions < maxScannedTransactions) {
|
|
108
|
+
queue.push({ hash: transaction.hash, depth: depth + 1 });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
isCollectibleEventTransactionOnAccount(transaction, account) {
|
|
115
|
+
if (!ton_1.Address.parse(transaction.account).equals(account)) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
return transaction.outMsgs.some((message) => {
|
|
119
|
+
return message.destination == null;
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
exports.ToncenterV3Indexer = ToncenterV3Indexer;
|
|
@@ -19,5 +19,6 @@ __exportStar(require("./LiteClientOpener"), exports);
|
|
|
19
19
|
__exportStar(require("./OpenerUtils"), exports);
|
|
20
20
|
__exportStar(require("./RetryableContractOpener"), exports);
|
|
21
21
|
__exportStar(require("./SandboxOpener"), exports);
|
|
22
|
+
__exportStar(require("./ToncenterV3Indexer"), exports);
|
|
22
23
|
__exportStar(require("./TonClient4Opener"), exports);
|
|
23
24
|
__exportStar(require("./TonClientOpener"), exports);
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { Asset, IConfiguration } from '../interfaces';
|
|
2
2
|
import { AssetFromFTArg, AssetFromNFTCollectionArg, AssetFromNFTItemArg, EVMAddress, NFTAddressType, TVMAddress } from '../structs/Struct';
|
|
3
|
+
import { FT } from './FT';
|
|
4
|
+
import { TAC } from './TAC';
|
|
5
|
+
import { TON } from './TON';
|
|
3
6
|
export declare class AssetFactory {
|
|
4
7
|
static from(configuration: IConfiguration, token: AssetFromFTArg | AssetFromNFTCollectionArg | AssetFromNFTItemArg): Promise<Asset>;
|
|
5
|
-
static createFTAsset(configuration: IConfiguration, address: TVMAddress | EVMAddress): Promise<
|
|
8
|
+
static createFTAsset(configuration: IConfiguration, address: TVMAddress | EVMAddress): Promise<FT | TAC | TON>;
|
|
6
9
|
static createNFTAsset(configuration: IConfiguration, address: TVMAddress | EVMAddress, addressType: NFTAddressType, index?: bigint): Promise<Asset>;
|
|
7
10
|
}
|
|
@@ -7,6 +7,7 @@ const Struct_1 = require("../structs/Struct");
|
|
|
7
7
|
const AssetCache_1 = require("./AssetCache");
|
|
8
8
|
const FT_1 = require("./FT");
|
|
9
9
|
const NFT_1 = require("./NFT");
|
|
10
|
+
const TAC_1 = require("./TAC");
|
|
10
11
|
const TON_1 = require("./TON");
|
|
11
12
|
class AssetFactory {
|
|
12
13
|
static async from(configuration, token) {
|
|
@@ -35,6 +36,9 @@ class AssetFactory {
|
|
|
35
36
|
address === Consts_1.TON_BURN_ADDRESS) {
|
|
36
37
|
return ton;
|
|
37
38
|
}
|
|
39
|
+
if (address === (await configuration.nativeTACAddress())) {
|
|
40
|
+
return TAC_1.TAC.create(configuration);
|
|
41
|
+
}
|
|
38
42
|
return FT_1.FT.fromAddress(configuration, address);
|
|
39
43
|
}
|
|
40
44
|
static async createNFTAsset(configuration, address, addressType, index) {
|
package/dist/src/assets/FT.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { Cell, OpenedContract } from '@ton/ton';
|
|
|
3
3
|
import { JettonMinterData, JettonWallet } from '../../artifacts/tonTypes';
|
|
4
4
|
import { Asset, IConfiguration } from '../interfaces';
|
|
5
5
|
import { AssetOpType } from '../structs/InternalStruct';
|
|
6
|
-
import { AssetType, EVMAddress, FeeParams, FTOriginAndData, TVMAddress, UserWalletBalanceExtended } from '../structs/Struct';
|
|
6
|
+
import { AssetType, BlockchainType, EVMAddress, FeeParams, FTOriginAndData, TVMAddress, UserWalletBalanceExtended } from '../structs/Struct';
|
|
7
7
|
import { Origin } from '../structs/Struct';
|
|
8
8
|
export declare class FT implements Asset {
|
|
9
9
|
private _tvmAddress;
|
|
@@ -55,8 +55,8 @@ export declare class FT implements Asset {
|
|
|
55
55
|
getUserBalance(userAddress: string): Promise<bigint>;
|
|
56
56
|
getUserBalanceExtended(userAddress: string): Promise<UserWalletBalanceExtended>;
|
|
57
57
|
checkBalance(userAddress: string): Promise<void>;
|
|
58
|
-
checkCanBeTransferredBy(userAddress: string): Promise<void>;
|
|
59
|
-
getBalanceOf(userAddress: string): Promise<bigint>;
|
|
58
|
+
checkCanBeTransferredBy(userAddress: string, blockchainType?: BlockchainType): Promise<void>;
|
|
59
|
+
getBalanceOf(userAddress: string, blockchainType?: BlockchainType): Promise<bigint>;
|
|
60
60
|
private getTransferPayload;
|
|
61
61
|
private getBurnPayload;
|
|
62
62
|
}
|
package/dist/src/assets/FT.js
CHANGED
|
@@ -122,20 +122,18 @@ class FT {
|
|
|
122
122
|
throw errors_1.missingJettonDataError;
|
|
123
123
|
}
|
|
124
124
|
const metadata = await (0, ContentUtils_1.readJettonMetadata)(jettonData.content);
|
|
125
|
-
const decimalsRaw = metadata.metadata
|
|
125
|
+
const decimalsRaw = metadata.metadata?.decimals ?? Consts_1.TON_DECIMALS;
|
|
126
126
|
if (decimalsRaw === undefined) {
|
|
127
127
|
throw errors_1.missingDecimals;
|
|
128
128
|
}
|
|
129
129
|
decimals = Number(decimalsRaw);
|
|
130
130
|
}
|
|
131
131
|
else {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
decimals = await this.getTACDecimals(configuration, finalEvmAddress);
|
|
132
|
+
const resolvedEvmAddress = (0, ethers_1.isAddress)(address)
|
|
133
|
+
? address
|
|
134
|
+
: (cachedEvmAddress ?? (await jettonMinter.getEVMTokenAddress()));
|
|
135
|
+
finalEvmAddress = resolvedEvmAddress;
|
|
136
|
+
decimals = await this.getTACDecimals(configuration, resolvedEvmAddress);
|
|
139
137
|
}
|
|
140
138
|
const token = new FT(tvmAddress, origin, configuration, decimals);
|
|
141
139
|
if (finalEvmAddress || (0, ethers_1.isAddress)(address)) {
|
|
@@ -224,17 +222,19 @@ class FT {
|
|
|
224
222
|
return this._evmAddress;
|
|
225
223
|
}
|
|
226
224
|
const tokenAddressString = this._tvmAddress.toString({ bounceable: true });
|
|
225
|
+
let evmAddress;
|
|
227
226
|
if (this.origin === Struct_2.Origin.TON) {
|
|
228
|
-
|
|
227
|
+
evmAddress = await this._configuration.TACParams.tokenUtils.computeAddress(tokenAddressString);
|
|
229
228
|
}
|
|
230
229
|
else if (this.origin === Struct_2.Origin.TAC) {
|
|
231
230
|
const givenMinter = this._configuration.TONParams.contractOpener.open(this._configuration.artifacts.ton.wrappers.JettonMinter.createFromAddress(ton_1.Address.parse(tokenAddressString)));
|
|
232
|
-
|
|
231
|
+
evmAddress = await givenMinter.getEVMTokenAddress();
|
|
233
232
|
}
|
|
234
233
|
else {
|
|
235
234
|
throw (0, errors_1.unknownTokenTypeError)(tokenAddressString, 'Token origin is neither TON nor TAC');
|
|
236
235
|
}
|
|
237
|
-
|
|
236
|
+
this._evmAddress = evmAddress;
|
|
237
|
+
return evmAddress;
|
|
238
238
|
}
|
|
239
239
|
async getTVMAddress() {
|
|
240
240
|
return this._tvmAddress.toString({ bounceable: true });
|
|
@@ -290,11 +290,24 @@ class FT {
|
|
|
290
290
|
throw (0, errors_1.insufficientBalanceError)(this._tvmAddress.toString());
|
|
291
291
|
}
|
|
292
292
|
}
|
|
293
|
-
async checkCanBeTransferredBy(userAddress) {
|
|
294
|
-
await this.
|
|
293
|
+
async checkCanBeTransferredBy(userAddress, blockchainType = Struct_1.BlockchainType.TON) {
|
|
294
|
+
if ((await this.getBalanceOf(userAddress, blockchainType)) < this._transferAmount) {
|
|
295
|
+
throw (0, errors_1.insufficientBalanceError)(this._tvmAddress.toString());
|
|
296
|
+
}
|
|
295
297
|
}
|
|
296
|
-
async getBalanceOf(userAddress) {
|
|
297
|
-
|
|
298
|
+
async getBalanceOf(userAddress, blockchainType = Struct_1.BlockchainType.TON) {
|
|
299
|
+
if (blockchainType === Struct_1.BlockchainType.TON) {
|
|
300
|
+
Validator_1.Validator.validateTVMAddress(userAddress);
|
|
301
|
+
return this.getUserBalance(userAddress);
|
|
302
|
+
}
|
|
303
|
+
Validator_1.Validator.validateEVMAddress(userAddress);
|
|
304
|
+
const evmAddress = await this.getEVMAddress();
|
|
305
|
+
const nativeTACAddress = await this._configuration.nativeTACAddress();
|
|
306
|
+
if (evmAddress === nativeTACAddress) {
|
|
307
|
+
return this._configuration.TACParams.provider.getBalance(userAddress);
|
|
308
|
+
}
|
|
309
|
+
const erc20Token = new ethers_1.ethers.Contract(evmAddress, this._configuration.artifacts.tac.compilationArtifacts.IERC20WithDecimals.abi, this._configuration.TACParams.provider);
|
|
310
|
+
return erc20Token.balanceOf(userAddress);
|
|
298
311
|
}
|
|
299
312
|
getTransferPayload(rawAmount, notificationReceiverAddress, responseAddress, evmData, crossChainTonAmount, forwardFeeAmount, feeData) {
|
|
300
313
|
const queryId = (0, Utils_1.generateRandomNumberByTimestamp)().randomNumber;
|
package/dist/src/assets/NFT.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Cell } from '@ton/ton';
|
|
2
2
|
import { NFTCollectionData, NFTItemData } from '../../artifacts/tonTypes';
|
|
3
3
|
import { Asset, IConfiguration } from '../interfaces';
|
|
4
|
-
import { AssetType, EVMAddress, FeeParams, Origin, TVMAddress } from '../structs/Struct';
|
|
4
|
+
import { AssetType, BlockchainType, EVMAddress, FeeParams, Origin, TVMAddress } from '../structs/Struct';
|
|
5
5
|
export declare class NFT implements Asset {
|
|
6
6
|
private readonly _addresses;
|
|
7
7
|
readonly origin: Origin;
|
|
@@ -61,8 +61,8 @@ export declare class NFT implements Asset {
|
|
|
61
61
|
feeParams?: FeeParams;
|
|
62
62
|
}): Cell;
|
|
63
63
|
isOwnedBy(userAddress: string): Promise<boolean>;
|
|
64
|
-
checkCanBeTransferredBy(userAddress: string): Promise<void>;
|
|
65
|
-
getBalanceOf(userAddress: string): Promise<bigint>;
|
|
64
|
+
checkCanBeTransferredBy(userAddress: string, blockchainType?: BlockchainType): Promise<void>;
|
|
65
|
+
getBalanceOf(userAddress: string, blockchainType?: BlockchainType): Promise<bigint>;
|
|
66
66
|
private getBurnPayload;
|
|
67
67
|
private getTransferPayload;
|
|
68
68
|
}
|
package/dist/src/assets/NFT.js
CHANGED
|
@@ -207,13 +207,19 @@ class NFT {
|
|
|
207
207
|
const nftData = await NFT.getItemData(this._configuration, this.address.toString());
|
|
208
208
|
return !!nftData.ownerAddress?.equals(ton_1.Address.parse(userAddress));
|
|
209
209
|
}
|
|
210
|
-
async checkCanBeTransferredBy(userAddress) {
|
|
211
|
-
if (
|
|
210
|
+
async checkCanBeTransferredBy(userAddress, blockchainType = Struct_1.BlockchainType.TON) {
|
|
211
|
+
if ((await this.getBalanceOf(userAddress, blockchainType)) < 1n) {
|
|
212
212
|
throw (0, errors_1.insufficientBalanceError)(this.address.toString());
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
|
-
async getBalanceOf(userAddress) {
|
|
216
|
-
|
|
215
|
+
async getBalanceOf(userAddress, blockchainType = Struct_1.BlockchainType.TON) {
|
|
216
|
+
if (blockchainType === Struct_1.BlockchainType.TON) {
|
|
217
|
+
Validator_1.Validator.validateTVMAddress(userAddress);
|
|
218
|
+
return (await this.isOwnedBy(userAddress)) ? 1n : 0n;
|
|
219
|
+
}
|
|
220
|
+
Validator_1.Validator.validateEVMAddress(userAddress);
|
|
221
|
+
const nft = new ethers_1.ethers.Contract(await this.getEVMAddress(), this._configuration.artifacts.tac.compilationArtifacts.IERC721.abi, this._configuration.TACParams.provider);
|
|
222
|
+
return (await nft.ownerOf(this._addresses.index)).toLowerCase() === userAddress.toLowerCase() ? 1n : 0n;
|
|
217
223
|
}
|
|
218
224
|
getBurnPayload(crossChainLayerAddress, evmData, crossChainTonAmount, feeData) {
|
|
219
225
|
const queryId = (0, Utils_1.generateRandomNumberByTimestamp)().randomNumber;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Cell } from '@ton/ton';
|
|
2
|
+
import { Asset, IConfiguration } from '../interfaces';
|
|
3
|
+
import { AssetType, BlockchainType, GeneratePayloadParams, Origin } from '../structs/Struct';
|
|
4
|
+
export declare class TAC implements Asset {
|
|
5
|
+
private readonly _configuration;
|
|
6
|
+
private readonly _evmAddress;
|
|
7
|
+
private _wrappedAsset;
|
|
8
|
+
readonly type: AssetType;
|
|
9
|
+
readonly origin: Origin;
|
|
10
|
+
private constructor();
|
|
11
|
+
static create(configuration: IConfiguration): Promise<TAC>;
|
|
12
|
+
get address(): string;
|
|
13
|
+
get rawAmount(): bigint;
|
|
14
|
+
get clone(): TAC;
|
|
15
|
+
withAmount(amount: number): TAC;
|
|
16
|
+
withRawAmount(rawAmount: bigint): TAC;
|
|
17
|
+
addAmount(amount: number): TAC;
|
|
18
|
+
addRawAmount(rawAmount: bigint): TAC;
|
|
19
|
+
getEVMAddress(): Promise<string>;
|
|
20
|
+
getTVMAddress(): Promise<string>;
|
|
21
|
+
generatePayload(params: GeneratePayloadParams): Cell;
|
|
22
|
+
checkCanBeTransferredBy(userAddress: string, blockchainType?: BlockchainType): Promise<void>;
|
|
23
|
+
getBalanceOf(userAddress: string, blockchainType?: BlockchainType): Promise<bigint>;
|
|
24
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TAC = void 0;
|
|
4
|
+
const errors_1 = require("../errors");
|
|
5
|
+
const Validator_1 = require("../sdk/Validator");
|
|
6
|
+
const Struct_1 = require("../structs/Struct");
|
|
7
|
+
const FT_1 = require("./FT");
|
|
8
|
+
class TAC {
|
|
9
|
+
constructor(_configuration, _evmAddress, _wrappedAsset) {
|
|
10
|
+
this._configuration = _configuration;
|
|
11
|
+
this._evmAddress = _evmAddress;
|
|
12
|
+
this._wrappedAsset = _wrappedAsset;
|
|
13
|
+
this.type = Struct_1.AssetType.FT;
|
|
14
|
+
this.origin = Struct_1.Origin.TAC;
|
|
15
|
+
}
|
|
16
|
+
static async create(configuration) {
|
|
17
|
+
const evmAddress = await configuration.nativeTACAddress();
|
|
18
|
+
const wrappedAsset = await FT_1.FT.fromAddress(configuration, evmAddress);
|
|
19
|
+
return new TAC(configuration, evmAddress, wrappedAsset);
|
|
20
|
+
}
|
|
21
|
+
get address() {
|
|
22
|
+
return this._wrappedAsset.address;
|
|
23
|
+
}
|
|
24
|
+
get rawAmount() {
|
|
25
|
+
return this._wrappedAsset.rawAmount;
|
|
26
|
+
}
|
|
27
|
+
get clone() {
|
|
28
|
+
return new TAC(this._configuration, this._evmAddress, this._wrappedAsset.clone);
|
|
29
|
+
}
|
|
30
|
+
withAmount(amount) {
|
|
31
|
+
const nextWrappedAsset = this._wrappedAsset.withAmount(amount);
|
|
32
|
+
if (nextWrappedAsset === this._wrappedAsset) {
|
|
33
|
+
return this;
|
|
34
|
+
}
|
|
35
|
+
return new TAC(this._configuration, this._evmAddress, nextWrappedAsset);
|
|
36
|
+
}
|
|
37
|
+
withRawAmount(rawAmount) {
|
|
38
|
+
const nextWrappedAsset = this._wrappedAsset.withRawAmount(rawAmount);
|
|
39
|
+
if (nextWrappedAsset === this._wrappedAsset) {
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
42
|
+
return new TAC(this._configuration, this._evmAddress, nextWrappedAsset);
|
|
43
|
+
}
|
|
44
|
+
addAmount(amount) {
|
|
45
|
+
this._wrappedAsset = this._wrappedAsset.addAmount(amount);
|
|
46
|
+
return this;
|
|
47
|
+
}
|
|
48
|
+
addRawAmount(rawAmount) {
|
|
49
|
+
this._wrappedAsset = this._wrappedAsset.addRawAmount(rawAmount);
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
async getEVMAddress() {
|
|
53
|
+
return this._evmAddress;
|
|
54
|
+
}
|
|
55
|
+
async getTVMAddress() {
|
|
56
|
+
return this.address;
|
|
57
|
+
}
|
|
58
|
+
generatePayload(params) {
|
|
59
|
+
return this._wrappedAsset.generatePayload(params);
|
|
60
|
+
}
|
|
61
|
+
async checkCanBeTransferredBy(userAddress, blockchainType = Struct_1.BlockchainType.TON) {
|
|
62
|
+
if ((await this.getBalanceOf(userAddress, blockchainType)) < this.rawAmount) {
|
|
63
|
+
throw (0, errors_1.insufficientBalanceError)(this.address);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async getBalanceOf(userAddress, blockchainType = Struct_1.BlockchainType.TON) {
|
|
67
|
+
if (blockchainType === Struct_1.BlockchainType.TAC) {
|
|
68
|
+
Validator_1.Validator.validateEVMAddress(userAddress);
|
|
69
|
+
return this._configuration.TACParams.provider.getBalance(userAddress);
|
|
70
|
+
}
|
|
71
|
+
return this._wrappedAsset.getBalanceOf(userAddress, blockchainType);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
exports.TAC = TAC;
|
package/dist/src/assets/TON.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { Cell } from '@ton/ton';
|
|
|
2
2
|
import { Asset, IConfiguration } from '../interfaces';
|
|
3
3
|
import type { SenderAbstraction } from '../sender';
|
|
4
4
|
import type { ShardTransaction } from '../structs/InternalStruct';
|
|
5
|
-
import { AssetType, FeeParams, Origin } from '../structs/Struct';
|
|
5
|
+
import { AssetType, BlockchainType, FeeParams, Origin } from '../structs/Struct';
|
|
6
6
|
export declare class TON implements Asset {
|
|
7
7
|
readonly address: string;
|
|
8
8
|
readonly type: AssetType;
|
|
@@ -29,6 +29,6 @@ export declare class TON implements Asset {
|
|
|
29
29
|
}): Cell;
|
|
30
30
|
getUserBalance(userAddress: string): Promise<bigint>;
|
|
31
31
|
static checkBalance(sender: SenderAbstraction, config: IConfiguration, transactions: ShardTransaction[]): Promise<void>;
|
|
32
|
-
checkCanBeTransferredBy(userAddress: string): Promise<void>;
|
|
33
|
-
getBalanceOf(userAddress: string): Promise<bigint>;
|
|
32
|
+
checkCanBeTransferredBy(userAddress: string, blockchainType?: BlockchainType): Promise<void>;
|
|
33
|
+
getBalanceOf(userAddress: string, blockchainType?: BlockchainType): Promise<bigint>;
|
|
34
34
|
}
|