@tonappchain/sdk 0.5.6 → 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,26 +208,51 @@ 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)
161
215
  .storeAddress((0, ton_1.address)(this.TONParams.crossChainLayerAddress))
216
+ .storeAddress(null)
162
217
  .storeRef((0, ton_1.beginCell)().endCell())
163
218
  .storeRef(this.TONParams.jettonWalletCode)
164
- .storeStringTail(l2Address)
219
+ .storeStringTail(evmAddress)
165
220
  .endCell());
166
221
  if (!expectedMinterAddress.equals(givenMinter.address)) {
167
222
  return InternalStruct_1.AssetOpType.JETTON_TRANSFER;
168
223
  }
169
224
  return InternalStruct_1.AssetOpType.JETTON_BURN;
170
225
  }
171
- 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) {
172
249
  const uniqueAssetsMap = new Map();
173
250
  let crossChainTonAmount = 0n;
174
251
  for await (const asset of assets ?? []) {
175
252
  if (asset.rawAmount <= 0)
176
253
  continue;
254
+ if (asset.type !== Struct_1.AssetType.FT)
255
+ continue;
177
256
  if (asset.address) {
178
257
  (0, Utils_1.validateTVMAddress)(asset.address);
179
258
  uniqueAssetsMap.set(asset.address, (uniqueAssetsMap.get(asset.address) || 0n) + BigInt(asset.rawAmount));
@@ -185,53 +264,113 @@ class TacSdk {
185
264
  const jettons = Array.from(uniqueAssetsMap.entries()).map(([address, rawAmount]) => ({
186
265
  address,
187
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,
188
280
  }));
189
281
  return {
190
282
  jettons,
283
+ nfts,
191
284
  crossChainTonAmount,
192
285
  };
193
286
  }
194
- async generatePayload(jetton, caller, evmData, crossChainTonAmount) {
287
+ async generateJettonPayload(jetton, caller, evmData, crossChainTonAmount, forwardFeeTonAmount, feeParams) {
195
288
  const opType = await this.getJettonOpType(jetton);
196
289
  await (0, Utils_1.sleep)(this.delay * 1000);
197
290
  console.log(`***** Jetton ${jetton.address} requires ${opType} operation`);
291
+ const feeData = this.generateFeeData(feeParams);
198
292
  let payload;
199
293
  switch (opType) {
200
294
  case InternalStruct_1.AssetOpType.JETTON_BURN:
201
295
  payload = this.getJettonBurnPayload({
202
296
  notificationReceiverAddress: this.TONParams.crossChainLayerAddress,
203
297
  ...jetton,
204
- }, evmData, crossChainTonAmount);
298
+ }, evmData, crossChainTonAmount, feeData);
205
299
  break;
206
300
  case InternalStruct_1.AssetOpType.JETTON_TRANSFER:
207
- 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);
208
331
  break;
209
332
  }
210
333
  return payload;
211
334
  }
212
- async generateCrossChainMessages(caller, evmData, aggregatedData) {
335
+ async generateCrossChainMessages(caller, evmData, aggregatedData, feeParams) {
213
336
  let crossChainTonAmount = aggregatedData.crossChainTonAmount;
214
- if (aggregatedData.jettons.length == 0) {
337
+ let feeTonAmount = feeParams.protocolFee + feeParams.evmExecutorFee + feeParams.tvmExecutorFee;
338
+ if (aggregatedData.jettons.length == 0 && aggregatedData.nfts.length == 0) {
215
339
  return [
216
340
  {
217
341
  address: this.TONParams.crossChainLayerAddress,
218
- value: crossChainTonAmount + Consts_1.TRANSACTION_TON_AMOUNT,
219
- payload: this.getTonTransferPayload(caller, evmData, crossChainTonAmount),
342
+ value: crossChainTonAmount + feeTonAmount + Consts_1.TRANSACTION_TON_AMOUNT,
343
+ payload: this.getTonTransferPayload(caller, evmData, crossChainTonAmount, feeParams),
220
344
  },
221
345
  ];
222
346
  }
223
347
  const messages = [];
348
+ let currentFeeParams = feeParams;
224
349
  for (const jetton of aggregatedData.jettons) {
225
- const payload = await this.generatePayload(jetton, caller, evmData, crossChainTonAmount);
350
+ const payload = await this.generateJettonPayload(jetton, caller, evmData, crossChainTonAmount, feeTonAmount, currentFeeParams);
226
351
  await (0, Utils_1.sleep)(this.delay * 1000);
227
352
  const jettonWalletAddress = await this.getUserJettonWalletAddress(caller, jetton.address);
228
353
  await (0, Utils_1.sleep)(this.delay * 1000);
229
354
  messages.push({
230
355
  address: jettonWalletAddress,
231
- 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,
232
369
  payload,
233
370
  });
234
371
  crossChainTonAmount = 0n;
372
+ feeTonAmount = 0n;
373
+ currentFeeParams = undefined;
235
374
  }
236
375
  return messages;
237
376
  }
@@ -260,75 +399,243 @@ class TacSdk {
260
399
  }
261
400
  async convertAssetsToRawFormat(assets) {
262
401
  return await Promise.all((assets ?? []).map(async (asset) => {
263
- const address = (0, ethers_1.isAddress)(asset.address)
264
- ? await this.getTVMTokenAddress(asset.address)
265
- : asset.address;
266
- return {
267
- address,
268
- rawAmount: await this.getRawAmount(asset, address),
269
- };
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;
270
431
  }));
271
432
  }
272
- 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();
273
436
  const tacSimulationBody = {
274
437
  tacCallParams: {
275
438
  arguments: evmProxyMsg.encodedParameters ?? '0x',
276
439
  methodName: (0, Utils_1.formatSolidityMethodName)(evmProxyMsg.methodName),
277
440
  target: evmProxyMsg.evmTargetAddress,
278
441
  },
442
+ evmValidExecutors: evmValidExecutors,
279
443
  extraData: '0x',
280
444
  feeAssetAddress: '',
281
445
  shardsKey: transactionLinker.shardsKey,
282
446
  tonAssets: rawAssets.map((asset) => ({
283
447
  amount: asset.rawAmount.toString(),
284
448
  tokenAddress: asset.address || '',
449
+ assetType: asset.type,
285
450
  })),
286
451
  tonCaller: transactionLinker.caller,
287
452
  };
288
453
  const tacSimulationResult = await this.simulateTACMessage(tacSimulationBody);
289
454
  if (!tacSimulationResult.simulationStatus) {
290
455
  if (forceSend) {
291
- 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
+ };
292
467
  }
293
468
  throw tacSimulationResult;
294
469
  }
295
- 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 };
296
485
  }
297
- async sendCrossChainTransaction(evmProxyMsg, sender, assets, forceSend = false) {
486
+ async getTransactionSimulationInfo(evmProxyMsg, sender, assets) {
298
487
  const rawAssets = await this.convertAssetsToRawFormat(assets);
299
- const aggregatedData = await this.aggregateJettons(rawAssets);
488
+ const aggregatedData = await this.aggregateTokens(rawAssets);
300
489
  const transactionLinkerShardCount = aggregatedData.jettons.length == 0 ? 1 : aggregatedData.jettons.length;
301
- 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;
302
500
  const transactionLinker = (0, Utils_1.generateTransactionLinker)(caller, transactionLinkerShardCount);
303
- const gasLimit = await this.getGasLimit(evmProxyMsg, transactionLinker, rawAssets, forceSend);
304
- if (evmProxyMsg.gasLimit == 0n || evmProxyMsg.gasLimit == undefined) {
305
- 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;
306
510
  }
307
- const evmData = (0, Utils_1.buildEvmDataCell)(transactionLinker, evmProxyMsg);
308
- 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);
309
526
  await (0, Utils_1.sleep)(this.delay * 1000);
310
527
  const transaction = {
311
528
  validUntil: +new Date() + 15 * 60 * 1000,
312
529
  messages,
313
530
  network: this.network,
314
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);
315
537
  console.log('*****Sending transaction: ', transaction);
316
538
  const sendTransactionResult = await sender.sendShardTransaction(transaction, this.delay, this.network, this.TONParams.contractOpener);
317
539
  return { sendTransactionResult, ...transactionLinker };
318
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
+ }
319
625
  async getEVMTokenAddress(tvmTokenAddress) {
320
626
  if (tvmTokenAddress !== this.nativeTONAddress) {
321
627
  (0, Utils_1.validateTVMAddress)(tvmTokenAddress);
628
+ tvmTokenAddress = ton_1.Address.parse(tvmTokenAddress).toString({ bounceable: true });
322
629
  const { code: givenMinterCodeBOC } = await this.TONParams.contractOpener.getContractState((0, ton_1.address)(tvmTokenAddress));
323
630
  await (0, Utils_1.sleep)(this.delay * 1000);
324
631
  if (givenMinterCodeBOC && this.TONParams.jettonMinterCode.equals(ton_1.Cell.fromBoc(givenMinterCodeBOC)[0])) {
325
632
  const givenMinter = this.TONParams.contractOpener.open(new JettonMaster_1.JettonMaster((0, ton_1.address)(tvmTokenAddress)));
326
- const l2Address = await givenMinter.getL2Address();
633
+ const evmAddress = await givenMinter.getEVMAddress();
327
634
  await (0, Utils_1.sleep)(this.delay * 1000);
328
- return l2Address;
635
+ return evmAddress;
329
636
  }
330
637
  }
331
- 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);
332
639
  }
333
640
  async getTVMTokenAddress(evmTokenAddress) {
334
641
  (0, Utils_1.validateEVMAddress)(evmTokenAddress);
@@ -336,7 +643,7 @@ class TacSdk {
336
643
  if (bytecode.includes(ethers_1.ethers.id('getInfo()').slice(2, 10))) {
337
644
  const contract = new ethers_1.ethers.Contract(evmTokenAddress, this.TACParams.crossChainLayerTokenABI, this.TACParams.provider);
338
645
  const info = await contract.getInfo.staticCall();
339
- return info.l1Address;
646
+ return info.tvmAddress;
340
647
  }
341
648
  const jettonMaster = JettonMaster_1.JettonMaster.createFromConfig({
342
649
  evmTokenAddress,
@@ -346,6 +653,48 @@ class TacSdk {
346
653
  });
347
654
  return jettonMaster.address.toString();
348
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
+ }
349
698
  async simulateTACMessage(req) {
350
699
  for (const endpoint of this.liteSequencerEndpoints) {
351
700
  try {