@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.
Files changed (67) hide show
  1. package/LICENSE +20 -20
  2. package/README.md +203 -199
  3. package/dist/artifacts/dev/index.d.ts +2 -2
  4. package/dist/artifacts/dev/index.js +2 -1
  5. package/dist/artifacts/dev/ton/endpoints.d.ts +1 -0
  6. package/dist/artifacts/dev/ton/endpoints.js +2 -1
  7. package/dist/artifacts/mainnet/index.d.ts +2 -2
  8. package/dist/artifacts/mainnet/index.js +2 -1
  9. package/dist/artifacts/mainnet/ton/endpoints.d.ts +1 -0
  10. package/dist/artifacts/mainnet/ton/endpoints.js +2 -1
  11. package/dist/artifacts/testnet/index.d.ts +2 -2
  12. package/dist/artifacts/testnet/index.js +2 -1
  13. package/dist/artifacts/testnet/ton/endpoints.d.ts +1 -0
  14. package/dist/artifacts/testnet/ton/endpoints.js +2 -1
  15. package/dist/src/adapters/BaseContractOpener.js +8 -5
  16. package/dist/src/adapters/RetryableContractOpener.d.ts +6 -1
  17. package/dist/src/adapters/RetryableContractOpener.js +35 -8
  18. package/dist/src/adapters/ToncenterV3Indexer.d.ts +34 -0
  19. package/dist/src/adapters/ToncenterV3Indexer.js +123 -0
  20. package/dist/src/adapters/index.d.ts +1 -0
  21. package/dist/src/adapters/index.js +1 -0
  22. package/dist/src/assets/AssetFactory.d.ts +4 -1
  23. package/dist/src/assets/AssetFactory.js +4 -0
  24. package/dist/src/assets/FT.d.ts +3 -3
  25. package/dist/src/assets/FT.js +28 -15
  26. package/dist/src/assets/NFT.d.ts +3 -3
  27. package/dist/src/assets/NFT.js +10 -4
  28. package/dist/src/assets/TAC.d.ts +24 -0
  29. package/dist/src/assets/TAC.js +74 -0
  30. package/dist/src/assets/TON.d.ts +3 -3
  31. package/dist/src/assets/TON.js +12 -5
  32. package/dist/src/assets/index.d.ts +1 -0
  33. package/dist/src/assets/index.js +3 -1
  34. package/dist/src/errors/index.d.ts +1 -1
  35. package/dist/src/errors/index.js +8 -1
  36. package/dist/src/errors/instances.d.ts +6 -0
  37. package/dist/src/errors/instances.js +11 -1
  38. package/dist/src/interfaces/Asset.d.ts +11 -6
  39. package/dist/src/interfaces/ContractOpener.d.ts +1 -1
  40. package/dist/src/interfaces/IConfiguration.d.ts +1 -3
  41. package/dist/src/interfaces/ILiteSequencerClient.d.ts +14 -2
  42. package/dist/src/interfaces/IOperationTracker.d.ts +26 -1
  43. package/dist/src/interfaces/ITACTransactionManager.d.ts +20 -1
  44. package/dist/src/interfaces/ITacSDK.d.ts +52 -10
  45. package/dist/src/interfaces/IToncenterV3Indexer.d.ts +35 -0
  46. package/dist/src/interfaces/IToncenterV3Indexer.js +2 -0
  47. package/dist/src/interfaces/index.d.ts +1 -0
  48. package/dist/src/interfaces/index.js +1 -0
  49. package/dist/src/sdk/Consts.d.ts +6 -0
  50. package/dist/src/sdk/Consts.js +7 -1
  51. package/dist/src/sdk/LiteSequencerClient.d.ts +5 -2
  52. package/dist/src/sdk/LiteSequencerClient.js +59 -1
  53. package/dist/src/sdk/OperationTracker.d.ts +5 -1
  54. package/dist/src/sdk/OperationTracker.js +76 -0
  55. package/dist/src/sdk/TACTransactionManager.d.ts +4 -0
  56. package/dist/src/sdk/TACTransactionManager.js +181 -35
  57. package/dist/src/sdk/TONTransactionManager.js +28 -6
  58. package/dist/src/sdk/TacSdk.d.ts +8 -3
  59. package/dist/src/sdk/TacSdk.js +40 -2
  60. package/dist/src/sdk/Utils.d.ts +2 -1
  61. package/dist/src/sdk/Utils.js +5 -1
  62. package/dist/src/structs/InternalStruct.d.ts +41 -2
  63. package/dist/src/structs/Struct.d.ts +209 -6
  64. package/dist/src/structs/Struct.js +12 -1
  65. package/dist/src/wrappers/ContentUtils.d.ts +36 -13
  66. package/dist/src/wrappers/ContentUtils.js +197 -98
  67. package/package.json +121 -121
@@ -23,7 +23,11 @@ class LiteSequencerClient {
23
23
  const response = await this.httpClient.get(new URL(path, this.endpoint).toString(), {
24
24
  params: { transactionHash },
25
25
  });
26
- return response.data.response?.operationId || '';
26
+ const operationResponse = response.data.response;
27
+ if (operationResponse.length > 0) {
28
+ return operationResponse[0].operationId;
29
+ }
30
+ return '';
27
31
  }
28
32
  else {
29
33
  const response = await this.httpClient.get(new URL(path, this.endpoint).toString(), {
@@ -40,6 +44,23 @@ class LiteSequencerClient {
40
44
  throw (0, errors_1.operationFetchError)(`request ${requestLabel} failed to complete request`, error);
41
45
  }
42
46
  }
47
+ async getOperationIdByTonTransactionHash(transactionHash) {
48
+ const path = 'ton/operation-id';
49
+ const requestLabel = this.getRequestLabel('GET', path);
50
+ try {
51
+ const response = await this.httpClient.get(new URL(path, this.endpoint).toString(), {
52
+ params: { transactionHash },
53
+ });
54
+ return response.data.response || '';
55
+ }
56
+ catch (error) {
57
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
58
+ if (error?.response?.status === 404) {
59
+ return '';
60
+ }
61
+ throw (0, errors_1.operationFetchError)(`request ${requestLabel} failed to complete request`, error);
62
+ }
63
+ }
43
64
  async getOperationType(operationId) {
44
65
  const path = 'operation-type';
45
66
  const requestLabel = this.getRequestLabel('GET', path);
@@ -55,6 +76,22 @@ class LiteSequencerClient {
55
76
  throw (0, errors_1.operationFetchError)(`request ${requestLabel} failed to complete request`, error);
56
77
  }
57
78
  }
79
+ async getOperationTypeV2(operationId) {
80
+ const path = 'v2/operation-type';
81
+ const requestLabel = this.getRequestLabel('GET', path);
82
+ try {
83
+ const response = await this.httpClient.get(new URL(path, this.endpoint).toString(), {
84
+ params: {
85
+ operationId,
86
+ },
87
+ transformResponse: [Utils_1.toCamelCaseTransformer],
88
+ });
89
+ return response.data.response;
90
+ }
91
+ catch (error) {
92
+ throw (0, errors_1.operationFetchError)(`request ${requestLabel} failed to complete request`, error);
93
+ }
94
+ }
58
95
  async getOperationId(transactionLinker) {
59
96
  const path = 'ton/operation-id';
60
97
  const requestLabel = this.getRequestLabel('POST', path);
@@ -117,6 +154,27 @@ class LiteSequencerClient {
117
154
  throw (0, errors_1.profilingFetchError)(`request ${requestLabel} failed to complete request`, error);
118
155
  }
119
156
  }
157
+ async getStageProfilingsV2(operationIds, chunkSize = this.maxChunkSize) {
158
+ const path = 'v2/stage-profiling';
159
+ const requestLabel = this.getRequestLabel('POST', path);
160
+ if (!operationIds || operationIds.length === 0) {
161
+ throw (0, errors_1.emptyArrayError)('operationIds');
162
+ }
163
+ try {
164
+ const response = await this.processChunkedRequest(operationIds, async (chunk) => {
165
+ const response = await this.httpClient.post(new URL(path, this.endpoint).toString(), {
166
+ operationIds: chunk,
167
+ }, {
168
+ transformResponse: [Utils_1.toCamelCaseTransformer],
169
+ });
170
+ return response.data;
171
+ }, chunkSize);
172
+ return response.response;
173
+ }
174
+ catch (error) {
175
+ throw (0, errors_1.profilingFetchError)(`request ${requestLabel} failed to complete request`, error);
176
+ }
177
+ }
120
178
  async getOperationStatuses(operationIds, chunkSize = this.maxChunkSize) {
121
179
  const path = 'status';
122
180
  const requestLabel = this.getRequestLabel('POST', path);
@@ -1,5 +1,5 @@
1
1
  import { ILiteSequencerClient, ILiteSequencerClientFactory, ILogger, IOperationTracker } from '../interfaces';
2
- import { ConvertCurrencyParams, ConvertedCurrencyResult, ExecutionStages, ExecutionStagesByOperationId, GetTVMExecutorFeeParams, Network, OperationIdsByShardsKey, OperationType, SimplifiedStatuses, StatusInfo, StatusInfosByOperationId, SuggestedTVMExecutorFee, TACSimulationParams, TACSimulationResult, TransactionLinker, WaitOptions } from '../structs/Struct';
2
+ import { ConvertCurrencyParams, ConvertedCurrencyResult, ExecutionStages, ExecutionStagesByOperationId, ExecutionStagesV2, ExecutionStagesV2ByOperationId, GetTVMExecutorFeeParams, Network, OperationIdsByShardsKey, OperationType, OperationTypeV2Info, SimplifiedStatuses, StatusInfo, StatusInfosByOperationId, SuggestedTVMExecutorFee, TACSimulationParams, TACSimulationResult, TransactionLinker, WaitOptions } from '../structs/Struct';
3
3
  export declare class DefaultLiteSequencerClientFactory implements ILiteSequencerClientFactory {
4
4
  createClients(endpoints: string[]): ILiteSequencerClient[];
5
5
  }
@@ -8,11 +8,15 @@ export declare class OperationTracker implements IOperationTracker {
8
8
  private readonly logger;
9
9
  constructor(network: Network, customLiteSequencerEndpoints?: string[], logger?: ILogger, clientFactory?: ILiteSequencerClientFactory);
10
10
  getOperationIdByTransactionHash(transactionHash: string, waitOptions?: WaitOptions<string> | null): Promise<string>;
11
+ getOperationIdByTonTransactionHash(transactionHash: string, waitOptions?: WaitOptions<string> | null): Promise<string>;
11
12
  getOperationType(operationId: string, waitOptions?: WaitOptions<OperationType> | null): Promise<OperationType>;
13
+ getOperationTypeV2(operationId: string, waitOptions?: WaitOptions<OperationTypeV2Info> | null): Promise<OperationTypeV2Info>;
12
14
  getOperationId(transactionLinker: TransactionLinker, waitOptions?: WaitOptions<string> | null): Promise<string>;
13
15
  getOperationIdsByShardsKeys(shardsKeys: string[], caller: string, waitOptions?: WaitOptions<OperationIdsByShardsKey> | null, chunkSize?: number): Promise<OperationIdsByShardsKey>;
14
16
  getStageProfiling(operationId: string, waitOptions?: WaitOptions<ExecutionStages> | null): Promise<ExecutionStages>;
15
17
  getStageProfilings(operationIds: string[], waitOptions?: WaitOptions<ExecutionStagesByOperationId> | null, chunkSize?: number): Promise<ExecutionStagesByOperationId>;
18
+ getStageProfilingV2(operationId: string, waitOptions?: WaitOptions<ExecutionStagesV2> | null): Promise<ExecutionStagesV2>;
19
+ getStageProfilingsV2(operationIds: string[], waitOptions?: WaitOptions<ExecutionStagesV2ByOperationId> | null, chunkSize?: number): Promise<ExecutionStagesV2ByOperationId>;
16
20
  getOperationStatuses(operationIds: string[], waitOptions?: WaitOptions<StatusInfosByOperationId> | null, chunkSize?: number): Promise<StatusInfosByOperationId>;
17
21
  getOperationStatus(operationId: string, waitOptions?: WaitOptions<StatusInfo> | null): Promise<StatusInfo>;
18
22
  getSimplifiedOperationStatus(transactionLinker: TransactionLinker): Promise<SimplifiedStatuses>;
@@ -52,6 +52,24 @@ class OperationTracker {
52
52
  ? await requestFn()
53
53
  : await (0, Utils_1.waitUntilSuccess)({ logger: this.logger, ...waitOptions }, requestFn, `OperationTracker: Getting operation ID by transaction hash ${(0, Utils_1.formatObjectForLogging)(transactionHash)}`);
54
54
  }
55
+ async getOperationIdByTonTransactionHash(transactionHash, waitOptions) {
56
+ const requestFn = async () => {
57
+ let lastError;
58
+ for (const client of this.clients) {
59
+ try {
60
+ const id = await client.getOperationIdByTonTransactionHash(transactionHash);
61
+ return id;
62
+ }
63
+ catch (error) {
64
+ lastError = error;
65
+ }
66
+ }
67
+ throw (0, errors_1.allEndpointsFailedError)(lastError, waitOptions?.includeErrorTrace ?? false);
68
+ };
69
+ return waitOptions === null
70
+ ? await requestFn()
71
+ : await (0, Utils_1.waitUntilSuccess)({ logger: this.logger, ...waitOptions }, requestFn, `OperationTracker: Getting operation ID by TON transaction hash ${(0, Utils_1.formatObjectForLogging)(transactionHash)}`);
72
+ }
55
73
  async getOperationType(operationId, waitOptions) {
56
74
  const requestFn = async () => {
57
75
  let lastError;
@@ -70,6 +88,24 @@ class OperationTracker {
70
88
  ? await requestFn()
71
89
  : await (0, Utils_1.waitUntilSuccess)({ logger: this.logger, ...waitOptions }, requestFn, `OperationTracker: Getting operation type for ${(0, Utils_1.formatObjectForLogging)(operationId)}`);
72
90
  }
91
+ async getOperationTypeV2(operationId, waitOptions) {
92
+ const requestFn = async () => {
93
+ let lastError;
94
+ for (const client of this.clients) {
95
+ try {
96
+ const type = await client.getOperationTypeV2(operationId);
97
+ return type;
98
+ }
99
+ catch (error) {
100
+ lastError = error;
101
+ }
102
+ }
103
+ throw (0, errors_1.allEndpointsFailedError)(lastError, waitOptions?.includeErrorTrace ?? false);
104
+ };
105
+ return waitOptions === null
106
+ ? await requestFn()
107
+ : await (0, Utils_1.waitUntilSuccess)({ logger: this.logger, ...waitOptions }, requestFn, `OperationTracker: Getting operation type v2 for ${(0, Utils_1.formatObjectForLogging)(operationId)}`);
108
+ }
73
109
  async getOperationId(transactionLinker, waitOptions) {
74
110
  const requestFn = async () => {
75
111
  let lastError;
@@ -146,6 +182,46 @@ class OperationTracker {
146
182
  ? await requestFn()
147
183
  : await (0, Utils_1.waitUntilSuccess)({ logger: this.logger, ...waitOptions }, requestFn, `OperationTracker: Getting stage profilings for operations: ${operationIds.join(', ')} chunkSize=${chunkSize}`);
148
184
  }
185
+ async getStageProfilingV2(operationId, waitOptions) {
186
+ const requestFn = async () => {
187
+ let lastError;
188
+ for (const client of this.clients) {
189
+ try {
190
+ const map = await client.getStageProfilingsV2([operationId]);
191
+ const result = map[operationId];
192
+ if (!result) {
193
+ throw new Error(`No stageProfiling v2 data for operationId=${operationId}`);
194
+ }
195
+ return result;
196
+ }
197
+ catch (error) {
198
+ lastError = error;
199
+ }
200
+ }
201
+ throw (0, errors_1.allEndpointsFailedError)(lastError, waitOptions?.includeErrorTrace ?? false);
202
+ };
203
+ return waitOptions === null
204
+ ? await requestFn()
205
+ : await (0, Utils_1.waitUntilSuccess)({ logger: this.logger, ...waitOptions }, requestFn, `OperationTracker: Getting stage profiling v2 for operation ${operationId}`);
206
+ }
207
+ async getStageProfilingsV2(operationIds, waitOptions, chunkSize = 100) {
208
+ const requestFn = async () => {
209
+ let lastError;
210
+ for (const client of this.clients) {
211
+ try {
212
+ const result = await client.getStageProfilingsV2(operationIds, chunkSize);
213
+ return result;
214
+ }
215
+ catch (error) {
216
+ lastError = error;
217
+ }
218
+ }
219
+ throw (0, errors_1.allEndpointsFailedError)(lastError, waitOptions?.includeErrorTrace ?? false);
220
+ };
221
+ return waitOptions === null
222
+ ? await requestFn()
223
+ : await (0, Utils_1.waitUntilSuccess)({ logger: this.logger, ...waitOptions }, requestFn, `OperationTracker: Getting stage profilings v2 for operations: ${operationIds.join(', ')} chunkSize=${chunkSize}`);
224
+ }
149
225
  async getOperationStatuses(operationIds, waitOptions, chunkSize = 100) {
150
226
  const requestFn = async () => {
151
227
  let lastError;
@@ -1,10 +1,14 @@
1
1
  import { Wallet } from 'ethers';
2
2
  import { Asset, IConfiguration, ILogger, IOperationTracker, ITACTransactionManager } from '../interfaces';
3
+ import { CrossChainTransactionToTONOptions, TACCrossChainTransactionResult } from '../structs/Struct';
3
4
  export declare class TACTransactionManager implements ITACTransactionManager {
4
5
  private readonly config;
5
6
  private readonly operationTracker;
6
7
  private readonly logger;
7
8
  constructor(config: IConfiguration, operationTracker: IOperationTracker, logger?: ILogger);
9
+ private prepareBridgeTokensToTON;
8
10
  private approveAsset;
11
+ private estimateApprovalGas;
12
+ sendCrossChainTransactionToTON(signer: Wallet, tonTarget: string, assets?: Asset[], options?: CrossChainTransactionToTONOptions): Promise<TACCrossChainTransactionResult>;
9
13
  bridgeTokensToTON(signer: Wallet, value: bigint, tonTarget: string, assets?: Asset[], tvmExecutorFee?: bigint, tvmValidExecutors?: string[]): Promise<string>;
10
14
  }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TACTransactionManager = void 0;
4
4
  const ethers_1 = require("ethers");
5
5
  const assets_1 = require("../assets");
6
+ const errors_1 = require("../errors");
6
7
  const Struct_1 = require("../structs/Struct");
7
8
  const Consts_1 = require("./Consts");
8
9
  const Logger_1 = require("./Logger");
@@ -14,28 +15,10 @@ class TACTransactionManager {
14
15
  this.operationTracker = operationTracker;
15
16
  this.logger = logger;
16
17
  }
17
- async approveAsset(asset, signer, spenderAddress) {
18
- const evmAddress = await asset.getEVMAddress();
19
- if (asset.type === Struct_1.AssetType.FT) {
20
- this.logger.debug(`Approving FT ${evmAddress} for ${spenderAddress}`);
21
- const erc20Abi = this.config.artifacts.tac.compilationArtifacts.IERC20WithDecimals.abi;
22
- const contract = new ethers_1.ethers.Contract(evmAddress, erc20Abi, this.config.TACParams.provider);
23
- const tx = await contract.connect(signer).approve(spenderAddress, asset.rawAmount);
24
- await tx.wait();
25
- }
26
- else if (asset.type === Struct_1.AssetType.NFT) {
27
- this.logger.debug(`Approving NFT ${evmAddress} for ${spenderAddress}`);
28
- const erc721Abi = this.config.artifacts.tac.compilationArtifacts.IERC721.abi;
29
- const contract = new ethers_1.ethers.Contract(evmAddress, erc721Abi, this.config.TACParams.provider);
30
- const tx = await contract.connect(signer).approve(spenderAddress, asset.addresses.index);
31
- await tx.wait();
32
- }
33
- this.logger.debug(`Approved ${evmAddress} for ${spenderAddress}`);
34
- }
35
- async bridgeTokensToTON(signer, value, tonTarget, assets = [], tvmExecutorFee, tvmValidExecutors) {
36
- this.logger.debug('Bridging tokens to TON');
18
+ async prepareBridgeTokensToTON(value, tonTarget, assets = [], tvmExecutorFee, tvmValidExecutors) {
37
19
  Validator_1.Validator.validateTVMAddress(tonTarget);
38
- // Add native TAC asset if value > 0
20
+ Validator_1.Validator.validateTVMAddresses(tvmValidExecutors);
21
+ const validExecutors = tvmValidExecutors?.length ? tvmValidExecutors : this.config.getTrustedTONExecutors;
39
22
  const tonAssets = [...assets];
40
23
  if (value > 0n) {
41
24
  tonAssets.push((await assets_1.AssetFactory.from(this.config, {
@@ -43,26 +26,19 @@ class TACTransactionManager {
43
26
  tokenType: Struct_1.AssetType.FT,
44
27
  })).withRawAmount(value));
45
28
  }
46
- // Calculate executor fee if not provided
47
29
  if (!tvmExecutorFee) {
48
30
  const feeParams = {
49
31
  tonAssets: (0, Utils_1.mapAssetsToTonAssets)(tonAssets),
50
32
  feeSymbol: Consts_1.TAC_SYMBOL,
51
- tvmValidExecutors: tvmValidExecutors ?? [],
33
+ tvmValidExecutors: validExecutors,
52
34
  };
53
- const suggestedFee = await this.operationTracker.getTVMExecutorFee(feeParams);
35
+ const suggestedFee = await this.operationTracker.getTVMExecutorFee(feeParams, null);
54
36
  this.logger.debug(`Suggested TON executor fee: ${(0, Utils_1.formatObjectForLogging)(suggestedFee)}`);
55
37
  tvmExecutorFee = BigInt(suggestedFee.inTAC);
56
38
  }
57
- // Approve all assets
58
- const crossChainLayerAddress = await this.config.TACParams.crossChainLayer.getAddress();
59
- for (const asset of assets) {
60
- await this.approveAsset(asset, signer, crossChainLayerAddress);
61
- }
62
39
  const protocolFee = await this.config.TACParams.crossChainLayer.getProtocolFee();
63
- const shardsKey = BigInt(Math.round(Math.random() * 1e18));
40
+ const shardsKey = BigInt(Math.round(Math.random() * Consts_1.SHARDS_KEY_RANDOM_RANGE));
64
41
  this.logger.debug(`Shards key: ${shardsKey}, Protocol fee: ${protocolFee}`);
65
- // Prepare bridge data
66
42
  const [toBridge, toBridgeNFT] = await Promise.all([
67
43
  Promise.all(assets
68
44
  .filter((a) => a.type === Struct_1.AssetType.FT)
@@ -74,7 +50,7 @@ class TACTransactionManager {
74
50
  .filter((a) => a.type === Struct_1.AssetType.NFT)
75
51
  .map(async (a) => ({
76
52
  evmAddress: await a.getEVMAddress(),
77
- amount: 1n,
53
+ amount: Consts_1.NFT_BRIDGE_AMOUNT,
78
54
  tokenId: a.addresses.index,
79
55
  }))),
80
56
  ]);
@@ -84,15 +60,185 @@ class TACTransactionManager {
84
60
  tvmPayload: '',
85
61
  tvmProtocolFee: protocolFee,
86
62
  tvmExecutorFee,
87
- tvmValidExecutors: this.config.getTrustedTONExecutors,
63
+ tvmValidExecutors: validExecutors,
88
64
  toBridge,
89
65
  toBridgeNFT,
90
66
  };
91
- const totalValue = value + BigInt(outMessage.tvmProtocolFee) + BigInt(outMessage.tvmExecutorFee);
67
+ return {
68
+ outMessagePayload: this.config.artifacts.tac.utils.encodeOutMessageV1(outMessage),
69
+ totalValue: value + BigInt(outMessage.tvmProtocolFee) + BigInt(outMessage.tvmExecutorFee),
70
+ };
71
+ }
72
+ async approveAsset(asset, signer, spenderAddress, transactionParams = {}) {
73
+ const evmAddress = await asset.getEVMAddress();
74
+ const hasTransactionParams = Object.keys(transactionParams).length > 0;
75
+ if (asset.type === Struct_1.AssetType.FT) {
76
+ this.logger.debug(`Approving FT ${evmAddress} for ${spenderAddress}`);
77
+ const contract = new ethers_1.ethers.Contract(evmAddress, this.config.artifacts.tac.compilationArtifacts.IERC20WithDecimals.abi, this.config.TACParams.provider);
78
+ const tx = hasTransactionParams
79
+ ? await contract.connect(signer).approve(spenderAddress, asset.rawAmount, transactionParams)
80
+ : await contract.connect(signer).approve(spenderAddress, asset.rawAmount);
81
+ await tx.wait();
82
+ }
83
+ else {
84
+ this.logger.debug(`Approving NFT ${evmAddress} for ${spenderAddress}`);
85
+ const contract = new ethers_1.ethers.Contract(evmAddress, this.config.artifacts.tac.compilationArtifacts.IERC721.abi, this.config.TACParams.provider);
86
+ const tx = hasTransactionParams
87
+ ? await contract
88
+ .connect(signer)
89
+ .approve(spenderAddress, asset.addresses.index, transactionParams)
90
+ : await contract.connect(signer).approve(spenderAddress, asset.addresses.index);
91
+ await tx.wait();
92
+ }
93
+ this.logger.debug(`Approved ${evmAddress} for ${spenderAddress}`);
94
+ }
95
+ async estimateApprovalGas(asset, signer, spenderAddress, transactionParams = {}) {
96
+ const evmAddress = await asset.getEVMAddress();
97
+ if (asset.type === Struct_1.AssetType.FT) {
98
+ const contract = new ethers_1.ethers.Contract(evmAddress, this.config.artifacts.tac.compilationArtifacts.IERC20WithDecimals.abi, this.config.TACParams.provider);
99
+ return contract.connect(signer).approve.estimateGas(spenderAddress, asset.rawAmount, transactionParams);
100
+ }
101
+ const contract = new ethers_1.ethers.Contract(evmAddress, this.config.artifacts.tac.compilationArtifacts.IERC721.abi, this.config.TACParams.provider);
102
+ return contract
103
+ .connect(signer)
104
+ .approve.estimateGas(spenderAddress, asset.addresses.index, transactionParams);
105
+ }
106
+ async sendCrossChainTransactionToTON(signer, tonTarget, assets = [], options) {
107
+ const signerAddress = signer.address;
108
+ const nativeTACAsset = await assets_1.AssetFactory.from(this.config, {
109
+ address: await this.config.nativeTACAddress(),
110
+ tokenType: Struct_1.AssetType.FT,
111
+ });
112
+ let value = 0n;
113
+ const bridgeAssets = [];
114
+ for (const asset of assets) {
115
+ if (asset.origin === Struct_1.Origin.TAC &&
116
+ asset.type === Struct_1.AssetType.FT &&
117
+ asset.address === nativeTACAsset.address) {
118
+ value += asset.rawAmount;
119
+ continue;
120
+ }
121
+ bridgeAssets.push(asset);
122
+ }
123
+ const crossChainLayerAddress = await this.config.TACParams.crossChainLayer.getAddress();
124
+ const preparedBridge = await this.prepareBridgeTokensToTON(value, tonTarget, bridgeAssets, options?.tvmExecutorFee, options?.tvmValidExecutors);
125
+ const feeData = await this.config.TACParams.provider.getFeeData();
126
+ const maxFeePerGas = feeData.maxFeePerGas;
127
+ if (maxFeePerGas === null) {
128
+ throw (0, errors_1.gasPriceFetchError)('TAC provider did not return maxFeePerGas');
129
+ }
130
+ const latestBlock = await this.config.TACParams.provider.getBlock('latest').catch((error) => {
131
+ throw (0, errors_1.blockGasLimitFetchError)('provider failed to return latest block', error);
132
+ });
133
+ const blockGasLimit = latestBlock?.gasLimit;
134
+ if (blockGasLimit === null || blockGasLimit === undefined) {
135
+ throw (0, errors_1.blockGasLimitFetchError)('provider did not return latest block gas limit');
136
+ }
137
+ const transactionParams = {
138
+ maxFeePerGas,
139
+ maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ?? Consts_1.DEFAULT_EIP1559_PRIORITY_FEE,
140
+ };
141
+ const shouldValidateAssetsBalance = options?.validateAssetsBalance ?? true;
142
+ if (shouldValidateAssetsBalance) {
143
+ await Promise.all(bridgeAssets.map((asset) => asset.checkCanBeTransferredBy(signerAddress, Struct_1.BlockchainType.TAC)));
144
+ }
145
+ let requiredApprovalBalance = 0n;
146
+ const approvalPlans = [];
147
+ for (const asset of bridgeAssets) {
148
+ const estimatedGas = await this.estimateApprovalGas(asset, signer, crossChainLayerAddress, transactionParams);
149
+ if (estimatedGas > blockGasLimit) {
150
+ throw (0, errors_1.estimatedGasExceedsBlockGasLimitError)('approve', estimatedGas, blockGasLimit);
151
+ }
152
+ requiredApprovalBalance += estimatedGas * maxFeePerGas;
153
+ approvalPlans.push({ asset, gasLimit: estimatedGas });
154
+ }
155
+ const approvalBalance = await this.config.TACParams.provider.getBalance(signer.address);
156
+ if (approvalBalance < requiredApprovalBalance) {
157
+ throw (0, errors_1.insufficientBalanceError)(Consts_1.TAC_SYMBOL);
158
+ }
159
+ for (const approval of approvalPlans) {
160
+ await this.approveAsset(approval.asset, signer, crossChainLayerAddress, {
161
+ ...transactionParams,
162
+ gasLimit: approval.gasLimit,
163
+ });
164
+ }
165
+ const sendMessageGas = await this.config.TACParams.crossChainLayer
166
+ .connect(signer)
167
+ .sendMessage.estimateGas(Consts_1.CROSS_CHAIN_MESSAGE_VERSION_V1, preparedBridge.outMessagePayload, {
168
+ ...transactionParams,
169
+ value: preparedBridge.totalValue,
170
+ });
171
+ if (sendMessageGas > blockGasLimit) {
172
+ throw (0, errors_1.estimatedGasExceedsBlockGasLimitError)('sendMessage', sendMessageGas, blockGasLimit);
173
+ }
174
+ const requiredSendMessageBalance = preparedBridge.totalValue + sendMessageGas * maxFeePerGas;
175
+ const sendMessageBalance = await this.config.TACParams.provider.getBalance(signer.address);
176
+ if (sendMessageBalance < requiredSendMessageBalance) {
177
+ throw (0, errors_1.insufficientBalanceError)(Consts_1.TAC_SYMBOL);
178
+ }
179
+ const tx = await this.config.TACParams.crossChainLayer
180
+ .connect(signer)
181
+ .sendMessage(Consts_1.CROSS_CHAIN_MESSAGE_VERSION_V1, preparedBridge.outMessagePayload, {
182
+ ...transactionParams,
183
+ gasLimit: sendMessageGas,
184
+ value: preparedBridge.totalValue,
185
+ });
186
+ await tx.wait();
187
+ this.logger.debug(`Transaction hash: ${tx.hash}`);
188
+ const shouldWaitForOperationId = options?.waitOperationId ?? true;
189
+ if (!shouldWaitForOperationId) {
190
+ return { txHash: tx.hash };
191
+ }
192
+ const waitOptions = options?.waitOptions ?? {};
193
+ waitOptions.successCheck = waitOptions.successCheck ?? ((id) => !!id);
194
+ waitOptions.logger = waitOptions.logger ?? this.logger;
195
+ const operationId = await this.operationTracker
196
+ .getOperationIdByTransactionHash(tx.hash, waitOptions)
197
+ .catch((error) => {
198
+ this.logger.error(`Error while waiting for operation ID by transaction hash: ${error}`);
199
+ return undefined;
200
+ });
201
+ const shouldWaitForFinalization = options?.waitFinalization ?? true;
202
+ if (!shouldWaitForFinalization) {
203
+ return { txHash: tx.hash, operationId };
204
+ }
205
+ if (!operationId) {
206
+ throw errors_1.operationIdRequiredForFinalizationError;
207
+ }
208
+ const finalizationWaitOptions = options?.finalizationWaitOptions ?? {};
209
+ finalizationWaitOptions.successCheck = finalizationWaitOptions.successCheck ?? Utils_1.isFinalProfiling;
210
+ finalizationWaitOptions.logger = finalizationWaitOptions.logger ?? this.logger;
211
+ finalizationWaitOptions.onSuccess =
212
+ finalizationWaitOptions.onSuccess ??
213
+ (async (profilingData) => {
214
+ if (!profilingData.executedInTON.exists) {
215
+ return;
216
+ }
217
+ if (!profilingData.executedInTON.stageData?.success) {
218
+ throw errors_1.executedInTONStageFailedError;
219
+ }
220
+ for (const transaction of profilingData.executedInTON.stageData?.transactions ?? []) {
221
+ await this.config.TONParams.contractOpener.trackTransactionTree(this.config.TONParams.crossChainLayerAddress, transaction.hash);
222
+ }
223
+ });
224
+ await this.operationTracker.getStageProfiling(operationId, finalizationWaitOptions);
225
+ return { txHash: tx.hash, operationId };
226
+ }
227
+ async bridgeTokensToTON(signer, value, tonTarget, assets = [], tvmExecutorFee, tvmValidExecutors) {
228
+ this.logger.debug('Bridging tokens to TON');
229
+ Validator_1.Validator.validateTVMAddress(tonTarget);
230
+ Validator_1.Validator.validateTVMAddresses(tvmValidExecutors);
231
+ const crossChainLayerAddress = await this.config.TACParams.crossChainLayer.getAddress();
232
+ const { outMessagePayload, totalValue } = await this.prepareBridgeTokensToTON(value, tonTarget, assets, tvmExecutorFee, tvmValidExecutors);
92
233
  this.logger.debug(`Total value: ${totalValue}`);
234
+ for (const asset of assets) {
235
+ await this.approveAsset(asset, signer, crossChainLayerAddress);
236
+ }
93
237
  const tx = await this.config.TACParams.crossChainLayer
94
238
  .connect(signer)
95
- .sendMessage(1n, this.config.artifacts.tac.utils.encodeOutMessageV1(outMessage), { value: totalValue });
239
+ .sendMessage(Consts_1.CROSS_CHAIN_MESSAGE_VERSION_V1, outMessagePayload, {
240
+ value: totalValue,
241
+ });
96
242
  await tx.wait();
97
243
  this.logger.debug(`Transaction hash: ${tx.hash}`);
98
244
  return tx.hash;
@@ -68,8 +68,7 @@ class TONTransactionManager {
68
68
  async prepareCrossChainTransaction(evmProxyMsg, sender, assets, options, skipAssetsBalanceValidation = false) {
69
69
  this.logger.debug('Preparing cross-chain transaction');
70
70
  const caller = sender.getSenderAddress();
71
- const { allowSimulationError = false, isRoundTrip = undefined, calculateRollbackFee = true, validateAssetsBalance = true, evmDataBuilder = Utils_1.buildEvmDataCell, } = options || {};
72
- const { evmValidExecutors = [], tvmValidExecutors = [] } = options || {};
71
+ const { allowSimulationError = false, isRoundTrip = undefined, calculateRollbackFee = true, validateAssetsBalance = true, evmValidExecutors = [], tvmValidExecutors = [], evmDataBuilder = Utils_1.buildEvmDataCell, } = options || {};
73
72
  Validator_1.Validator.validateEVMAddress(evmProxyMsg.evmTargetAddress);
74
73
  const aggregatedData = (0, Utils_1.aggregateTokens)(assets);
75
74
  Validator_1.Validator.validateEVMAddresses(evmValidExecutors);
@@ -182,16 +181,14 @@ class TONTransactionManager {
182
181
  if (!shouldWaitForOperationId) {
183
182
  return { sendTransactionResult, ...transactionLinker };
184
183
  }
185
- const waitOptions = tx.options?.waitOptions ?? {};
186
184
  const ensureTxExecuted = tx.options?.ensureTxExecuted ?? true;
187
185
  if (ensureTxExecuted && sendTransactionResult.boc) {
188
186
  const hash = (0, Utils_1.getNormalizedExtMessageHash)((0, ton_1.loadMessage)(ton_1.Cell.fromBase64(sendTransactionResult.boc).beginParse()));
189
187
  this.logger.info(`Tracking transaction tree for hash: ${hash}`);
190
- await this.config.TONParams.contractOpener.trackTransactionTree(sender.getSenderAddress(), hash, {
191
- maxDepth: Consts_1.DEFAULT_FIND_TX_MAX_DEPTH,
192
- });
188
+ await this.config.TONParams.contractOpener.trackTransactionTree(sender.getSenderAddress(), hash);
193
189
  this.logger.info(`Transaction tree successful`);
194
190
  }
191
+ const waitOptions = tx.options?.waitOptions ?? {};
195
192
  waitOptions.successCheck = waitOptions.successCheck ?? ((id) => !!id);
196
193
  waitOptions.logger = waitOptions.logger ?? this.logger;
197
194
  const operationId = await this.operationTracker
@@ -200,6 +197,31 @@ class TONTransactionManager {
200
197
  this.logger.error(`Error while waiting for operation ID: ${error}`);
201
198
  return undefined;
202
199
  });
200
+ const shouldWaitForFinalization = tx.options?.waitFinalization ?? true;
201
+ if (!shouldWaitForFinalization) {
202
+ return { sendTransactionResult, ...transactionLinker };
203
+ }
204
+ if (!operationId) {
205
+ throw errors_1.operationIdRequiredForFinalizationError;
206
+ }
207
+ const finalizationWaitOptions = tx.options?.finalizationWaitOptions ?? {};
208
+ finalizationWaitOptions.successCheck = finalizationWaitOptions.successCheck ?? Utils_1.isFinalProfiling;
209
+ finalizationWaitOptions.logger = finalizationWaitOptions.logger ?? this.logger;
210
+ finalizationWaitOptions.onSuccess =
211
+ finalizationWaitOptions.onSuccess ??
212
+ (async (profilingData) => {
213
+ const stage = profilingData.executedInTON;
214
+ if (!stage.exists) {
215
+ return;
216
+ }
217
+ if (!stage.stageData?.success) {
218
+ throw errors_1.executedInTONStageFailedError;
219
+ }
220
+ for (const transaction of stage.stageData?.transactions ?? []) {
221
+ await this.config.TONParams.contractOpener.trackTransactionTree(this.config.TONParams.crossChainLayerAddress, transaction.hash);
222
+ }
223
+ });
224
+ await this.operationTracker.getStageProfiling(operationId, finalizationWaitOptions);
203
225
  return { sendTransactionResult, operationId, ...transactionLinker };
204
226
  }
205
227
  async sendCrossChainTransactions(sender, txs, options) {
@@ -1,9 +1,9 @@
1
1
  import { Wallet } from 'ethers';
2
2
  import { JettonMinterData, NFTItemData } from '../../artifacts/tonTypes';
3
- import { FT, NFT } from '../assets';
3
+ import { FT, NFT, TAC, TON } from '../assets';
4
4
  import { ContractOpener, IConfiguration, ILogger, IOperationTracker, ITacExplorerClient, ITacSDK } from '../interfaces';
5
5
  import type { SenderAbstraction } from '../sender';
6
- import { AssetFromFTArg, AssetFromNFTCollectionArg, AssetFromNFTItemArg, AssetLike, BatchCrossChainTxWithAssetLike, CrossChainPayloadResult, CrossChainTransactionOptions, CrossChainTransactionsOptions, CrosschainTx, EVMAddress, EvmProxyMsg, ExecutionFeeEstimationResult, NFTAddressType, SDKParams, SuggestedTVMExecutorFee, TacGasPrice, TACSimulationParams, TACSimulationResult, TransactionLinkerWithOperationId, TVMAddress, UserWalletBalanceExtended } from '../structs/Struct';
6
+ import { AssetFromFTArg, AssetFromNFTCollectionArg, AssetFromNFTItemArg, AssetLike, BatchCrossChainTxWithAssetLike, CrossChainPayloadResult, CrossChainTransactionOptions, CrossChainTransactionsOptions, CrossChainTransactionToTONOptions, CrosschainTx, EVMAddress, EvmProxyMsg, ExecutionFeeEstimationResult, NFTAddressType, SDKParams, SuggestedTVMExecutorFee, TACCrossChainTransactionResult, TacGasPrice, TACSimulationParams, TACSimulationResult, TransactionLinkerWithOperationId, TVMAddress, UserWalletBalanceExtended, WaitOptions } from '../structs/Struct';
7
7
  export declare class TacSdk implements ITacSDK {
8
8
  readonly config: IConfiguration;
9
9
  readonly contactOpener: ContractOpener;
@@ -12,6 +12,7 @@ export declare class TacSdk implements ITacSDK {
12
12
  private readonly simulator;
13
13
  private readonly tonTransactionManager;
14
14
  private readonly tacTransactionManager;
15
+ private readonly tonIndexer;
15
16
  private constructor();
16
17
  static create(sdkParams: SDKParams, logger?: ILogger): Promise<TacSdk>;
17
18
  closeConnections(): unknown;
@@ -23,12 +24,15 @@ export declare class TacSdk implements ITacSDK {
23
24
  getSimulationInfo(evmProxyMsg: EvmProxyMsg, sender: SenderAbstraction, assets?: AssetLike[], options?: CrossChainTransactionOptions): Promise<ExecutionFeeEstimationResult>;
24
25
  getTVMExecutorFeeInfo(assets: AssetLike[], feeSymbol: string, tvmValidExecutors?: string[]): Promise<SuggestedTVMExecutorFee>;
25
26
  sendCrossChainTransaction(evmProxyMsg: EvmProxyMsg, sender: SenderAbstraction, assets?: AssetLike[], options?: CrossChainTransactionOptions): Promise<TransactionLinkerWithOperationId>;
27
+ sendCrossChainTransactionToTON(signer: Wallet, tonTarget: string, assets?: AssetLike[], options?: CrossChainTransactionToTONOptions): Promise<TACCrossChainTransactionResult>;
26
28
  sendCrossChainTransactions(sender: SenderAbstraction, txs: BatchCrossChainTxWithAssetLike[], options?: CrossChainTransactionsOptions): Promise<TransactionLinkerWithOperationId[]>;
29
+ getOperationIdByTonTransactionHash(transactionHash: string, waitOptions?: WaitOptions<string> | null): Promise<string>;
30
+ getOperationIdByTonTransactionBoc(boc: string, waitOptions?: WaitOptions<string> | null): Promise<string>;
27
31
  bridgeTokensToTON(signer: Wallet, value: bigint, tonTarget: string, assets?: AssetLike[], tvmExecutorFee?: bigint, tvmValidExecutors?: string[]): Promise<string>;
28
32
  isContractDeployedOnTVM(address: string): Promise<boolean>;
29
33
  simulateTACMessage(req: TACSimulationParams): Promise<TACSimulationResult>;
30
34
  simulateTransactions(sender: SenderAbstraction, txs: CrosschainTx[]): Promise<ExecutionFeeEstimationResult[]>;
31
- getAsset(args: AssetFromFTArg): Promise<FT>;
35
+ getAsset(args: AssetFromFTArg): Promise<FT | TAC | TON>;
32
36
  getAsset(args: AssetFromNFTCollectionArg): Promise<NFT>;
33
37
  getAsset(args: AssetFromNFTItemArg): Promise<NFT>;
34
38
  getUserJettonWalletAddress(userAddress: string, tokenAddress: string): Promise<string>;
@@ -47,4 +51,5 @@ export declare class TacSdk implements ITacSDK {
47
51
  prepareCrossChainTransactionPayload(evmProxyMsg: EvmProxyMsg, senderAddress: string, assets?: AssetLike[], options?: CrossChainTransactionOptions): Promise<CrossChainPayloadResult[]>;
48
52
  getTACGasPrice(): Promise<TacGasPrice>;
49
53
  getTonContractOpener(): ContractOpener;
54
+ private toSequencerTonTransactionHash;
50
55
  }