@tonappchain/sdk 0.6.1-v3.0.4 → 0.6.1

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.
@@ -20,7 +20,6 @@ const Consts_1 = require("./Consts");
20
20
  const Utils_1 = require("./Utils");
21
21
  const artifacts_1 = require("@tonappchain/artifacts");
22
22
  const errors_1 = require("../errors");
23
- const contractOpener_1 = require("../adapters/contractOpener");
24
23
  const wrappers_1 = require("@tonappchain/artifacts/dist/src/ton/wrappers");
25
24
  const instances_1 = require("../errors/instances");
26
25
  class TacSdk {
@@ -45,7 +44,12 @@ class TacSdk {
45
44
  return new TacSdk(network, delay, artifacts, TONParams, TACParams, liteSequencerEndpoints);
46
45
  }
47
46
  static async prepareTONParams(network, delay, artifacts, TONParams) {
48
- const contractOpener = TONParams?.contractOpener ?? (await (0, contractOpener_1.orbsOpener4)(network));
47
+ const contractOpener = TONParams?.contractOpener ??
48
+ new ton_1.TonClient({
49
+ endpoint: network == Struct_1.Network.TESTNET
50
+ ? new URL('api/v2/jsonRPC', artifacts_1.testnet.TON_RPC_ENDPOINT_BY_TAC).toString()
51
+ : artifacts_1.mainnet.TON_PUBLIC_RPC_ENDPOINT,
52
+ });
49
53
  const settingsAddress = TONParams?.settingsAddress ?? artifacts.ton.addresses.TON_SETTINGS_ADDRESS;
50
54
  const settings = contractOpener.open(new Settings_1.Settings(ton_1.Address.parse(settingsAddress)));
51
55
  const jettonProxyAddress = await settings.getAddressSetting('JettonProxyAddress');
@@ -169,24 +173,9 @@ class TacSdk {
169
173
  .endCell();
170
174
  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
175
  }
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
176
  getTonTransferPayload(responseAddress, evmData, crossChainTonAmount, feeParams) {
188
177
  const queryId = (0, Utils_1.generateRandomNumberByTimestamp)().randomNumber;
189
- const feeData = this.generateFeeData(feeParams);
178
+ const feeData = (0, Utils_1.generateFeeData)(feeParams);
190
179
  return (0, ton_1.beginCell)()
191
180
  .storeUint(this.artifacts.ton.wrappers.CrossChainLayerOpCodes.anyone_tvmMsgToEVM, 32)
192
181
  .storeUint(queryId, 64)
@@ -288,7 +277,7 @@ class TacSdk {
288
277
  const opType = await this.getJettonOpType(jetton);
289
278
  await (0, Utils_1.sleep)(this.delay * 1000);
290
279
  console.log(`***** Jetton ${jetton.address} requires ${opType} operation`);
291
- const feeData = this.generateFeeData(feeParams);
280
+ const feeData = (0, Utils_1.generateFeeData)(feeParams);
292
281
  let payload;
293
282
  switch (opType) {
294
283
  case InternalStruct_1.AssetOpType.JETTON_BURN:
@@ -307,7 +296,7 @@ class TacSdk {
307
296
  const opType = await this.getNFTOpType(nft);
308
297
  await (0, Utils_1.sleep)(this.delay * 1000);
309
298
  console.log(`***** NFT ${nft.address} requires ${opType} operation`);
310
- const feeData = this.generateFeeData(feeParams);
299
+ const feeData = (0, Utils_1.generateFeeData)(feeParams);
311
300
  let payload;
312
301
  switch (opType) {
313
302
  case InternalStruct_1.AssetOpType.NFT_BURN:
@@ -391,6 +380,7 @@ class TacSdk {
391
380
  (0, Utils_1.validateTVMAddress)(precalculatedAddress);
392
381
  const contract = this.TONParams.contractOpener.open(new JettonMaster_1.JettonMaster((0, ton_1.address)(precalculatedAddress)));
393
382
  const { content } = await contract.getJettonData();
383
+ await (0, Utils_1.sleep)(this.delay * 1000);
394
384
  if (!content.metadata.decimals) {
395
385
  // if decimals not specified use default value 9
396
386
  return (0, ton_1.toNano)(asset.amount);
@@ -414,6 +404,7 @@ class TacSdk {
414
404
  const address = (0, ethers_1.isAddress)(asset.collectionAddress)
415
405
  ? await this.getTVMNFTAddress(asset.collectionAddress, asset.itemIndex)
416
406
  : await this.getNFTItemAddressTON(asset.collectionAddress, asset.itemIndex);
407
+ await (0, Utils_1.sleep)(this.delay * 1000);
417
408
  return {
418
409
  address,
419
410
  rawAmount: 1n,
@@ -441,7 +432,6 @@ class TacSdk {
441
432
  },
442
433
  evmValidExecutors: evmValidExecutors,
443
434
  extraData: '0x',
444
- feeAssetAddress: '',
445
435
  shardsKey: transactionLinker.shardsKey,
446
436
  tonAssets: rawAssets.map((asset) => ({
447
437
  amount: asset.rawAmount.toString(),
@@ -491,6 +481,29 @@ class TacSdk {
491
481
  const evmValidExecutors = this.TACParams.trustedTACExecutors;
492
482
  return await this.getFeeInfo(evmProxyMsg, transactionLinker, rawAssets, evmValidExecutors, false, undefined);
493
483
  }
484
+ async getTVMExecutorFeeInfo(assets, feeSymbol) {
485
+ const rawAssets = await this.convertAssetsToRawFormat(assets);
486
+ const requestBody = {
487
+ tonAssets: rawAssets.map((asset) => ({
488
+ amount: asset.rawAmount.toString(),
489
+ tokenAddress: asset.address || '',
490
+ assetType: asset.type,
491
+ })),
492
+ feeSymbol: feeSymbol,
493
+ };
494
+ let lastError;
495
+ for (const endpoint of this.liteSequencerEndpoints) {
496
+ try {
497
+ const response = await axios_1.default.post(`${endpoint}/ton/calculator/ton-executor-fee`, requestBody);
498
+ return response.data.response;
499
+ }
500
+ catch (error) {
501
+ console.error(`Error while calculating tvm executor fee ${endpoint}:`, error);
502
+ lastError = error;
503
+ }
504
+ }
505
+ throw (0, errors_1.simulationError)(lastError);
506
+ }
494
507
  async prepareCrossChainTransaction(evmProxyMsg, caller, assets, options) {
495
508
  let { forceSend = false, isRoundTrip = undefined, protocolFee = undefined, evmValidExecutors = [], evmExecutorFee = undefined, tvmValidExecutors = [], tvmExecutorFee = undefined, } = options || {};
496
509
  const rawAssets = await this.convertAssetsToRawFormat(assets);
@@ -556,6 +569,34 @@ class TacSdk {
556
569
  if (assets == undefined) {
557
570
  assets = [];
558
571
  }
572
+ let tonAssets = [];
573
+ for (const asset of assets) {
574
+ if (asset.type == Struct_1.AssetType.FT) {
575
+ const tvmAddress = await this.getTVMTokenAddress(asset.address);
576
+ tonAssets.push({
577
+ address: tvmAddress,
578
+ rawAmount: asset.rawAmount,
579
+ type: Struct_1.AssetType.FT,
580
+ });
581
+ }
582
+ else {
583
+ const nftItemAddress = await this.getTVMNFTAddress(asset.collectionAddress, asset.itemIndex);
584
+ tonAssets.push({
585
+ address: nftItemAddress,
586
+ amount: 1,
587
+ type: Struct_1.AssetType.NFT,
588
+ });
589
+ }
590
+ }
591
+ if (value > 0) {
592
+ const tvmAddress = await this.getTVMTokenAddress(await this.nativeTACAddress());
593
+ tonAssets.push({
594
+ address: tvmAddress,
595
+ rawAmount: value,
596
+ type: Struct_1.AssetType.FT,
597
+ });
598
+ }
599
+ const suggestedTONExecutorFee = await this.getTVMExecutorFeeInfo(tonAssets, Consts_1.TAC_SYMBOL);
559
600
  const crossChainLayerAddress = await this.TACParams.crossChainLayer.getAddress();
560
601
  for (const asset of assets) {
561
602
  if (asset.type == Struct_1.AssetType.FT) {
@@ -571,27 +612,12 @@ class TacSdk {
571
612
  }
572
613
  const shardsKey = BigInt(Math.round(Math.random() * 1e18));
573
614
  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
615
  const outMessage = {
590
616
  shardsKey: shardsKey,
591
617
  tvmTarget: tonTarget,
592
618
  tvmPayload: '',
593
619
  tvmProtocolFee: protocolFee,
594
- tvmExecutorFee: tvmExecutorFeeInTAC,
620
+ tvmExecutorFee: tvmExecutorFee ?? BigInt(suggestedTONExecutorFee.inTAC),
595
621
  tvmValidExecutors: this.TACParams.trustedTONExecutors,
596
622
  toBridge: assets
597
623
  .filter((asset) => asset.type === Struct_1.AssetType.FT)
@@ -690,24 +716,26 @@ class TacSdk {
690
716
  await (0, Utils_1.sleep)(this.delay * 1000);
691
717
  return evmAddress.toString();
692
718
  }
693
- return this.TACParams.tokenUtils.computeAddress(tvmNFTAddress);
719
+ return this.TACParams.tokenUtils.computeAddressERC721(tvmNFTAddress);
694
720
  }
695
721
  async isContractDeployedOnTVM(address) {
696
722
  return (await this.TONParams.contractOpener.getContractState(ton_1.Address.parse(address))).state === 'active';
697
723
  }
698
724
  async simulateTACMessage(req) {
725
+ let lastError;
699
726
  for (const endpoint of this.liteSequencerEndpoints) {
700
727
  try {
701
- const response = await axios_1.default.post(`${endpoint}/tac/simulator/simulate-message`, req, {
728
+ const response = await axios_1.default.post(new URL('tac/simulator/simulate-message', endpoint).toString(), req, {
702
729
  transformResponse: [Utils_1.toCamelCaseTransformer],
703
730
  });
704
731
  return response.data.response;
705
732
  }
706
733
  catch (error) {
707
734
  console.error(`Error while simulating with ${endpoint}:`, error);
735
+ lastError = error;
708
736
  }
709
737
  }
710
- throw errors_1.simulationError;
738
+ throw (0, errors_1.simulationError)(lastError);
711
739
  }
712
740
  }
713
741
  exports.TacSdk = TacSdk;
@@ -1,6 +1,6 @@
1
1
  import { Address, Cell } from '@ton/ton';
2
2
  import { AbiCoder } from 'ethers';
3
- import { EvmProxyMsg, TransactionLinker, ValidExecutors } from '../structs/Struct';
3
+ import { EvmProxyMsg, FeeParams, TransactionLinker, ValidExecutors } from '../structs/Struct';
4
4
  import { RandomNumberByTimestamp } from '../structs/InternalStruct';
5
5
  export declare const sleep: (ms: number) => Promise<unknown>;
6
6
  export declare function generateRandomNumber(interval: number): number;
@@ -16,3 +16,4 @@ export declare const convertKeysToCamelCase: <T>(data: T) => T;
16
16
  export declare const calculateRawAmount: (amount: number, decimals: number) => bigint;
17
17
  export declare const calculateAmount: (rawAmount: bigint, decimals: number) => number;
18
18
  export declare const toCamelCaseTransformer: (data: any) => any;
19
+ export declare const generateFeeData: (feeParams?: FeeParams) => Cell | undefined;
package/dist/sdk/Utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.toCamelCaseTransformer = exports.calculateAmount = exports.calculateRawAmount = exports.convertKeysToCamelCase = exports.sleep = void 0;
3
+ exports.generateFeeData = exports.toCamelCaseTransformer = exports.calculateAmount = exports.calculateRawAmount = exports.convertKeysToCamelCase = exports.sleep = void 0;
4
4
  exports.generateRandomNumber = generateRandomNumber;
5
5
  exports.generateRandomNumberByTimestamp = generateRandomNumberByTimestamp;
6
6
  exports.calculateContractAddress = calculateContractAddress;
@@ -126,3 +126,19 @@ const toCamelCaseTransformer = (data) => {
126
126
  }
127
127
  };
128
128
  exports.toCamelCaseTransformer = toCamelCaseTransformer;
129
+ const generateFeeData = (feeParams) => {
130
+ if (feeParams) {
131
+ let feeDataBuilder = (0, ton_1.beginCell)()
132
+ .storeBit(feeParams.isRoundTrip)
133
+ .storeCoins(feeParams.protocolFee)
134
+ .storeCoins(feeParams.evmExecutorFee);
135
+ if (feeParams.isRoundTrip) {
136
+ feeDataBuilder.storeCoins(feeParams.tvmExecutorFee);
137
+ }
138
+ return feeDataBuilder.endCell();
139
+ }
140
+ else {
141
+ return undefined;
142
+ }
143
+ };
144
+ exports.generateFeeData = generateFeeData;
@@ -0,0 +1,15 @@
1
+ import type { ContractOpener } from '../structs/Struct';
2
+ import type { SendResult, ShardTransaction } from '../structs/InternalStruct';
3
+ import { Network } from '../structs/Struct';
4
+ import { SenderAbstraction } from './SenderAbstraction';
5
+ import { HighloadWalletV3 } from '../wrappers/HighloadWalletV3';
6
+ export declare class BatchSender implements SenderAbstraction {
7
+ private wallet;
8
+ private secretKey;
9
+ constructor(wallet: HighloadWalletV3, secretKey: Buffer);
10
+ sendShardTransactions(shardTransactions: ShardTransaction[], delay: number, _chain: Network, contractOpener: ContractOpener): Promise<SendResult[]>;
11
+ private prepareGroups;
12
+ private sendGroup;
13
+ getSenderAddress(): string;
14
+ sendShardTransaction(shardTransaction: ShardTransaction, delay: number, _chain: Network, contractOpener: ContractOpener): Promise<SendResult>;
15
+ }
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BatchSender = void 0;
4
+ const ton_1 = require("@ton/ton");
5
+ const SenderAbstraction_1 = require("./SenderAbstraction");
6
+ const Consts_1 = require("../sdk/Consts");
7
+ const instances_1 = require("../errors/instances");
8
+ class BatchSender {
9
+ constructor(wallet, secretKey) {
10
+ this.wallet = wallet;
11
+ this.secretKey = secretKey;
12
+ }
13
+ async sendShardTransactions(shardTransactions, delay, _chain, contractOpener) {
14
+ const allMessages = [];
15
+ let minValidUntil = Number.MAX_SAFE_INTEGER;
16
+ for (const transaction of shardTransactions) {
17
+ for (const message of transaction.messages) {
18
+ allMessages.push((0, ton_1.internal)({
19
+ to: message.address,
20
+ value: (0, ton_1.fromNano)(message.value),
21
+ bounce: true,
22
+ body: message.payload,
23
+ }));
24
+ }
25
+ minValidUntil = Math.min(minValidUntil, transaction.validUntil);
26
+ }
27
+ const groups = await this.prepareGroups(allMessages);
28
+ const results = [];
29
+ let currentMessageIndex = 0;
30
+ for (const group of groups) {
31
+ await (0, SenderAbstraction_1.sleep)(delay * 1000);
32
+ try {
33
+ const result = await this.sendGroup(group, contractOpener);
34
+ results.push({
35
+ success: true,
36
+ result,
37
+ lastMessageIndex: currentMessageIndex + group.length - 1,
38
+ });
39
+ }
40
+ catch (error) {
41
+ results.push({
42
+ success: false,
43
+ error: error,
44
+ lastMessageIndex: currentMessageIndex - 1,
45
+ });
46
+ break; // Stop sending after first error
47
+ }
48
+ currentMessageIndex += group.length;
49
+ }
50
+ return results;
51
+ }
52
+ async prepareGroups(messages) {
53
+ const total = messages.length;
54
+ let left = 0;
55
+ const groups = [];
56
+ while (left < total) {
57
+ let groupSize = total - left;
58
+ if (groupSize > Consts_1.MAX_HIGHLOAD_GROUP_MSG_NUM) {
59
+ groupSize = Consts_1.MAX_HIGHLOAD_GROUP_MSG_NUM;
60
+ }
61
+ let validGroupFound = false;
62
+ while (groupSize > 0) {
63
+ const group = messages.slice(left, left + groupSize);
64
+ const createdAt = Math.floor(Date.now() / 1000) - 40;
65
+ const queryId = this.wallet.getQueryIdFromCreatedAt(createdAt);
66
+ const externalMsg = this.wallet.getExternalMessage(group, ton_1.SendMode.PAY_GAS_SEPARATELY, 0n, queryId);
67
+ const isBocSizeValid = externalMsg.body.toBoc().length <= Consts_1.MAX_EXT_MSG_SIZE;
68
+ const isDepthValid = externalMsg.body.depth() <= Consts_1.MAX_MSG_DEPTH;
69
+ if (isBocSizeValid && isDepthValid) {
70
+ groups.push(group);
71
+ left += groupSize;
72
+ validGroupFound = true;
73
+ break;
74
+ }
75
+ if (groupSize <= 1) {
76
+ throw (0, instances_1.prepareMessageGroupError)(isBocSizeValid, isDepthValid);
77
+ }
78
+ groupSize = Math.floor(groupSize / 2);
79
+ }
80
+ if (!validGroupFound) {
81
+ throw instances_1.noValidGroupFoundError;
82
+ }
83
+ }
84
+ return groups;
85
+ }
86
+ async sendGroup(messages, contractOpener) {
87
+ const walletContract = contractOpener.open(this.wallet);
88
+ return walletContract.sendTransfer({
89
+ secretKey: this.secretKey,
90
+ messages,
91
+ sendMode: ton_1.SendMode.PAY_GAS_SEPARATELY,
92
+ });
93
+ }
94
+ getSenderAddress() {
95
+ return this.wallet.address.toString();
96
+ }
97
+ async sendShardTransaction(shardTransaction, delay, _chain, contractOpener) {
98
+ const messages = [];
99
+ for (const message of shardTransaction.messages) {
100
+ messages.push((0, ton_1.internal)({
101
+ to: message.address,
102
+ value: (0, ton_1.fromNano)(message.value),
103
+ bounce: true,
104
+ body: message.payload,
105
+ }));
106
+ }
107
+ await (0, SenderAbstraction_1.sleep)(delay * 1000);
108
+ const result = await this.sendGroup(messages, contractOpener);
109
+ return {
110
+ success: true,
111
+ result,
112
+ lastMessageIndex: shardTransaction.messages.length - 1,
113
+ };
114
+ }
115
+ }
116
+ exports.BatchSender = BatchSender;
@@ -1,14 +1,12 @@
1
1
  import type { ContractOpener } from '../structs/Struct';
2
- import type { ShardTransaction } from '../structs/InternalStruct';
2
+ import type { SendResult, ShardTransaction } from '../structs/InternalStruct';
3
3
  import { Network } from '../structs/Struct';
4
4
  import { SenderAbstraction, WalletInstance } from './SenderAbstraction';
5
5
  export declare class RawSender implements SenderAbstraction {
6
6
  private wallet;
7
7
  private secretKey;
8
8
  constructor(wallet: WalletInstance, secretKey: Buffer);
9
- sendShardTransactions(shardTransactions: ShardTransaction[], delay: number, chain: Network, contractOpener: ContractOpener): Promise<unknown>;
9
+ sendShardTransactions(shardTransactions: ShardTransaction[], delay: number, chain: Network, contractOpener: ContractOpener): Promise<SendResult[]>;
10
10
  getSenderAddress(): string;
11
- sendShardTransaction(shardTransaction: ShardTransaction, delay: number, _chain: Network, contractOpener: ContractOpener): Promise<void | (import("@ton/sandbox").SendMessageResult & {
12
- result: void;
13
- })>;
11
+ sendShardTransaction(shardTransaction: ShardTransaction, delay: number, _chain: Network, contractOpener: ContractOpener): Promise<SendResult>;
14
12
  }
@@ -4,43 +4,34 @@ exports.RawSender = void 0;
4
4
  const ton_1 = require("@ton/ton");
5
5
  const ton_2 = require("@ton/ton");
6
6
  const SenderAbstraction_1 = require("./SenderAbstraction");
7
- const HighloadWalletV3_1 = require("../wrappers/HighloadWalletV3");
8
7
  class RawSender {
9
8
  constructor(wallet, secretKey) {
10
9
  this.wallet = wallet;
11
10
  this.secretKey = secretKey;
12
11
  }
13
12
  async sendShardTransactions(shardTransactions, delay, chain, contractOpener) {
14
- const walletContract = contractOpener.open(this.wallet);
15
- const isBatchSendSupported = walletContract instanceof HighloadWalletV3_1.HighloadWalletV3 || walletContract instanceof ton_1.WalletContractV5R1;
16
- if (isBatchSendSupported) {
17
- const messages = [];
18
- for (const shardTx of shardTransactions) {
19
- for (const message of shardTx.messages) {
20
- messages.push((0, ton_1.internal)({
21
- to: message.address,
22
- value: (0, ton_1.fromNano)(message.value),
23
- bounce: true,
24
- body: message.payload,
25
- }));
26
- }
13
+ const results = [];
14
+ let currentMessageIndex = 0;
15
+ for (const shardTx of shardTransactions) {
16
+ try {
17
+ const result = await this.sendShardTransaction(shardTx, delay, chain, contractOpener);
18
+ results.push({
19
+ success: true,
20
+ result,
21
+ lastMessageIndex: currentMessageIndex + shardTx.messages.length - 1,
22
+ });
23
+ currentMessageIndex += shardTx.messages.length;
27
24
  }
28
- const seqno = await walletContract.getSeqno();
29
- await (0, SenderAbstraction_1.sleep)(delay * 1000);
30
- return walletContract.sendTransfer({
31
- seqno,
32
- secretKey: this.secretKey,
33
- messages,
34
- sendMode: ton_2.SendMode.PAY_GAS_SEPARATELY,
35
- });
36
- }
37
- else {
38
- const results = [];
39
- for (const shardTx of shardTransactions) {
40
- results.push(await this.sendShardTransaction(shardTx, delay, chain, contractOpener));
25
+ catch (error) {
26
+ results.push({
27
+ success: false,
28
+ error: error,
29
+ lastMessageIndex: currentMessageIndex - 1,
30
+ });
31
+ break; // Stop sending after first error
41
32
  }
42
- return results;
43
33
  }
34
+ return results;
44
35
  }
45
36
  getSenderAddress() {
46
37
  return this.wallet.address.toString();
@@ -59,12 +50,17 @@ class RawSender {
59
50
  }));
60
51
  }
61
52
  await (0, SenderAbstraction_1.sleep)(delay * 1000);
62
- return walletContract.sendTransfer({
53
+ const result = await walletContract.sendTransfer({
63
54
  seqno,
64
55
  secretKey: this.secretKey,
65
56
  messages,
66
57
  sendMode: ton_2.SendMode.PAY_GAS_SEPARATELY,
67
58
  });
59
+ return {
60
+ success: true,
61
+ result,
62
+ lastMessageIndex: shardTransaction.messages.length - 1,
63
+ };
68
64
  }
69
65
  }
70
66
  exports.RawSender = RawSender;
@@ -1,6 +1,6 @@
1
1
  import type { Contract, ContractProvider, MessageRelaxed, SendMode } from '@ton/ton';
2
2
  import type { ContractOpener } from '../structs/Struct';
3
- import type { ShardTransaction } from '../structs/InternalStruct';
3
+ import type { SendResult, ShardTransaction } from '../structs/InternalStruct';
4
4
  import { Network } from '../structs/Struct';
5
5
  export declare const sleep: (ms: number) => Promise<unknown>;
6
6
  export interface WalletInstance extends Contract {
@@ -14,7 +14,7 @@ export interface WalletInstance extends Contract {
14
14
  }): Promise<void>;
15
15
  }
16
16
  export interface SenderAbstraction {
17
- sendShardTransaction(shardTransaction: ShardTransaction, delay: number, chain?: Network, contractOpener?: ContractOpener): Promise<unknown>;
18
- sendShardTransactions(shardTransactions: ShardTransaction[], delay: number, chain?: Network, contractOpener?: ContractOpener): Promise<unknown>;
17
+ sendShardTransaction(shardTransaction: ShardTransaction, delay: number, chain?: Network, contractOpener?: ContractOpener): Promise<SendResult>;
18
+ sendShardTransactions(shardTransactions: ShardTransaction[], delay: number, chain?: Network, contractOpener?: ContractOpener): Promise<SendResult[]>;
19
19
  getSenderAddress(): string;
20
20
  }
@@ -5,6 +5,7 @@ const ton_1 = require("@ton/ton");
5
5
  const ton_crypto_1 = require("ton-crypto");
6
6
  const RawSender_1 = require("./RawSender");
7
7
  const TonConnectSender_1 = require("./TonConnectSender");
8
+ const BatchSender_1 = require("./BatchSender");
8
9
  const errors_1 = require("../errors");
9
10
  const Struct_1 = require("../structs/Struct");
10
11
  const HighloadWalletV3_1 = require("../wrappers/HighloadWalletV3");
@@ -49,6 +50,9 @@ class SenderFactory {
49
50
  config.timeout = params.options?.highloadV3?.timeout ?? HighloadWalletV3_1.DEFAULT_TIMEOUT;
50
51
  }
51
52
  const wallet = exports.wallets[params.version].create(config);
53
+ if (params.version === 'HIGHLOAD_V3') {
54
+ return new BatchSender_1.BatchSender(wallet, keypair.secretKey);
55
+ }
52
56
  return new RawSender_1.RawSender(wallet, keypair.secretKey);
53
57
  }
54
58
  }
@@ -1,12 +1,12 @@
1
1
  import { TonConnectUI } from '@tonconnect/ui';
2
- import type { ShardTransaction } from '../structs/InternalStruct';
2
+ import type { SendResult, ShardTransaction } from '../structs/InternalStruct';
3
3
  import { ContractOpener, Network } from '../structs/Struct';
4
4
  import { SenderAbstraction } from './SenderAbstraction';
5
- import { SendTransactionResponse } from '@tonconnect/sdk';
6
5
  export declare class TonConnectSender implements SenderAbstraction {
7
6
  readonly tonConnect: TonConnectUI;
8
7
  constructor(tonConnect: TonConnectUI);
9
- sendShardTransactions(shardTransactions: ShardTransaction[], delay: number, chain?: Network, contractOpener?: ContractOpener): Promise<SendTransactionResponse[]>;
8
+ private sendChunkedMessages;
9
+ sendShardTransactions(shardTransactions: ShardTransaction[], delay: number, chain: Network, _contractOpener?: ContractOpener): Promise<SendResult[]>;
10
10
  getSenderAddress(): string;
11
- sendShardTransaction(shardTransaction: ShardTransaction, delay: number, chain: Network): Promise<SendTransactionResponse>;
11
+ sendShardTransaction(shardTransaction: ShardTransaction, delay: number, chain: Network): Promise<SendResult>;
12
12
  }
@@ -5,13 +5,51 @@ const protocol_1 = require("@tonconnect/protocol");
5
5
  const ui_1 = require("@tonconnect/ui");
6
6
  const Struct_1 = require("../structs/Struct");
7
7
  const SenderAbstraction_1 = require("./SenderAbstraction");
8
+ const CHUNK_SIZE = 4;
8
9
  class TonConnectSender {
9
10
  constructor(tonConnect) {
10
11
  this.tonConnect = tonConnect;
11
12
  }
12
- async sendShardTransactions(shardTransactions, delay, chain, contractOpener) {
13
+ async sendChunkedMessages(messages, validUntil, chain) {
14
+ const responses = [];
15
+ let currentMessageIndex = 0;
16
+ const chunkSize =
17
+ //@ts-ignore // 'find' checks that maxMessages is a property of the feature
18
+ this.tonConnect.wallet?.device.features.find((feat) => feat.hasOwnProperty('maxMessages'))?.maxMessages ||
19
+ CHUNK_SIZE;
20
+ for (let i = 0; i < messages.length; i += chunkSize) {
21
+ const chunk = messages.slice(i, i + chunkSize);
22
+ const transaction = {
23
+ validUntil,
24
+ messages: chunk,
25
+ network: chain == Struct_1.Network.TESTNET ? ui_1.CHAIN.TESTNET : ui_1.CHAIN.MAINNET,
26
+ };
27
+ try {
28
+ const response = await this.tonConnect.sendTransaction(transaction);
29
+ responses.push({
30
+ success: true,
31
+ result: response,
32
+ lastMessageIndex: currentMessageIndex + chunk.length - 1,
33
+ });
34
+ currentMessageIndex += chunk.length;
35
+ }
36
+ catch (error) {
37
+ responses.push({
38
+ success: false,
39
+ error: error,
40
+ lastMessageIndex: currentMessageIndex - 1,
41
+ });
42
+ break; // Stop sending after first error
43
+ }
44
+ if (i + chunkSize < messages.length) {
45
+ await (0, SenderAbstraction_1.sleep)(1000);
46
+ }
47
+ }
48
+ return responses;
49
+ }
50
+ async sendShardTransactions(shardTransactions, delay, chain, _contractOpener) {
13
51
  const allMessages = [];
14
- let minValidUntil = 0;
52
+ let minValidUntil = Number.MAX_SAFE_INTEGER;
15
53
  for (const transaction of shardTransactions) {
16
54
  for (const message of transaction.messages) {
17
55
  allMessages.push({
@@ -22,14 +60,8 @@ class TonConnectSender {
22
60
  }
23
61
  minValidUntil = Math.min(minValidUntil, transaction.validUntil);
24
62
  }
25
- const transaction = {
26
- validUntil: minValidUntil,
27
- messages: allMessages,
28
- network: chain == Struct_1.Network.TESTNET ? ui_1.CHAIN.TESTNET : ui_1.CHAIN.MAINNET,
29
- };
30
63
  await (0, SenderAbstraction_1.sleep)(delay * 1000);
31
- const response = await this.tonConnect.sendTransaction(transaction);
32
- return [response];
64
+ return this.sendChunkedMessages(allMessages, minValidUntil, chain);
33
65
  }
34
66
  getSenderAddress() {
35
67
  return this.tonConnect.account?.address?.toString() || '';
@@ -43,13 +75,9 @@ class TonConnectSender {
43
75
  payload: protocol_1.Base64.encode(message.payload.toBoc()).toString(),
44
76
  });
45
77
  }
46
- const transaction = {
47
- validUntil: shardTransaction.validUntil,
48
- messages,
49
- network: chain == Struct_1.Network.TESTNET ? ui_1.CHAIN.TESTNET : ui_1.CHAIN.MAINNET,
50
- };
51
78
  await (0, SenderAbstraction_1.sleep)(delay * 1000);
52
- return this.tonConnect.sendTransaction(transaction);
79
+ const responses = await this.sendChunkedMessages(messages, shardTransaction.validUntil, chain);
80
+ return responses[0];
53
81
  }
54
82
  }
55
83
  exports.TonConnectSender = TonConnectSender;
@@ -1,2 +1,3 @@
1
1
  export * from './SenderAbstraction';
2
2
  export * from './SenderFactory';
3
+ export * from './BatchSender';
@@ -16,3 +16,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./SenderAbstraction"), exports);
18
18
  __exportStar(require("./SenderFactory"), exports);
19
+ __exportStar(require("./BatchSender"), exports);
@@ -1,5 +1,5 @@
1
1
  import { Cell } from '@ton/ton';
2
- import { ContractOpener, TACSimulationResult, ExecutionStagesByOperationId, Network, OperationIdsByShardsKey, RawAssetBridgingData, StatusInfosByOperationId, OperationType, AssetType } from './Struct';
2
+ import { ContractOpener, TACSimulationResult, ExecutionStagesByOperationId, Network, OperationIdsByShardsKey, RawAssetBridgingData, StatusInfosByOperationId, OperationType, AssetType, SuggestedTONExecutorFee } from './Struct';
3
3
  import { AbstractProvider, ethers, Interface, InterfaceAbi } from 'ethers';
4
4
  import { mainnet, testnet } from '@tonappchain/artifacts';
5
5
  export type ShardMessage = {
@@ -80,3 +80,10 @@ export type StatusesResponse = ResponseBase<StatusInfosByOperationId>;
80
80
  export type OperationIdsByShardsKeyResponse = ResponseBase<OperationIdsByShardsKey>;
81
81
  export type StageProfilingResponse = ResponseBase<ExecutionStagesByOperationId>;
82
82
  export type TACSimulationResponse = ResponseBase<TACSimulationResult>;
83
+ export type SuggestedTONExecutorFeeResponse = ResponseBase<SuggestedTONExecutorFee>;
84
+ export interface SendResult {
85
+ success: boolean;
86
+ result?: unknown;
87
+ error?: Error;
88
+ lastMessageIndex?: number;
89
+ }