@tonappchain/sdk 0.5.7 → 0.6.1-spb.0.0

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.
@@ -21,6 +21,8 @@ const Utils_1 = require("./Utils");
21
21
  const artifacts_1 = require("@tonappchain/artifacts");
22
22
  const errors_1 = require("../errors");
23
23
  const contractOpener_1 = require("../adapters/contractOpener");
24
+ const wrappers_1 = require("@tonappchain/artifacts/dist/src/ton/wrappers");
25
+ const instances_1 = require("../errors/instances");
24
26
  class TacSdk {
25
27
  constructor(network, delay, artifacts, TONParams, TACParams, liteSequencerEndpoints) {
26
28
  this.network = network;
@@ -35,7 +37,7 @@ class TacSdk {
35
37
  const delay = sdkParams.delay ?? Consts_1.DEFAULT_DELAY;
36
38
  const artifacts = network === Struct_1.Network.TESTNET ? artifacts_1.testnet : artifacts_1.mainnet;
37
39
  const TONParams = await this.prepareTONParams(network, delay, artifacts, sdkParams.TONParams);
38
- const TACParams = await this.prepareTACParams(artifacts, sdkParams.TACParams);
40
+ const TACParams = await this.prepareTACParams(artifacts, delay, sdkParams.TACParams);
39
41
  const liteSequencerEndpoints = sdkParams.customLiteSequencerEndpoints ??
40
42
  (network === Struct_1.Network.TESTNET
41
43
  ? artifacts_1.testnet.PUBLIC_LITE_SEQUENCER_ENDPOINTS
@@ -50,9 +52,15 @@ class TacSdk {
50
52
  await (0, Utils_1.sleep)(delay * 1000);
51
53
  const crossChainLayerAddress = await settings.getAddressSetting('CrossChainLayerAddress');
52
54
  await (0, Utils_1.sleep)(delay * 1000);
53
- const jettonMinterCode = await settings.getCellSetting('JETTON_MINTER_CODE');
55
+ const jettonMinterCode = await settings.getCellSetting('JettonMinterCode');
54
56
  await (0, Utils_1.sleep)(delay * 1000);
55
- const jettonWalletCode = await settings.getCellSetting('JETTON_WALLET_CODE');
57
+ const jettonWalletCode = await settings.getCellSetting('JettonWalletCode');
58
+ await (0, Utils_1.sleep)(delay * 1000);
59
+ const nftProxyAddress = await settings.getAddressSetting('NFTProxyAddress');
60
+ await (0, Utils_1.sleep)(delay * 1000);
61
+ const nftItemCode = await settings.getCellSetting('NFTItemCode');
62
+ await (0, Utils_1.sleep)(delay * 1000);
63
+ const nftCollectionCode = await settings.getCellSetting('NFTCollectionCode');
56
64
  await (0, Utils_1.sleep)(delay * 1000);
57
65
  return {
58
66
  contractOpener,
@@ -60,26 +68,42 @@ class TacSdk {
60
68
  crossChainLayerAddress,
61
69
  jettonMinterCode,
62
70
  jettonWalletCode,
71
+ nftProxyAddress,
72
+ nftItemCode,
73
+ nftCollectionCode,
63
74
  };
64
75
  }
65
- static async prepareTACParams(artifacts, TACParams) {
76
+ static async prepareTACParams(artifacts, delay, TACParams) {
66
77
  const provider = TACParams?.provider ?? ethers_1.ethers.getDefaultProvider(artifacts.TAC_RPC_ENDPOINT);
67
78
  const settingsAddress = TACParams?.settingsAddress?.toString() ?? artifacts.tac.addresses.TAC_SETTINGS_ADDRESS;
68
- const settings = new ethers_1.ethers.Contract(settingsAddress, TACParams?.settingsABI ?? artifacts.tac.compilationArtifacts.Settings.abi, provider);
79
+ const settings = artifacts.tac.wrappers.SettingsFactoryTAC.connect(settingsAddress, provider);
69
80
  const crossChainLayerABI = TACParams?.crossChainLayerABI ?? artifacts.tac.compilationArtifacts.CrossChainLayer.abi;
70
81
  const crossChainLayerAddress = await settings.getAddressSetting((0, ethers_1.keccak256)((0, ethers_1.toUtf8Bytes)('CrossChainLayerAddress')));
82
+ const crossChainLayer = artifacts.tac.wrappers.CrossChainLayerFactoryTAC.connect(crossChainLayerAddress, provider);
83
+ await (0, Utils_1.sleep)(delay * 1000);
71
84
  const tokenUtilsAddress = await settings.getAddressSetting((0, ethers_1.keccak256)((0, ethers_1.toUtf8Bytes)('TokenUtilsAddress')));
85
+ const tokenUtils = artifacts.tac.wrappers.TokenUtilsFactoryTAC.connect(tokenUtilsAddress, provider);
86
+ await (0, Utils_1.sleep)(delay * 1000);
87
+ const trustedTACExecutors = await settings.getTrustedEVMExecutors();
88
+ await (0, Utils_1.sleep)(delay * 1000);
89
+ const trustedTONExecutors = await settings.getTrustedTVMExecutors();
72
90
  const crossChainLayerTokenABI = TACParams?.crossChainLayerTokenABI ?? artifacts.tac.compilationArtifacts.CrossChainLayerToken.abi;
73
91
  const crossChainLayerTokenBytecode = TACParams?.crossChainLayerTokenBytecode ?? artifacts.tac.compilationArtifacts.CrossChainLayerToken.bytecode;
92
+ const crossChainLayerNFTABI = TACParams?.crossChainLayerNFTABI ?? artifacts.tac.compilationArtifacts.CrossChainLayerNFT.abi;
93
+ const crossChainLayerNFTBytecode = TACParams?.crossChainLayerNFTBytecode ?? artifacts.tac.compilationArtifacts.CrossChainLayerNFT.bytecode;
74
94
  return {
75
95
  provider,
76
- settingsAddress,
77
- tokenUtilsAddress,
96
+ settings,
97
+ tokenUtils,
98
+ crossChainLayer,
99
+ trustedTACExecutors,
100
+ trustedTONExecutors,
78
101
  abiCoder: new ethers_1.ethers.AbiCoder(),
79
102
  crossChainLayerABI,
80
- crossChainLayerAddress,
81
103
  crossChainLayerTokenABI,
82
104
  crossChainLayerTokenBytecode,
105
+ crossChainLayerNFTABI,
106
+ crossChainLayerNFTBytecode,
83
107
  };
84
108
  }
85
109
  closeConnections() {
@@ -88,9 +112,8 @@ class TacSdk {
88
112
  get nativeTONAddress() {
89
113
  return 'NONE';
90
114
  }
91
- get nativeTACAddress() {
92
- const crossChainLayer = new ethers_1.ethers.Contract(this.TACParams.crossChainLayerAddress, this.TACParams.crossChainLayerABI, this.TACParams.provider);
93
- return crossChainLayer.NATIVE_TOKEN_ADDRESS.staticCall();
115
+ async nativeTACAddress() {
116
+ return this.TACParams.crossChainLayer.NATIVE_TOKEN_ADDRESS.staticCall();
94
117
  }
95
118
  async getUserJettonWalletAddress(userAddress, tokenAddress) {
96
119
  const jettonMaster = this.TONParams.contractOpener.open(new JettonMaster_1.JettonMaster(ton_1.Address.parse(tokenAddress)));
@@ -124,21 +147,52 @@ class TacSdk {
124
147
  exists: true,
125
148
  };
126
149
  }
127
- getJettonTransferPayload(jettonData, responseAddress, evmData, crossChainTonAmount) {
150
+ getJettonTransferPayload(jettonData, responseAddress, evmData, crossChainTonAmount, forwardFeeAmount, feeData) {
151
+ const queryId = (0, Utils_1.generateRandomNumberByTimestamp)().randomNumber;
152
+ return JettonWallet_1.JettonWallet.transferMessage(jettonData.rawAmount, this.TONParams.jettonProxyAddress, responseAddress, Consts_1.JETTON_TRANSFER_FORWARD_TON_AMOUNT + forwardFeeAmount + crossChainTonAmount, crossChainTonAmount, feeData, evmData, queryId);
153
+ }
154
+ getJettonBurnPayload(jettonData, evmData, crossChainTonAmount, feeData) {
128
155
  const queryId = (0, Utils_1.generateRandomNumberByTimestamp)().randomNumber;
129
- return JettonWallet_1.JettonWallet.transferMessage(jettonData.rawAmount, this.TONParams.jettonProxyAddress, responseAddress, Consts_1.JETTON_TRANSFER_FORWARD_TON_AMOUNT, crossChainTonAmount, evmData, queryId);
156
+ return JettonWallet_1.JettonWallet.burnMessage(jettonData.rawAmount, jettonData.notificationReceiverAddress, crossChainTonAmount, feeData, evmData, queryId);
130
157
  }
131
- getJettonBurnPayload(jettonData, evmData, crossChainTonAmount) {
158
+ getNFTBurnPayload(burnData) {
132
159
  const queryId = (0, Utils_1.generateRandomNumberByTimestamp)().randomNumber;
133
- return JettonWallet_1.JettonWallet.burnMessage(jettonData.rawAmount, jettonData.notificationReceiverAddress, crossChainTonAmount, evmData, queryId);
160
+ return wrappers_1.NFTItem.burnMessage(queryId, (0, ton_1.address)(burnData.notificationReceiverAddress), burnData.crossChainTonAmount ?? 0, burnData.evmData, burnData.feeData);
134
161
  }
135
- getTonTransferPayload(responseAddress, evmData, crossChainTonAmount) {
162
+ getNFTTransferPayload(transferData, forwardFeeAmount) {
136
163
  const queryId = (0, Utils_1.generateRandomNumberByTimestamp)().randomNumber;
164
+ const crossChainTonAmount = transferData.crossChainTonAmount ?? 0n;
165
+ const forwardPayload = (0, ton_1.beginCell)()
166
+ .storeCoins(crossChainTonAmount)
167
+ .storeMaybeRef(transferData.feeData)
168
+ .storeMaybeRef(transferData.evmData)
169
+ .endCell();
170
+ return wrappers_1.NFTItem.transferMessage(queryId, (0, ton_1.address)(transferData.to ?? this.TONParams.nftProxyAddress), (0, ton_1.address)(transferData.responseAddress), Number((0, ton_1.fromNano)(Consts_1.NFT_TRANSFER_FORWARD_TON_AMOUNT + forwardFeeAmount + crossChainTonAmount)), forwardPayload);
171
+ }
172
+ generateFeeData(feeParams) {
173
+ if (feeParams) {
174
+ let feeDataBuilder = (0, ton_1.beginCell)()
175
+ .storeBit(feeParams.isRoundTrip)
176
+ .storeCoins(feeParams.protocolFee)
177
+ .storeCoins(feeParams.evmExecutorFee);
178
+ if (feeParams.isRoundTrip) {
179
+ feeDataBuilder.storeCoins(feeParams.tvmExecutorFee);
180
+ }
181
+ return feeDataBuilder.endCell();
182
+ }
183
+ else {
184
+ return undefined;
185
+ }
186
+ }
187
+ getTonTransferPayload(responseAddress, evmData, crossChainTonAmount, feeParams) {
188
+ const queryId = (0, Utils_1.generateRandomNumberByTimestamp)().randomNumber;
189
+ const feeData = this.generateFeeData(feeParams);
137
190
  return (0, ton_1.beginCell)()
138
- .storeUint(this.artifacts.ton.wrappers.CrossChainLayerOpCodes.anyone_l1MsgToL2, 32)
191
+ .storeUint(this.artifacts.ton.wrappers.CrossChainLayerOpCodes.anyone_tvmMsgToEVM, 32)
139
192
  .storeUint(queryId, 64)
140
193
  .storeUint(this.artifacts.ton.wrappers.OperationType.tonTransfer, 32)
141
194
  .storeCoins(crossChainTonAmount)
195
+ .storeMaybeRef(feeData)
142
196
  .storeAddress(ton_1.Address.parse(responseAddress))
143
197
  .storeMaybeRef(evmData)
144
198
  .endCell();
@@ -154,7 +208,7 @@ class TacSdk {
154
208
  return InternalStruct_1.AssetOpType.JETTON_TRANSFER;
155
209
  }
156
210
  const givenMinter = this.TONParams.contractOpener.open(new JettonMaster_1.JettonMaster((0, ton_1.address)(asset.address)));
157
- const l2Address = await givenMinter.getL2Address();
211
+ const evmAddress = await givenMinter.getEVMAddress();
158
212
  await (0, Utils_1.sleep)(this.delay * 1000);
159
213
  const expectedMinterAddress = await (0, Utils_1.calculateContractAddress)(this.TONParams.jettonMinterCode, (0, ton_1.beginCell)()
160
214
  .storeCoins(0)
@@ -162,19 +216,43 @@ class TacSdk {
162
216
  .storeAddress(null)
163
217
  .storeRef((0, ton_1.beginCell)().endCell())
164
218
  .storeRef(this.TONParams.jettonWalletCode)
165
- .storeStringTail(l2Address)
219
+ .storeStringTail(evmAddress)
166
220
  .endCell());
167
221
  if (!expectedMinterAddress.equals(givenMinter.address)) {
168
222
  return InternalStruct_1.AssetOpType.JETTON_TRANSFER;
169
223
  }
170
224
  return InternalStruct_1.AssetOpType.JETTON_BURN;
171
225
  }
172
- async aggregateJettons(assets) {
226
+ async getNFTOpType(asset) {
227
+ const { code: itemCodeBOC } = await this.TONParams.contractOpener.getContractState((0, ton_1.address)(asset.address));
228
+ if (!itemCodeBOC) {
229
+ throw errors_1.emptyContractError;
230
+ }
231
+ const givenNFTItemCode = ton_1.Cell.fromBoc(itemCodeBOC)[0];
232
+ await (0, Utils_1.sleep)(this.delay * 1000);
233
+ if (!this.TONParams.nftItemCode.equals(givenNFTItemCode)) {
234
+ return InternalStruct_1.AssetOpType.NFT_TRANSFER;
235
+ }
236
+ return InternalStruct_1.AssetOpType.NFT_BURN;
237
+ }
238
+ async getNFTItemAddressTON(collectionAddress, itemIndex) {
239
+ (0, Utils_1.validateTVMAddress)(collectionAddress);
240
+ const nftCollection = this.TONParams.contractOpener.open(wrappers_1.NFTCollection.createFromAddress((0, ton_1.address)(collectionAddress)));
241
+ return (await nftCollection.getNFTAddressByIndex(itemIndex)).toString();
242
+ }
243
+ async getNFTItemData(itemAddress) {
244
+ (0, Utils_1.validateTVMAddress)(itemAddress);
245
+ const nftItem = this.TONParams.contractOpener.open(wrappers_1.NFTItem.createFromAddress((0, ton_1.address)(itemAddress)));
246
+ return await nftItem.getNFTData();
247
+ }
248
+ async aggregateTokens(assets) {
173
249
  const uniqueAssetsMap = new Map();
174
250
  let crossChainTonAmount = 0n;
175
251
  for await (const asset of assets ?? []) {
176
252
  if (asset.rawAmount <= 0)
177
253
  continue;
254
+ if (asset.type !== Struct_1.AssetType.FT)
255
+ continue;
178
256
  if (asset.address) {
179
257
  (0, Utils_1.validateTVMAddress)(asset.address);
180
258
  uniqueAssetsMap.set(asset.address, (uniqueAssetsMap.get(asset.address) || 0n) + BigInt(asset.rawAmount));
@@ -186,53 +264,113 @@ class TacSdk {
186
264
  const jettons = Array.from(uniqueAssetsMap.entries()).map(([address, rawAmount]) => ({
187
265
  address,
188
266
  rawAmount,
267
+ type: Struct_1.AssetType.FT,
268
+ }));
269
+ uniqueAssetsMap.clear();
270
+ for await (const asset of assets ?? []) {
271
+ if (asset.type !== Struct_1.AssetType.NFT)
272
+ continue;
273
+ (0, Utils_1.validateTVMAddress)(asset.address);
274
+ uniqueAssetsMap.set(asset.address, 1n);
275
+ }
276
+ const nfts = Array.from(uniqueAssetsMap.entries()).map(([address, rawAmount]) => ({
277
+ address,
278
+ rawAmount,
279
+ type: Struct_1.AssetType.NFT,
189
280
  }));
190
281
  return {
191
282
  jettons,
283
+ nfts,
192
284
  crossChainTonAmount,
193
285
  };
194
286
  }
195
- async generatePayload(jetton, caller, evmData, crossChainTonAmount) {
287
+ async generateJettonPayload(jetton, caller, evmData, crossChainTonAmount, forwardFeeTonAmount, feeParams) {
196
288
  const opType = await this.getJettonOpType(jetton);
197
289
  await (0, Utils_1.sleep)(this.delay * 1000);
198
290
  console.log(`***** Jetton ${jetton.address} requires ${opType} operation`);
291
+ const feeData = this.generateFeeData(feeParams);
199
292
  let payload;
200
293
  switch (opType) {
201
294
  case InternalStruct_1.AssetOpType.JETTON_BURN:
202
295
  payload = this.getJettonBurnPayload({
203
296
  notificationReceiverAddress: this.TONParams.crossChainLayerAddress,
204
297
  ...jetton,
205
- }, evmData, crossChainTonAmount);
298
+ }, evmData, crossChainTonAmount, feeData);
206
299
  break;
207
300
  case InternalStruct_1.AssetOpType.JETTON_TRANSFER:
208
- payload = this.getJettonTransferPayload(jetton, caller, evmData, crossChainTonAmount);
301
+ payload = this.getJettonTransferPayload(jetton, caller, evmData, crossChainTonAmount, forwardFeeTonAmount, feeData);
302
+ break;
303
+ }
304
+ return payload;
305
+ }
306
+ async generateNFTPayload(nft, caller, evmData, crossChainTonAmount, forwardFeeTonAmount, feeParams) {
307
+ const opType = await this.getNFTOpType(nft);
308
+ await (0, Utils_1.sleep)(this.delay * 1000);
309
+ console.log(`***** NFT ${nft.address} requires ${opType} operation`);
310
+ const feeData = this.generateFeeData(feeParams);
311
+ let payload;
312
+ switch (opType) {
313
+ case InternalStruct_1.AssetOpType.NFT_BURN:
314
+ payload = this.getNFTBurnPayload({
315
+ notificationReceiverAddress: this.TONParams.crossChainLayerAddress,
316
+ ...nft,
317
+ evmData,
318
+ crossChainTonAmount,
319
+ feeData,
320
+ });
321
+ break;
322
+ case InternalStruct_1.AssetOpType.NFT_TRANSFER:
323
+ payload = this.getNFTTransferPayload({
324
+ to: this.TONParams.nftProxyAddress,
325
+ responseAddress: caller,
326
+ evmData,
327
+ crossChainTonAmount,
328
+ feeData,
329
+ ...nft,
330
+ }, forwardFeeTonAmount);
209
331
  break;
210
332
  }
211
333
  return payload;
212
334
  }
213
- async generateCrossChainMessages(caller, evmData, aggregatedData) {
335
+ async generateCrossChainMessages(caller, evmData, aggregatedData, feeParams) {
214
336
  let crossChainTonAmount = aggregatedData.crossChainTonAmount;
215
- if (aggregatedData.jettons.length == 0) {
337
+ let feeTonAmount = feeParams.protocolFee + feeParams.evmExecutorFee + feeParams.tvmExecutorFee;
338
+ if (aggregatedData.jettons.length == 0 && aggregatedData.nfts.length == 0) {
216
339
  return [
217
340
  {
218
341
  address: this.TONParams.crossChainLayerAddress,
219
- value: crossChainTonAmount + Consts_1.TRANSACTION_TON_AMOUNT,
220
- payload: this.getTonTransferPayload(caller, evmData, crossChainTonAmount),
342
+ value: crossChainTonAmount + feeTonAmount + Consts_1.TRANSACTION_TON_AMOUNT,
343
+ payload: this.getTonTransferPayload(caller, evmData, crossChainTonAmount, feeParams),
221
344
  },
222
345
  ];
223
346
  }
224
347
  const messages = [];
348
+ let currentFeeParams = feeParams;
225
349
  for (const jetton of aggregatedData.jettons) {
226
- const payload = await this.generatePayload(jetton, caller, evmData, crossChainTonAmount);
350
+ const payload = await this.generateJettonPayload(jetton, caller, evmData, crossChainTonAmount, feeTonAmount, currentFeeParams);
227
351
  await (0, Utils_1.sleep)(this.delay * 1000);
228
352
  const jettonWalletAddress = await this.getUserJettonWalletAddress(caller, jetton.address);
229
353
  await (0, Utils_1.sleep)(this.delay * 1000);
230
354
  messages.push({
231
355
  address: jettonWalletAddress,
232
- value: crossChainTonAmount + Consts_1.TRANSACTION_TON_AMOUNT,
356
+ value: crossChainTonAmount + feeTonAmount + Consts_1.TRANSACTION_TON_AMOUNT,
357
+ payload,
358
+ });
359
+ crossChainTonAmount = 0n;
360
+ feeTonAmount = 0n;
361
+ currentFeeParams = undefined;
362
+ }
363
+ for (const nft of aggregatedData.nfts) {
364
+ const payload = await this.generateNFTPayload(nft, caller, evmData, crossChainTonAmount, feeTonAmount, currentFeeParams);
365
+ await (0, Utils_1.sleep)(this.delay * 1000);
366
+ messages.push({
367
+ address: nft.address,
368
+ value: crossChainTonAmount + feeTonAmount + Consts_1.TRANSACTION_TON_AMOUNT,
233
369
  payload,
234
370
  });
235
371
  crossChainTonAmount = 0n;
372
+ feeTonAmount = 0n;
373
+ currentFeeParams = undefined;
236
374
  }
237
375
  return messages;
238
376
  }
@@ -261,75 +399,243 @@ class TacSdk {
261
399
  }
262
400
  async convertAssetsToRawFormat(assets) {
263
401
  return await Promise.all((assets ?? []).map(async (asset) => {
264
- const address = (0, ethers_1.isAddress)(asset.address)
265
- ? await this.getTVMTokenAddress(asset.address)
266
- : asset.address;
267
- return {
268
- address,
269
- rawAmount: await this.getRawAmount(asset, address),
270
- };
402
+ if (asset.type === Struct_1.AssetType.FT) {
403
+ const address = (0, ethers_1.isAddress)(asset.address)
404
+ ? await this.getTVMTokenAddress(asset.address)
405
+ : asset.address;
406
+ return {
407
+ address,
408
+ rawAmount: await this.getRawAmount(asset, address),
409
+ type: asset.type,
410
+ };
411
+ }
412
+ if (asset.type === Struct_1.AssetType.NFT) {
413
+ if ('collectionAddress' in asset) {
414
+ const address = (0, ethers_1.isAddress)(asset.collectionAddress)
415
+ ? await this.getTVMNFTAddress(asset.collectionAddress, asset.itemIndex)
416
+ : await this.getNFTItemAddressTON(asset.collectionAddress, asset.itemIndex);
417
+ return {
418
+ address,
419
+ rawAmount: 1n,
420
+ type: asset.type,
421
+ };
422
+ }
423
+ (0, Utils_1.validateTVMAddress)(asset.address);
424
+ return {
425
+ address: asset.address,
426
+ rawAmount: 1n,
427
+ type: asset.type,
428
+ };
429
+ }
430
+ throw instances_1.invalidAssetType;
271
431
  }));
272
432
  }
273
- async getGasLimit(evmProxyMsg, transactionLinker, rawAssets, forceSend = false) {
433
+ async getFeeInfo(evmProxyMsg, transactionLinker, rawAssets, evmValidExecutors, forceSend = false, isRoundTrip) {
434
+ const crossChainLayer = this.TONParams.contractOpener.open(this.artifacts.ton.wrappers.CrossChainLayer.createFromAddress(ton_1.Address.parse(this.TONParams.crossChainLayerAddress)));
435
+ const fullStateCCL = await crossChainLayer.getFullData();
274
436
  const tacSimulationBody = {
275
437
  tacCallParams: {
276
438
  arguments: evmProxyMsg.encodedParameters ?? '0x',
277
439
  methodName: (0, Utils_1.formatSolidityMethodName)(evmProxyMsg.methodName),
278
440
  target: evmProxyMsg.evmTargetAddress,
279
441
  },
442
+ evmValidExecutors: evmValidExecutors,
280
443
  extraData: '0x',
281
444
  feeAssetAddress: '',
282
445
  shardsKey: transactionLinker.shardsKey,
283
446
  tonAssets: rawAssets.map((asset) => ({
284
447
  amount: asset.rawAmount.toString(),
285
448
  tokenAddress: asset.address || '',
449
+ assetType: asset.type,
286
450
  })),
287
451
  tonCaller: transactionLinker.caller,
288
452
  };
289
453
  const tacSimulationResult = await this.simulateTACMessage(tacSimulationBody);
290
454
  if (!tacSimulationResult.simulationStatus) {
291
455
  if (forceSend) {
292
- return 0n;
456
+ return {
457
+ feeParams: {
458
+ isRoundTrip: isRoundTrip ?? false,
459
+ gasLimit: 0n,
460
+ protocolFee: BigInt((0, ton_1.toNano)(fullStateCCL.tacProtocolFee)) +
461
+ BigInt(isRoundTrip ?? false) * BigInt((0, ton_1.toNano)(fullStateCCL.tonProtocolFee)),
462
+ evmExecutorFee: 0n,
463
+ tvmExecutorFee: 0n,
464
+ },
465
+ simulation: tacSimulationResult,
466
+ };
293
467
  }
294
468
  throw tacSimulationResult;
295
469
  }
296
- return (BigInt(tacSimulationResult.estimatedGas) * 120n) / 100n;
470
+ isRoundTrip = isRoundTrip ?? tacSimulationResult.outMessages != null;
471
+ let tonExecutorFeeInTON = 0n;
472
+ if (isRoundTrip) {
473
+ tonExecutorFeeInTON = BigInt(tacSimulationResult.suggestedTonExecutionFee);
474
+ }
475
+ const protocolFee = BigInt((0, ton_1.toNano)(fullStateCCL.tacProtocolFee)) +
476
+ BigInt(isRoundTrip) * BigInt((0, ton_1.toNano)(fullStateCCL.tonProtocolFee));
477
+ const feeParams = {
478
+ isRoundTrip: isRoundTrip,
479
+ gasLimit: tacSimulationResult.estimatedGas,
480
+ protocolFee: protocolFee,
481
+ evmExecutorFee: BigInt(tacSimulationResult.suggestedTacExecutionFee),
482
+ tvmExecutorFee: tonExecutorFeeInTON,
483
+ };
484
+ return { feeParams: feeParams, simulation: tacSimulationResult };
297
485
  }
298
- async sendCrossChainTransaction(evmProxyMsg, sender, assets, forceSend = false) {
486
+ async getTransactionSimulationInfo(evmProxyMsg, sender, assets) {
299
487
  const rawAssets = await this.convertAssetsToRawFormat(assets);
300
- const aggregatedData = await this.aggregateJettons(rawAssets);
488
+ const aggregatedData = await this.aggregateTokens(rawAssets);
301
489
  const transactionLinkerShardCount = aggregatedData.jettons.length == 0 ? 1 : aggregatedData.jettons.length;
302
- const caller = sender.getSenderAddress();
490
+ const transactionLinker = (0, Utils_1.generateTransactionLinker)(sender.getSenderAddress(), transactionLinkerShardCount);
491
+ const evmValidExecutors = this.TACParams.trustedTACExecutors;
492
+ return await this.getFeeInfo(evmProxyMsg, transactionLinker, rawAssets, evmValidExecutors, false, undefined);
493
+ }
494
+ async prepareCrossChainTransaction(evmProxyMsg, caller, assets, options) {
495
+ let { forceSend = false, isRoundTrip = undefined, protocolFee = undefined, evmValidExecutors = [], evmExecutorFee = undefined, tvmValidExecutors = [], tvmExecutorFee = undefined, } = options || {};
496
+ const rawAssets = await this.convertAssetsToRawFormat(assets);
497
+ const aggregatedData = await this.aggregateTokens(rawAssets);
498
+ const tokensLength = aggregatedData.jettons.length + aggregatedData.nfts.length;
499
+ let transactionLinkerShardCount = tokensLength == 0 ? 1 : tokensLength;
303
500
  const transactionLinker = (0, Utils_1.generateTransactionLinker)(caller, transactionLinkerShardCount);
304
- const gasLimit = await this.getGasLimit(evmProxyMsg, transactionLinker, rawAssets, forceSend);
305
- if (evmProxyMsg.gasLimit == 0n || evmProxyMsg.gasLimit == undefined) {
306
- evmProxyMsg.gasLimit = gasLimit;
501
+ if (evmValidExecutors.length == 0) {
502
+ evmValidExecutors = this.TACParams.trustedTACExecutors;
503
+ }
504
+ if (tvmValidExecutors.length == 0) {
505
+ tvmValidExecutors = this.TACParams.trustedTONExecutors;
506
+ }
507
+ const { feeParams } = await this.getFeeInfo(evmProxyMsg, transactionLinker, rawAssets, evmValidExecutors, forceSend, isRoundTrip);
508
+ if (evmProxyMsg.gasLimit == undefined) {
509
+ evmProxyMsg.gasLimit = feeParams.gasLimit;
307
510
  }
308
- const evmData = (0, Utils_1.buildEvmDataCell)(transactionLinker, evmProxyMsg);
309
- const messages = await this.generateCrossChainMessages(caller, evmData, aggregatedData);
511
+ if (evmExecutorFee != undefined) {
512
+ feeParams.evmExecutorFee = evmExecutorFee;
513
+ }
514
+ if (feeParams.isRoundTrip && tvmExecutorFee != undefined) {
515
+ feeParams.tvmExecutorFee = tvmExecutorFee;
516
+ }
517
+ if (protocolFee != undefined) {
518
+ feeParams.protocolFee = protocolFee;
519
+ }
520
+ const validExecutors = {
521
+ tac: evmValidExecutors,
522
+ ton: tvmValidExecutors,
523
+ };
524
+ const evmData = (0, Utils_1.buildEvmDataCell)(transactionLinker, evmProxyMsg, validExecutors);
525
+ const messages = await this.generateCrossChainMessages(caller, evmData, aggregatedData, feeParams);
310
526
  await (0, Utils_1.sleep)(this.delay * 1000);
311
527
  const transaction = {
312
528
  validUntil: +new Date() + 15 * 60 * 1000,
313
529
  messages,
314
530
  network: this.network,
315
531
  };
532
+ return { transaction, transactionLinker };
533
+ }
534
+ async sendCrossChainTransaction(evmProxyMsg, sender, assets, options) {
535
+ const caller = sender.getSenderAddress();
536
+ const { transaction, transactionLinker } = await this.prepareCrossChainTransaction(evmProxyMsg, caller, assets, options);
316
537
  console.log('*****Sending transaction: ', transaction);
317
538
  const sendTransactionResult = await sender.sendShardTransaction(transaction, this.delay, this.network, this.TONParams.contractOpener);
318
539
  return { sendTransactionResult, ...transactionLinker };
319
540
  }
541
+ async sendCrossChainTransactions(sender, txs) {
542
+ const transactions = [];
543
+ const transactionLinkers = [];
544
+ const caller = sender.getSenderAddress();
545
+ for (const { options, assets, evmProxyMsg } of txs) {
546
+ const { transaction, transactionLinker } = await this.prepareCrossChainTransaction(evmProxyMsg, caller, assets, options);
547
+ transactions.push(transaction);
548
+ transactionLinkers.push(transactionLinker);
549
+ }
550
+ console.log('*****Sending transactions: ', transactions);
551
+ await sender.sendShardTransactions(transactions, this.delay, this.network, this.TONParams.contractOpener);
552
+ return transactionLinkers;
553
+ }
554
+ // TODO move to sdk.TAC, sdk.TON
555
+ async bridgeTokensToTON(signer, value, tonTarget, assets, tvmExecutorFee) {
556
+ if (assets == undefined) {
557
+ assets = [];
558
+ }
559
+ const crossChainLayerAddress = await this.TACParams.crossChainLayer.getAddress();
560
+ for (const asset of assets) {
561
+ if (asset.type == Struct_1.AssetType.FT) {
562
+ const tokenContract = this.artifacts.tac.wrappers.ERC20FactoryTAC.connect(asset.address, this.TACParams.provider);
563
+ const tx = await tokenContract.connect(signer).approve(crossChainLayerAddress, asset.rawAmount);
564
+ await tx.wait();
565
+ }
566
+ if (asset.type == Struct_1.AssetType.NFT) {
567
+ const tokenContract = this.artifacts.tac.wrappers.ERC721FactoryTAC.connect(asset.collectionAddress, this.TACParams.provider);
568
+ const tx = await tokenContract.connect(signer).approve(crossChainLayerAddress, asset.itemIndex);
569
+ await tx.wait();
570
+ }
571
+ }
572
+ const shardsKey = BigInt(Math.round(Math.random() * 1e18));
573
+ const protocolFee = await this.TACParams.crossChainLayer.getProtocolFee();
574
+ let tvmExecutorFeeInTON = 0n;
575
+ if (tvmExecutorFee != undefined) {
576
+ tvmExecutorFeeInTON = tvmExecutorFee;
577
+ }
578
+ else {
579
+ tvmExecutorFeeInTON =
580
+ ((((0, ton_1.toNano)('0.065') + (0, ton_1.toNano)('0.05')) * BigInt(assets.length + 1 + Number(value != 0n)) +
581
+ (0, ton_1.toNano)('0.2')) *
582
+ 120n) /
583
+ 100n; // TODO calc that
584
+ }
585
+ const tonToTacRate = 100n;
586
+ const scale = 10n ** 9n;
587
+ const tonToTacRateScaled = tonToTacRate * scale;
588
+ const tvmExecutorFeeInTAC = tonToTacRateScaled * tvmExecutorFeeInTON;
589
+ const outMessage = {
590
+ shardsKey: shardsKey,
591
+ tvmTarget: tonTarget,
592
+ tvmPayload: '',
593
+ tvmProtocolFee: protocolFee,
594
+ tvmExecutorFee: tvmExecutorFeeInTAC,
595
+ tvmValidExecutors: this.TACParams.trustedTONExecutors,
596
+ toBridge: assets
597
+ .filter((asset) => asset.type === Struct_1.AssetType.FT)
598
+ .map((asset) => ({
599
+ evmAddress: asset.address,
600
+ amount: asset.rawAmount,
601
+ })),
602
+ toBridgeNFT: assets
603
+ .filter((asset) => asset.type === Struct_1.AssetType.NFT)
604
+ .map((asset) => ({
605
+ evmAddress: asset.collectionAddress,
606
+ amount: 1n,
607
+ tokenId: asset.itemIndex,
608
+ })),
609
+ };
610
+ const encodedOutMessage = this.artifacts.tac.utils.encodeOutMessageV1(outMessage);
611
+ const outMsgVersion = 1n;
612
+ const totalValue = value + BigInt(outMessage.tvmProtocolFee) + BigInt(outMessage.tvmExecutorFee);
613
+ const tx = await this.TACParams.crossChainLayer
614
+ .connect(signer)
615
+ .sendMessage(outMsgVersion, encodedOutMessage, { value: totalValue });
616
+ await tx.wait();
617
+ return tx.hash;
618
+ }
619
+ get getTrustedTACExecutors() {
620
+ return this.TACParams.trustedTACExecutors;
621
+ }
622
+ get getTrustedTONExecutors() {
623
+ return this.TACParams.trustedTONExecutors;
624
+ }
320
625
  async getEVMTokenAddress(tvmTokenAddress) {
321
626
  if (tvmTokenAddress !== this.nativeTONAddress) {
322
627
  (0, Utils_1.validateTVMAddress)(tvmTokenAddress);
628
+ tvmTokenAddress = ton_1.Address.parse(tvmTokenAddress).toString({ bounceable: true });
323
629
  const { code: givenMinterCodeBOC } = await this.TONParams.contractOpener.getContractState((0, ton_1.address)(tvmTokenAddress));
324
630
  await (0, Utils_1.sleep)(this.delay * 1000);
325
631
  if (givenMinterCodeBOC && this.TONParams.jettonMinterCode.equals(ton_1.Cell.fromBoc(givenMinterCodeBOC)[0])) {
326
632
  const givenMinter = this.TONParams.contractOpener.open(new JettonMaster_1.JettonMaster((0, ton_1.address)(tvmTokenAddress)));
327
- const l2Address = await givenMinter.getL2Address();
633
+ const evmAddress = await givenMinter.getEVMAddress();
328
634
  await (0, Utils_1.sleep)(this.delay * 1000);
329
- return l2Address;
635
+ return evmAddress;
330
636
  }
331
637
  }
332
- return (0, Utils_1.calculateEVMTokenAddress)(this.TACParams.abiCoder, this.TACParams.tokenUtilsAddress, this.TACParams.crossChainLayerTokenBytecode, this.TACParams.crossChainLayerAddress, tvmTokenAddress);
638
+ return this.TACParams.tokenUtils.computeAddress(tvmTokenAddress);
333
639
  }
334
640
  async getTVMTokenAddress(evmTokenAddress) {
335
641
  (0, Utils_1.validateEVMAddress)(evmTokenAddress);
@@ -337,7 +643,7 @@ class TacSdk {
337
643
  if (bytecode.includes(ethers_1.ethers.id('getInfo()').slice(2, 10))) {
338
644
  const contract = new ethers_1.ethers.Contract(evmTokenAddress, this.TACParams.crossChainLayerTokenABI, this.TACParams.provider);
339
645
  const info = await contract.getInfo.staticCall();
340
- return info.l1Address;
646
+ return info.tvmAddress;
341
647
  }
342
648
  const jettonMaster = JettonMaster_1.JettonMaster.createFromConfig({
343
649
  evmTokenAddress,
@@ -347,6 +653,48 @@ class TacSdk {
347
653
  });
348
654
  return jettonMaster.address.toString();
349
655
  }
656
+ async getTVMNFTAddress(evmNFTAddress, tokenId) {
657
+ (0, Utils_1.validateEVMAddress)(evmNFTAddress);
658
+ let nftCollection;
659
+ const bytecode = await this.TACParams.provider.getCode(evmNFTAddress);
660
+ if (bytecode.includes(ethers_1.ethers.id('getInfo()').slice(2, 10))) {
661
+ const contract = new ethers_1.ethers.Contract(evmNFTAddress, this.TACParams.crossChainLayerNFTABI, this.TACParams.provider);
662
+ const info = await contract.getInfo.staticCall();
663
+ nftCollection = this.TONParams.contractOpener.open(wrappers_1.NFTCollection.createFromAddress(info.tvmAddress));
664
+ }
665
+ else {
666
+ nftCollection = this.TONParams.contractOpener.open(wrappers_1.NFTCollection.createFromConfig({
667
+ ownerAddress: (0, ton_1.address)(this.TONParams.crossChainLayerAddress),
668
+ content: (0, ton_1.beginCell)().endCell(),
669
+ nftItemCode: this.TONParams.nftItemCode,
670
+ originalAddress: evmNFTAddress,
671
+ }, this.TONParams.nftCollectionCode));
672
+ }
673
+ return tokenId == undefined
674
+ ? nftCollection.address.toString()
675
+ : (await nftCollection.getNFTAddressByIndex(tokenId)).toString();
676
+ }
677
+ async getEVMNFTAddress(tvmNFTAddress, addressType) {
678
+ (0, Utils_1.validateTVMAddress)(tvmNFTAddress);
679
+ tvmNFTAddress = ton_1.Address.parse(tvmNFTAddress).toString({ bounceable: true });
680
+ if (addressType == Struct_1.NFTAddressType.ITEM) {
681
+ tvmNFTAddress = (await this.getNFTItemData(tvmNFTAddress)).collectionAddress.toString();
682
+ addressType = Struct_1.NFTAddressType.COLLECTION;
683
+ await (0, Utils_1.sleep)(this.delay * 1000);
684
+ }
685
+ const { code: givenNFTCollection } = await this.TONParams.contractOpener.getContractState((0, ton_1.address)(tvmNFTAddress));
686
+ await (0, Utils_1.sleep)(this.delay * 1000);
687
+ if (givenNFTCollection && this.TONParams.nftCollectionCode.equals(ton_1.Cell.fromBoc(givenNFTCollection)[0])) {
688
+ const nftCollection = this.TONParams.contractOpener.open(wrappers_1.NFTCollection.createFromAddress((0, ton_1.address)(tvmNFTAddress)));
689
+ const evmAddress = await nftCollection.getOriginalAddress();
690
+ await (0, Utils_1.sleep)(this.delay * 1000);
691
+ return evmAddress.toString();
692
+ }
693
+ return this.TACParams.tokenUtils.computeAddress(tvmNFTAddress);
694
+ }
695
+ async isContractDeployedOnTVM(address) {
696
+ return (await this.TONParams.contractOpener.getContractState(ton_1.Address.parse(address))).state === 'active';
697
+ }
350
698
  async simulateTACMessage(req) {
351
699
  for (const endpoint of this.liteSequencerEndpoints) {
352
700
  try {