@tonappchain/sdk 0.7.0-rc14 → 0.7.0-rc15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/dist/adapters/retryableContractOpener.js +1 -1
- package/dist/assets/AssetFactory.js +1 -1
- package/dist/assets/FT.d.ts +8 -12
- package/dist/assets/FT.js +69 -36
- package/dist/assets/NFT.d.ts +4 -2
- package/dist/assets/NFT.js +14 -2
- package/dist/assets/TON.d.ts +4 -10
- package/dist/assets/TON.js +17 -15
- package/dist/errors/index.d.ts +1 -1
- package/dist/errors/index.js +7 -2
- package/dist/errors/instances.d.ts +8 -1
- package/dist/errors/instances.js +22 -16
- package/dist/index.d.ts +2 -1
- package/dist/index.js +5 -3
- package/dist/interfaces/Asset.d.ts +22 -17
- package/dist/interfaces/ILiteSequencerClient.d.ts +14 -1
- package/dist/interfaces/IOperationTracker.d.ts +16 -1
- package/dist/interfaces/ISimulator.d.ts +7 -36
- package/dist/interfaces/ITACTransactionManager.d.ts +15 -0
- package/dist/interfaces/ITONTransactionManager.d.ts +21 -0
- package/dist/interfaces/ITONTransactionManager.js +2 -0
- package/dist/interfaces/ITacSDK.d.ts +51 -13
- package/dist/interfaces/index.d.ts +2 -1
- package/dist/interfaces/index.js +2 -1
- package/dist/sdk/Configuration.d.ts +1 -0
- package/dist/sdk/Configuration.js +52 -4
- package/dist/sdk/Consts.d.ts +1 -0
- package/dist/sdk/Consts.js +5 -4
- package/dist/sdk/LiteSequencerClient.d.ts +5 -3
- package/dist/sdk/LiteSequencerClient.js +27 -4
- package/dist/sdk/OperationTracker.d.ts +3 -1
- package/dist/sdk/OperationTracker.js +51 -11
- package/dist/sdk/Simulator.d.ts +6 -12
- package/dist/sdk/Simulator.js +30 -124
- package/dist/sdk/TACTransactionManager.d.ts +10 -0
- package/dist/sdk/TACTransactionManager.js +92 -0
- package/dist/sdk/TONTransactionManager.d.ts +17 -0
- package/dist/sdk/TONTransactionManager.js +209 -0
- package/dist/sdk/TacSdk.d.ts +16 -10
- package/dist/sdk/TacSdk.js +52 -19
- package/dist/sdk/TxFinalizer.d.ts +3 -2
- package/dist/sdk/TxFinalizer.js +8 -8
- package/dist/sdk/Utils.d.ts +8 -4
- package/dist/sdk/Utils.js +80 -12
- package/dist/sdk/Validator.d.ts +2 -2
- package/dist/sdk/Validator.js +1 -1
- package/dist/structs/InternalStruct.d.ts +6 -2
- package/dist/structs/Struct.d.ts +70 -16
- package/package.json +4 -3
- package/dist/interfaces/ITransactionManager.d.ts +0 -35
- package/dist/sdk/TransactionManager.d.ts +0 -22
- package/dist/sdk/TransactionManager.js +0 -272
- /package/dist/interfaces/{ITransactionManager.js → ITACTransactionManager.js} +0 -0
package/dist/sdk/Simulator.d.ts
CHANGED
|
@@ -1,17 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { IHttpClient } from '../interfaces';
|
|
1
|
+
import { IConfiguration, ILogger, IOperationTracker, ISimulator } from '../interfaces';
|
|
3
2
|
import type { SenderAbstraction } from '../sender';
|
|
4
|
-
import { CrosschainTx,
|
|
3
|
+
import { CrosschainTx, ExecutionFeeEstimationResult } from '../structs/Struct';
|
|
5
4
|
export declare class Simulator implements ISimulator {
|
|
6
5
|
private readonly config;
|
|
6
|
+
private readonly operationTracker;
|
|
7
7
|
private readonly logger;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
simulateTransactions(sender: SenderAbstraction, txs: CrosschainTx[]): Promise<TACSimulationResult[]>;
|
|
12
|
-
private buildTACSimulationRequest;
|
|
13
|
-
getTVMExecutorFeeInfo(assets: Asset[], feeSymbol: string, tvmValidExecutors?: string[]): Promise<SuggestedTONExecutorFee>;
|
|
14
|
-
private getFeeInfo;
|
|
15
|
-
getTransactionSimulationInfo(evmProxyMsg: EvmProxyMsg, sender: SenderAbstraction, assets?: Asset[]): Promise<ExecutionFeeEstimationResult>;
|
|
16
|
-
getSimulationInfoForTransaction(evmProxyMsg: EvmProxyMsg, transactionLinker: TransactionLinker, assets: Asset[], allowSimulationError?: boolean, isRoundTrip?: boolean, evmValidExecutors?: string[], tvmValidExecutors?: string[], calculateRollbackFee?: boolean): Promise<ExecutionFeeEstimationResult>;
|
|
8
|
+
constructor(config: IConfiguration, operationTracker: IOperationTracker, logger?: ILogger);
|
|
9
|
+
getSimulationsInfo(sender: SenderAbstraction, txs: CrosschainTx[]): Promise<ExecutionFeeEstimationResult[]>;
|
|
10
|
+
getSimulationInfo(sender: SenderAbstraction, tx: CrosschainTx): Promise<ExecutionFeeEstimationResult>;
|
|
17
11
|
}
|
package/dist/sdk/Simulator.js
CHANGED
|
@@ -2,162 +2,68 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Simulator = void 0;
|
|
4
4
|
const ton_1 = require("@ton/ton");
|
|
5
|
-
const errors_1 = require("../errors");
|
|
6
|
-
const AxiosHttpClient_1 = require("./AxiosHttpClient");
|
|
7
5
|
const Logger_1 = require("./Logger");
|
|
8
6
|
const Utils_1 = require("./Utils");
|
|
9
7
|
const Validator_1 = require("./Validator");
|
|
10
8
|
class Simulator {
|
|
11
|
-
constructor(config, logger = new Logger_1.NoopLogger()
|
|
9
|
+
constructor(config, operationTracker, logger = new Logger_1.NoopLogger()) {
|
|
12
10
|
this.config = config;
|
|
11
|
+
this.operationTracker = operationTracker;
|
|
13
12
|
this.logger = logger;
|
|
14
|
-
this.httpClient = httpClient;
|
|
15
13
|
}
|
|
16
|
-
async
|
|
17
|
-
Validator_1.Validator.validateTACSimulationRequest(req);
|
|
18
|
-
this.logger.debug('Simulating TAC message');
|
|
19
|
-
let lastError;
|
|
20
|
-
for (const endpoint of this.config.liteSequencerEndpoints) {
|
|
21
|
-
try {
|
|
22
|
-
const response = await this.httpClient.post(new URL('tac/simulator/simulate-message', endpoint).toString(), req, {
|
|
23
|
-
transformResponse: [Utils_1.toCamelCaseTransformer],
|
|
24
|
-
});
|
|
25
|
-
this.logger.debug('TAC message simulation success');
|
|
26
|
-
return response.data.response;
|
|
27
|
-
}
|
|
28
|
-
catch (error) {
|
|
29
|
-
this.logger.error(`Error while simulating with ${endpoint}: ${error}`);
|
|
30
|
-
lastError = error;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
throw (0, errors_1.simulationError)(lastError);
|
|
34
|
-
}
|
|
35
|
-
async simulateTransactions(sender, txs) {
|
|
14
|
+
async getSimulationsInfo(sender, txs) {
|
|
36
15
|
this.logger.debug(`Simulating ${txs.length} TAC messages`);
|
|
37
16
|
const results = [];
|
|
38
17
|
for (const tx of txs) {
|
|
39
|
-
const
|
|
40
|
-
const result = await this.simulateTACMessage(req);
|
|
18
|
+
const result = await this.getSimulationInfo(sender, tx);
|
|
41
19
|
results.push(result);
|
|
42
20
|
}
|
|
43
21
|
return results;
|
|
44
22
|
}
|
|
45
|
-
async
|
|
23
|
+
async getSimulationInfo(sender, tx) {
|
|
24
|
+
this.logger.debug('Getting simulation info');
|
|
46
25
|
const { evmProxyMsg, assets = [], options = {} } = tx;
|
|
47
|
-
const { evmValidExecutors = this.config.TACParams.trustedTACExecutors, tvmValidExecutors = this.config.TACParams.trustedTONExecutors, calculateRollbackFee = true, } = options;
|
|
26
|
+
const { evmValidExecutors = this.config.TACParams.trustedTACExecutors, tvmValidExecutors = this.config.TACParams.trustedTONExecutors, calculateRollbackFee = true, allowSimulationError = false, } = options;
|
|
27
|
+
Validator_1.Validator.validateEVMAddress(evmProxyMsg.evmTargetAddress);
|
|
48
28
|
Validator_1.Validator.validateEVMAddresses(evmValidExecutors);
|
|
49
29
|
Validator_1.Validator.validateTVMAddresses(tvmValidExecutors);
|
|
50
30
|
const aggregatedData = await (0, Utils_1.aggregateTokens)(assets);
|
|
51
|
-
const
|
|
52
|
-
const transactionLinker = (0, Utils_1.generateTransactionLinker)(sender.getSenderAddress(),
|
|
53
|
-
|
|
31
|
+
const shardCount = aggregatedData.jettons.length || 1;
|
|
32
|
+
const transactionLinker = (0, Utils_1.generateTransactionLinker)(sender.getSenderAddress(), shardCount);
|
|
33
|
+
const tacSimulationParams = {
|
|
54
34
|
tacCallParams: {
|
|
55
35
|
arguments: evmProxyMsg.encodedParameters ?? '0x',
|
|
56
36
|
methodName: (0, Utils_1.formatSolidityMethodName)(evmProxyMsg.methodName),
|
|
57
37
|
target: evmProxyMsg.evmTargetAddress,
|
|
58
38
|
},
|
|
59
|
-
evmValidExecutors
|
|
60
|
-
tvmValidExecutors
|
|
39
|
+
evmValidExecutors,
|
|
40
|
+
tvmValidExecutors,
|
|
61
41
|
extraData: '0x',
|
|
62
42
|
shardsKey: transactionLinker.shardsKey,
|
|
63
|
-
tonAssets:
|
|
64
|
-
amount: asset.rawAmount.toString(),
|
|
65
|
-
tokenAddress: asset.address || '',
|
|
66
|
-
assetType: asset.type,
|
|
67
|
-
})),
|
|
43
|
+
tonAssets: (0, Utils_1.mapAssetsToTonAssets)(assets),
|
|
68
44
|
tonCaller: transactionLinker.caller,
|
|
69
|
-
calculateRollbackFee
|
|
45
|
+
calculateRollbackFee,
|
|
70
46
|
};
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const requestBody = {
|
|
75
|
-
tonAssets: assets.map((asset) => ({
|
|
76
|
-
amount: asset.rawAmount.toString(),
|
|
77
|
-
tokenAddress: asset.address || '',
|
|
78
|
-
assetType: asset.type,
|
|
79
|
-
})),
|
|
80
|
-
feeSymbol: feeSymbol,
|
|
81
|
-
tvmValidExecutors: tvmValidExecutors,
|
|
82
|
-
};
|
|
83
|
-
let lastError;
|
|
84
|
-
for (const endpoint of this.config.liteSequencerEndpoints) {
|
|
85
|
-
try {
|
|
86
|
-
const response = await this.httpClient.post(`${endpoint}/ton/calculator/ton-executor-fee`, requestBody);
|
|
87
|
-
return response.data.response;
|
|
88
|
-
}
|
|
89
|
-
catch (error) {
|
|
90
|
-
this.logger.error(`Error while calculating tvm executor fee ${endpoint}: ${error}`);
|
|
91
|
-
lastError = error;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
this.logger.error('Error while calculating tvm executor fee on all endpoints');
|
|
95
|
-
throw (0, errors_1.simulationError)(lastError);
|
|
96
|
-
}
|
|
97
|
-
async getFeeInfo(evmProxyMsg, transactionLinker, assets, allowSimulationError = false, isRoundTrip = true, evmValidExecutors = this.config.TACParams.trustedTACExecutors, tvmValidExecutors = this.config.TACParams.trustedTONExecutors, calculateRollbackFee = true) {
|
|
98
|
-
this.logger.debug('Getting fee info');
|
|
99
|
-
Validator_1.Validator.validateEVMAddress(evmProxyMsg.evmTargetAddress);
|
|
100
|
-
Validator_1.Validator.validateEVMAddresses(evmValidExecutors);
|
|
101
|
-
Validator_1.Validator.validateTVMAddresses(tvmValidExecutors);
|
|
47
|
+
const simulation = await this.operationTracker.simulateTACMessage(tacSimulationParams);
|
|
48
|
+
this.logger.debug(`TAC simulation ${simulation.simulationStatus ? 'success' : 'failed'}`);
|
|
49
|
+
const isRoundTrip = options.isRoundTrip ?? assets.length !== 0;
|
|
102
50
|
const crossChainLayer = this.config.TONParams.contractOpener.open(this.config.artifacts.ton.wrappers.CrossChainLayer.createFromAddress(ton_1.Address.parse(this.config.TONParams.crossChainLayerAddress)));
|
|
103
51
|
const fullStateCCL = await crossChainLayer.getFullData();
|
|
104
|
-
this.logger.debug(`Full state CCL: ${(0, Utils_1.formatObjectForLogging)(fullStateCCL)}`);
|
|
105
|
-
const tacSimulationBody = {
|
|
106
|
-
tacCallParams: {
|
|
107
|
-
arguments: evmProxyMsg.encodedParameters ?? '0x',
|
|
108
|
-
methodName: (0, Utils_1.formatSolidityMethodName)(evmProxyMsg.methodName),
|
|
109
|
-
target: evmProxyMsg.evmTargetAddress,
|
|
110
|
-
},
|
|
111
|
-
evmValidExecutors: evmValidExecutors,
|
|
112
|
-
tvmValidExecutors: tvmValidExecutors,
|
|
113
|
-
extraData: '0x',
|
|
114
|
-
shardsKey: transactionLinker.shardsKey,
|
|
115
|
-
tonAssets: assets.map((asset) => ({
|
|
116
|
-
amount: asset.rawAmount.toString(),
|
|
117
|
-
tokenAddress: asset.address || '',
|
|
118
|
-
assetType: asset.type,
|
|
119
|
-
})),
|
|
120
|
-
tonCaller: transactionLinker.caller,
|
|
121
|
-
calculateRollbackFee: calculateRollbackFee,
|
|
122
|
-
};
|
|
123
|
-
isRoundTrip = isRoundTrip ?? assets.length != 0;
|
|
124
|
-
this.logger.debug(`Is round trip: ${isRoundTrip}`);
|
|
125
|
-
const tacSimulationResult = await this.simulateTACMessage(tacSimulationBody);
|
|
126
|
-
this.logger.debug(`TAC simulation ${tacSimulationResult.simulationStatus ? 'success' : 'failed'}`);
|
|
127
|
-
const protocolFee = BigInt((0, ton_1.toNano)(fullStateCCL.tacProtocolFee)) +
|
|
128
|
-
BigInt(isRoundTrip) * BigInt((0, ton_1.toNano)(fullStateCCL.tonProtocolFee));
|
|
129
52
|
const feeParams = {
|
|
130
|
-
isRoundTrip
|
|
131
|
-
gasLimit:
|
|
132
|
-
protocolFee:
|
|
133
|
-
|
|
134
|
-
|
|
53
|
+
isRoundTrip,
|
|
54
|
+
gasLimit: simulation.simulationStatus ? simulation.estimatedGas : 0n,
|
|
55
|
+
protocolFee: BigInt((0, ton_1.toNano)(fullStateCCL.tacProtocolFee)) +
|
|
56
|
+
BigInt(isRoundTrip) * BigInt((0, ton_1.toNano)(fullStateCCL.tonProtocolFee)),
|
|
57
|
+
evmExecutorFee: BigInt(simulation.suggestedTacExecutionFee),
|
|
58
|
+
tvmExecutorFee: BigInt(simulation.suggestedTonExecutionFee) * BigInt(isRoundTrip),
|
|
135
59
|
};
|
|
136
|
-
if (!
|
|
137
|
-
|
|
138
|
-
this.logger.info('Force send is true, returning fee params');
|
|
139
|
-
return {
|
|
140
|
-
feeParams,
|
|
141
|
-
simulation: tacSimulationResult,
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
throw tacSimulationResult;
|
|
60
|
+
if (!simulation.simulationStatus && !allowSimulationError) {
|
|
61
|
+
throw simulation;
|
|
145
62
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
this.logger.debug('Getting transaction simulation info');
|
|
151
|
-
Validator_1.Validator.validateEVMAddress(evmProxyMsg.evmTargetAddress);
|
|
152
|
-
const aggregatedData = await (0, Utils_1.aggregateTokens)(assets);
|
|
153
|
-
const transactionLinkerShardCount = aggregatedData.jettons.length == 0 ? 1 : aggregatedData.jettons.length;
|
|
154
|
-
this.logger.debug(`Transaction linker shard count: ${transactionLinkerShardCount}`);
|
|
155
|
-
const transactionLinker = (0, Utils_1.generateTransactionLinker)(sender.getSenderAddress(), transactionLinkerShardCount);
|
|
156
|
-
this.logger.debug(`Transaction linker: ${(0, Utils_1.formatObjectForLogging)(transactionLinker)}`);
|
|
157
|
-
return await this.getFeeInfo(evmProxyMsg, transactionLinker, assets ?? []);
|
|
158
|
-
}
|
|
159
|
-
async getSimulationInfoForTransaction(evmProxyMsg, transactionLinker, assets, allowSimulationError = false, isRoundTrip, evmValidExecutors, tvmValidExecutors, calculateRollbackFee) {
|
|
160
|
-
return await this.getFeeInfo(evmProxyMsg, transactionLinker, assets, allowSimulationError, isRoundTrip, evmValidExecutors, tvmValidExecutors, calculateRollbackFee);
|
|
63
|
+
if (allowSimulationError && !simulation.simulationStatus) {
|
|
64
|
+
this.logger.info('Simulation failed but allowSimulationError is true, returning partial fee params');
|
|
65
|
+
}
|
|
66
|
+
return { feeParams, simulation };
|
|
161
67
|
}
|
|
162
68
|
}
|
|
163
69
|
exports.Simulator = Simulator;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Wallet } from 'ethers';
|
|
2
|
+
import { Asset, IConfiguration, ILogger, IOperationTracker, ITACTransactionManager } from '../interfaces';
|
|
3
|
+
export declare class TACTransactionManager implements ITACTransactionManager {
|
|
4
|
+
private readonly config;
|
|
5
|
+
private readonly operationTracker;
|
|
6
|
+
private readonly logger;
|
|
7
|
+
constructor(config: IConfiguration, operationTracker: IOperationTracker, logger?: ILogger);
|
|
8
|
+
private approveAsset;
|
|
9
|
+
bridgeTokensToTON(signer: Wallet, value: bigint, tonTarget: string, assets?: Asset[], tvmExecutorFee?: bigint, tvmValidExecutors?: string[]): Promise<string>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TACTransactionManager = void 0;
|
|
4
|
+
const assets_1 = require("../assets");
|
|
5
|
+
const Struct_1 = require("../structs/Struct");
|
|
6
|
+
const Consts_1 = require("./Consts");
|
|
7
|
+
const Logger_1 = require("./Logger");
|
|
8
|
+
const Utils_1 = require("./Utils");
|
|
9
|
+
const Validator_1 = require("./Validator");
|
|
10
|
+
class TACTransactionManager {
|
|
11
|
+
constructor(config, operationTracker, logger = new Logger_1.NoopLogger()) {
|
|
12
|
+
this.config = config;
|
|
13
|
+
this.operationTracker = operationTracker;
|
|
14
|
+
this.logger = logger;
|
|
15
|
+
}
|
|
16
|
+
async approveAsset(asset, signer, spenderAddress) {
|
|
17
|
+
const evmAddress = await asset.getEVMAddress();
|
|
18
|
+
if (asset.type === Struct_1.AssetType.FT) {
|
|
19
|
+
this.logger.debug(`Approving FT ${evmAddress} for ${spenderAddress}`);
|
|
20
|
+
const contract = this.config.artifacts.tac.wrappers.ERC20FactoryTAC.connect(evmAddress, this.config.TACParams.provider);
|
|
21
|
+
const tx = await contract.connect(signer).approve(spenderAddress, asset.rawAmount);
|
|
22
|
+
await tx.wait();
|
|
23
|
+
}
|
|
24
|
+
else if (asset.type === Struct_1.AssetType.NFT) {
|
|
25
|
+
this.logger.debug(`Approving NFT ${evmAddress} for ${spenderAddress}`);
|
|
26
|
+
const contract = this.config.artifacts.tac.wrappers.ERC721FactoryTAC.connect(evmAddress, this.config.TACParams.provider);
|
|
27
|
+
const tx = await contract.connect(signer).approve(spenderAddress, asset.addresses.index);
|
|
28
|
+
await tx.wait();
|
|
29
|
+
}
|
|
30
|
+
this.logger.debug(`Approved ${evmAddress} for ${spenderAddress}`);
|
|
31
|
+
}
|
|
32
|
+
async bridgeTokensToTON(signer, value, tonTarget, assets = [], tvmExecutorFee, tvmValidExecutors) {
|
|
33
|
+
this.logger.debug('Bridging tokens to TON');
|
|
34
|
+
Validator_1.Validator.validateTVMAddress(tonTarget);
|
|
35
|
+
// Add native TAC asset if value > 0
|
|
36
|
+
if (value > 0n) {
|
|
37
|
+
const nativeTacAsset = (await assets_1.AssetFactory.from(this.config, {
|
|
38
|
+
address: await this.config.nativeTACAddress(),
|
|
39
|
+
tokenType: Struct_1.AssetType.FT,
|
|
40
|
+
})).withRawAmount(value);
|
|
41
|
+
assets = [...assets, nativeTacAsset];
|
|
42
|
+
}
|
|
43
|
+
// Calculate executor fee if not provided
|
|
44
|
+
if (!tvmExecutorFee) {
|
|
45
|
+
const feeParams = {
|
|
46
|
+
tonAssets: (0, Utils_1.mapAssetsToTonAssets)(assets),
|
|
47
|
+
feeSymbol: Consts_1.TAC_SYMBOL,
|
|
48
|
+
tvmValidExecutors: tvmValidExecutors ?? [],
|
|
49
|
+
};
|
|
50
|
+
const suggestedFee = await this.operationTracker.getTVMExecutorFee(feeParams);
|
|
51
|
+
this.logger.debug(`Suggested TON executor fee: ${(0, Utils_1.formatObjectForLogging)(suggestedFee)}`);
|
|
52
|
+
tvmExecutorFee = BigInt(suggestedFee.inTAC);
|
|
53
|
+
}
|
|
54
|
+
// Approve all assets
|
|
55
|
+
const crossChainLayerAddress = await this.config.TACParams.crossChainLayer.getAddress();
|
|
56
|
+
await Promise.all(assets.map(asset => this.approveAsset(asset, signer, crossChainLayerAddress)));
|
|
57
|
+
const protocolFee = await this.config.TACParams.crossChainLayer.getProtocolFee();
|
|
58
|
+
const shardsKey = BigInt(Math.round(Math.random() * 1e18));
|
|
59
|
+
this.logger.debug(`Shards key: ${shardsKey}, Protocol fee: ${protocolFee}`);
|
|
60
|
+
// Prepare bridge data
|
|
61
|
+
const [toBridge, toBridgeNFT] = await Promise.all([
|
|
62
|
+
Promise.all(assets.filter(a => a.type === Struct_1.AssetType.FT).map(async (a) => ({
|
|
63
|
+
evmAddress: await a.getEVMAddress(),
|
|
64
|
+
amount: a.rawAmount,
|
|
65
|
+
}))),
|
|
66
|
+
Promise.all(assets.filter(a => a.type === Struct_1.AssetType.NFT).map(async (a) => ({
|
|
67
|
+
evmAddress: await a.getEVMAddress(),
|
|
68
|
+
amount: 1n,
|
|
69
|
+
tokenId: a.addresses.index,
|
|
70
|
+
}))),
|
|
71
|
+
]);
|
|
72
|
+
const outMessage = {
|
|
73
|
+
shardsKey,
|
|
74
|
+
tvmTarget: tonTarget,
|
|
75
|
+
tvmPayload: '',
|
|
76
|
+
tvmProtocolFee: protocolFee,
|
|
77
|
+
tvmExecutorFee,
|
|
78
|
+
tvmValidExecutors: this.config.getTrustedTONExecutors,
|
|
79
|
+
toBridge,
|
|
80
|
+
toBridgeNFT,
|
|
81
|
+
};
|
|
82
|
+
const totalValue = value + BigInt(outMessage.tvmProtocolFee) + BigInt(outMessage.tvmExecutorFee);
|
|
83
|
+
this.logger.debug(`Total value: ${totalValue}`);
|
|
84
|
+
const tx = await this.config.TACParams.crossChainLayer
|
|
85
|
+
.connect(signer)
|
|
86
|
+
.sendMessage(1n, this.config.artifacts.tac.utils.encodeOutMessageV1(outMessage), { value: totalValue });
|
|
87
|
+
await tx.wait();
|
|
88
|
+
this.logger.debug(`Transaction hash: ${tx.hash}`);
|
|
89
|
+
return tx.hash;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
exports.TACTransactionManager = TACTransactionManager;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { IConfiguration, ILogger, IOperationTracker, ISimulator, ITONTransactionManager } from '../interfaces';
|
|
2
|
+
import type { SenderAbstraction } from '../sender';
|
|
3
|
+
import { CrosschainTx, EvmProxyMsg, OperationIdsByShardsKey, TransactionLinkerWithOperationId, WaitOptions } from '../structs/Struct';
|
|
4
|
+
export declare class TONTransactionManager implements ITONTransactionManager {
|
|
5
|
+
private readonly config;
|
|
6
|
+
private readonly simulator;
|
|
7
|
+
private readonly operationTracker;
|
|
8
|
+
private readonly logger;
|
|
9
|
+
constructor(config: IConfiguration, simulator: ISimulator, operationTracker: IOperationTracker, logger?: ILogger);
|
|
10
|
+
private buildFeeParams;
|
|
11
|
+
private prepareCrossChainTransaction;
|
|
12
|
+
private generateCrossChainMessages;
|
|
13
|
+
sendCrossChainTransaction(evmProxyMsg: EvmProxyMsg, sender: SenderAbstraction, tx: CrosschainTx, waitOptions?: WaitOptions<string>): Promise<TransactionLinkerWithOperationId>;
|
|
14
|
+
sendCrossChainTransactions(sender: SenderAbstraction, txs: CrosschainTx[], waitOptions?: WaitOptions<OperationIdsByShardsKey>): Promise<TransactionLinkerWithOperationId[]>;
|
|
15
|
+
private prepareBatchTransactions;
|
|
16
|
+
private waitForOperationIds;
|
|
17
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TONTransactionManager = void 0;
|
|
4
|
+
const assets_1 = require("../assets");
|
|
5
|
+
const errors_1 = require("../errors");
|
|
6
|
+
const Consts_1 = require("./Consts");
|
|
7
|
+
const Logger_1 = require("./Logger");
|
|
8
|
+
const Utils_1 = require("./Utils");
|
|
9
|
+
const Validator_1 = require("./Validator");
|
|
10
|
+
class TONTransactionManager {
|
|
11
|
+
constructor(config, simulator, operationTracker, logger = new Logger_1.NoopLogger()) {
|
|
12
|
+
this.config = config;
|
|
13
|
+
this.simulator = simulator;
|
|
14
|
+
this.operationTracker = operationTracker;
|
|
15
|
+
this.logger = logger;
|
|
16
|
+
}
|
|
17
|
+
async buildFeeParams(options, evmProxyMsg, sender, tx) {
|
|
18
|
+
const { withoutSimulation, protocolFee, evmExecutorFee, tvmExecutorFee, isRoundTrip } = options;
|
|
19
|
+
if (withoutSimulation) {
|
|
20
|
+
if (protocolFee === undefined || evmExecutorFee === undefined) {
|
|
21
|
+
throw errors_1.missingFeeParamsError;
|
|
22
|
+
}
|
|
23
|
+
if (isRoundTrip && tvmExecutorFee === undefined) {
|
|
24
|
+
throw errors_1.missingTvmExecutorFeeError;
|
|
25
|
+
}
|
|
26
|
+
if (!evmProxyMsg.gasLimit) {
|
|
27
|
+
throw errors_1.missingGasLimitError;
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
protocolFee,
|
|
31
|
+
evmExecutorFee,
|
|
32
|
+
tvmExecutorFee: tvmExecutorFee || 0n,
|
|
33
|
+
gasLimit: evmProxyMsg.gasLimit,
|
|
34
|
+
isRoundTrip: isRoundTrip || false,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const simulationResult = await this.simulator.getSimulationInfo(sender, tx);
|
|
38
|
+
if (!evmProxyMsg.gasLimit)
|
|
39
|
+
evmProxyMsg.gasLimit = simulationResult.feeParams.gasLimit;
|
|
40
|
+
return {
|
|
41
|
+
protocolFee: protocolFee ?? simulationResult.feeParams.protocolFee,
|
|
42
|
+
evmExecutorFee: evmExecutorFee ?? simulationResult.feeParams.evmExecutorFee,
|
|
43
|
+
tvmExecutorFee: simulationResult.feeParams.isRoundTrip && tvmExecutorFee !== undefined
|
|
44
|
+
? tvmExecutorFee
|
|
45
|
+
: simulationResult.feeParams.tvmExecutorFee,
|
|
46
|
+
gasLimit: evmProxyMsg.gasLimit ?? simulationResult.feeParams.gasLimit,
|
|
47
|
+
isRoundTrip: isRoundTrip ?? simulationResult.feeParams.isRoundTrip,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
async prepareCrossChainTransaction(evmProxyMsg, sender, assets, options, skipAssetsBalanceValidation = false) {
|
|
51
|
+
this.logger.debug('Preparing cross-chain transaction');
|
|
52
|
+
const caller = sender.getSenderAddress();
|
|
53
|
+
const { allowSimulationError = false, isRoundTrip = undefined, calculateRollbackFee = true, validateAssetsBalance = true } = options || {};
|
|
54
|
+
const { evmValidExecutors = [], tvmValidExecutors = [] } = options || {};
|
|
55
|
+
Validator_1.Validator.validateEVMAddress(evmProxyMsg.evmTargetAddress);
|
|
56
|
+
const aggregatedData = await (0, Utils_1.aggregateTokens)(assets);
|
|
57
|
+
Validator_1.Validator.validateEVMAddresses(evmValidExecutors);
|
|
58
|
+
Validator_1.Validator.validateTVMAddresses(tvmValidExecutors);
|
|
59
|
+
const shouldValidateAssets = validateAssetsBalance && !skipAssetsBalanceValidation;
|
|
60
|
+
if (shouldValidateAssets) {
|
|
61
|
+
await Promise.all([
|
|
62
|
+
...aggregatedData.jettons.map((jetton) => jetton.checkCanBeTransferredBy(caller)),
|
|
63
|
+
...aggregatedData.nfts.map((nft) => nft.checkCanBeTransferredBy(caller)),
|
|
64
|
+
aggregatedData.ton?.checkCanBeTransferredBy(caller),
|
|
65
|
+
].filter(Boolean));
|
|
66
|
+
}
|
|
67
|
+
const tokensLength = aggregatedData.jettons.length + aggregatedData.nfts.length;
|
|
68
|
+
const transactionLinker = (0, Utils_1.generateTransactionLinker)(caller, tokensLength || 1);
|
|
69
|
+
this.logger.debug(`Generated transaction linker: ${(0, Utils_1.formatObjectForLogging)(transactionLinker)}`);
|
|
70
|
+
const tacExecutors = evmValidExecutors.length ? evmValidExecutors : this.config.getTrustedTACExecutors;
|
|
71
|
+
const tonExecutors = tvmValidExecutors.length ? tvmValidExecutors : this.config.getTrustedTONExecutors;
|
|
72
|
+
const tx = {
|
|
73
|
+
evmProxyMsg,
|
|
74
|
+
assets: assets ?? [],
|
|
75
|
+
options: {
|
|
76
|
+
allowSimulationError,
|
|
77
|
+
isRoundTrip,
|
|
78
|
+
evmValidExecutors: tacExecutors,
|
|
79
|
+
tvmValidExecutors: tonExecutors,
|
|
80
|
+
calculateRollbackFee,
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
const feeParams = await this.buildFeeParams(options || {}, evmProxyMsg, sender, tx);
|
|
84
|
+
this.logger.debug(`Resulting fee params: ${(0, Utils_1.formatObjectForLogging)(feeParams)}`);
|
|
85
|
+
const validExecutors = {
|
|
86
|
+
tac: tacExecutors,
|
|
87
|
+
ton: tonExecutors,
|
|
88
|
+
};
|
|
89
|
+
const evmData = (0, Utils_1.buildEvmDataCell)(transactionLinker, evmProxyMsg, validExecutors);
|
|
90
|
+
const messages = await this.generateCrossChainMessages(caller, evmData, aggregatedData, feeParams);
|
|
91
|
+
return {
|
|
92
|
+
transaction: {
|
|
93
|
+
validUntil: Date.now() + Consts_1.FIFTEEN_MINUTES,
|
|
94
|
+
messages,
|
|
95
|
+
network: this.config.network,
|
|
96
|
+
},
|
|
97
|
+
transactionLinker,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
async generateCrossChainMessages(caller, evmData, aggregatedData, feeParams) {
|
|
101
|
+
this.logger.debug('Generating cross-chain messages');
|
|
102
|
+
const { jettons, nfts, ton = assets_1.TON.create(this.config) } = aggregatedData;
|
|
103
|
+
const totalAssets = [...jettons, ...nfts];
|
|
104
|
+
let crossChainTonAmount = ton.rawAmount;
|
|
105
|
+
let feeTonAmount = feeParams.protocolFee + feeParams.evmExecutorFee + feeParams.tvmExecutorFee;
|
|
106
|
+
this.logger.debug(`Crosschain ton amount: ${crossChainTonAmount}, Fee ton amount: ${feeTonAmount}`);
|
|
107
|
+
if (!totalAssets.length) {
|
|
108
|
+
return [
|
|
109
|
+
{
|
|
110
|
+
address: this.config.TONParams.crossChainLayerAddress,
|
|
111
|
+
value: crossChainTonAmount + feeTonAmount + Consts_1.TRANSACTION_TON_AMOUNT,
|
|
112
|
+
payload: await ton.generatePayload({ excessReceiver: caller, evmData, feeParams }),
|
|
113
|
+
},
|
|
114
|
+
];
|
|
115
|
+
}
|
|
116
|
+
const messages = [];
|
|
117
|
+
let currentFeeParams = feeParams;
|
|
118
|
+
for (const asset of totalAssets) {
|
|
119
|
+
const payload = await asset.generatePayload({
|
|
120
|
+
excessReceiver: caller,
|
|
121
|
+
evmData,
|
|
122
|
+
crossChainTonAmount,
|
|
123
|
+
forwardFeeTonAmount: feeTonAmount,
|
|
124
|
+
feeParams: currentFeeParams,
|
|
125
|
+
});
|
|
126
|
+
const address = asset instanceof assets_1.FT ? await asset.getUserWalletAddress(caller) : asset.address;
|
|
127
|
+
messages.push({
|
|
128
|
+
address,
|
|
129
|
+
value: crossChainTonAmount + feeTonAmount + Consts_1.TRANSACTION_TON_AMOUNT,
|
|
130
|
+
payload,
|
|
131
|
+
});
|
|
132
|
+
crossChainTonAmount = 0n;
|
|
133
|
+
feeTonAmount = 0n;
|
|
134
|
+
currentFeeParams = undefined;
|
|
135
|
+
}
|
|
136
|
+
this.logger.debug('Cross-chain messages generated successfully');
|
|
137
|
+
return messages;
|
|
138
|
+
}
|
|
139
|
+
async sendCrossChainTransaction(evmProxyMsg, sender, tx, waitOptions) {
|
|
140
|
+
const { transaction, transactionLinker } = await this.prepareCrossChainTransaction(evmProxyMsg, sender, tx.assets, tx.options);
|
|
141
|
+
await assets_1.TON.checkBalance(sender, this.config, [transaction]);
|
|
142
|
+
this.logger.debug(`Sending transaction: ${(0, Utils_1.formatObjectForLogging)(transactionLinker)}`);
|
|
143
|
+
const sendTransactionResult = await sender.sendShardTransaction(transaction, this.config.network, this.config.TONParams.contractOpener);
|
|
144
|
+
if (!waitOptions) {
|
|
145
|
+
return { sendTransactionResult, ...transactionLinker };
|
|
146
|
+
}
|
|
147
|
+
const operationId = await this.operationTracker.getOperationId(transactionLinker, {
|
|
148
|
+
...waitOptions,
|
|
149
|
+
successCheck: (id) => !!id,
|
|
150
|
+
logger: this.logger,
|
|
151
|
+
})
|
|
152
|
+
.catch((error) => {
|
|
153
|
+
this.logger.error(`Error while waiting for operation ID: ${error}`);
|
|
154
|
+
return undefined;
|
|
155
|
+
});
|
|
156
|
+
return { sendTransactionResult, operationId, ...transactionLinker };
|
|
157
|
+
}
|
|
158
|
+
async sendCrossChainTransactions(sender, txs, waitOptions) {
|
|
159
|
+
const caller = sender.getSenderAddress();
|
|
160
|
+
this.logger.debug(`Preparing ${txs.length} cross-chain transactions for ${caller}`);
|
|
161
|
+
const { transactions, transactionLinkers } = await this.prepareBatchTransactions(txs, sender);
|
|
162
|
+
await assets_1.TON.checkBalance(sender, this.config, transactions);
|
|
163
|
+
this.logger.debug(`Sending transactions: ${(0, Utils_1.formatObjectForLogging)(transactionLinkers)}`);
|
|
164
|
+
await sender.sendShardTransactions(transactions, this.config.network, this.config.TONParams.contractOpener);
|
|
165
|
+
return waitOptions
|
|
166
|
+
? await this.waitForOperationIds(transactionLinkers, caller, waitOptions)
|
|
167
|
+
: transactionLinkers;
|
|
168
|
+
}
|
|
169
|
+
async prepareBatchTransactions(txs, sender) {
|
|
170
|
+
const caller = sender.getSenderAddress();
|
|
171
|
+
const txsRequiringValidation = txs.filter((tx) => tx.options?.validateAssetsBalance ?? true);
|
|
172
|
+
if (txsRequiringValidation.length) {
|
|
173
|
+
// Aggregate only assets from txs that require validation and validate once per unique asset
|
|
174
|
+
const assetsToValidate = txsRequiringValidation.flatMap((tx) => tx.assets ?? []);
|
|
175
|
+
const aggregatedData = await (0, Utils_1.aggregateTokens)(assetsToValidate);
|
|
176
|
+
await Promise.all([
|
|
177
|
+
...aggregatedData.jettons.map((jetton) => jetton.checkCanBeTransferredBy(caller)),
|
|
178
|
+
...aggregatedData.nfts.map((nft) => nft.checkCanBeTransferredBy(caller)),
|
|
179
|
+
aggregatedData.ton?.checkCanBeTransferredBy(caller),
|
|
180
|
+
].filter(Boolean));
|
|
181
|
+
}
|
|
182
|
+
const results = await Promise.all(txs.map(({ evmProxyMsg, assets, options }) => this.prepareCrossChainTransaction(evmProxyMsg, sender, assets, options, true)));
|
|
183
|
+
return {
|
|
184
|
+
transactions: results.map((r) => r.transaction),
|
|
185
|
+
transactionLinkers: results.map((r) => r.transactionLinker),
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
async waitForOperationIds(transactionLinkers, caller, waitOptions) {
|
|
189
|
+
this.logger.debug(`Waiting for operation IDs`);
|
|
190
|
+
try {
|
|
191
|
+
const operationIds = await this.operationTracker.getOperationIdsByShardsKeys(transactionLinkers.map((linker) => linker.shardsKey), caller, {
|
|
192
|
+
...waitOptions,
|
|
193
|
+
logger: this.logger,
|
|
194
|
+
successCheck: (operationIds) => Object.keys(operationIds).length == transactionLinkers.length &&
|
|
195
|
+
Object.values(operationIds).every((ids) => ids.operationIds.length > 0),
|
|
196
|
+
});
|
|
197
|
+
this.logger.debug(`Operation IDs: ${(0, Utils_1.formatObjectForLogging)(operationIds)}`);
|
|
198
|
+
return transactionLinkers.map((linker) => ({
|
|
199
|
+
...linker,
|
|
200
|
+
operationId: operationIds[linker.shardsKey].operationIds.at(0),
|
|
201
|
+
}));
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
this.logger.error(`Error while waiting for operation IDs: ${error}`);
|
|
205
|
+
return transactionLinkers;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
exports.TONTransactionManager = TONTransactionManager;
|
package/dist/sdk/TacSdk.d.ts
CHANGED
|
@@ -1,28 +1,33 @@
|
|
|
1
|
+
import { AgnosticProxySDK } from '@tonappchain/agnostic-sdk';
|
|
1
2
|
import { Wallet } from 'ethers';
|
|
2
3
|
import { FT, NFT } from '../assets';
|
|
3
|
-
import {
|
|
4
|
+
import { IConfiguration, ILogger, IOperationTracker, ITacSDK } from '../interfaces';
|
|
4
5
|
import type { SenderAbstraction } from '../sender';
|
|
5
|
-
import { AssetFromFTArg, AssetFromNFTCollectionArg, AssetFromNFTItemArg, CrossChainTransactionOptions, CrosschainTx, EVMAddress, EvmProxyMsg, ExecutionFeeEstimationResult, NFTAddressType, NFTItemData, OperationIdsByShardsKey, SDKParams,
|
|
6
|
+
import { AssetFromFTArg, AssetFromNFTCollectionArg, AssetFromNFTItemArg, AssetLike, CrossChainTransactionOptions, CrosschainTx, CrosschainTxWithAssetLike, EVMAddress, EvmProxyMsg, ExecutionFeeEstimationResult, NFTAddressType, NFTItemData, OperationIdsByShardsKey, SDKParams, SuggestedTVMExecutorFee, TACSimulationParams, TACSimulationResult, TransactionLinkerWithOperationId, TVMAddress, UserWalletBalanceExtended, WaitOptions } from '../structs/Struct';
|
|
6
7
|
import { JettonMasterData } from '../wrappers/JettonMaster';
|
|
7
8
|
export declare class TacSdk implements ITacSDK {
|
|
8
9
|
readonly config: IConfiguration;
|
|
10
|
+
readonly operationTracker: IOperationTracker;
|
|
9
11
|
private readonly simulator;
|
|
10
|
-
private readonly
|
|
12
|
+
private readonly tonTransactionManager;
|
|
13
|
+
private readonly tacTransactionManager;
|
|
11
14
|
private constructor();
|
|
12
15
|
static create(sdkParams: SDKParams, logger?: ILogger): Promise<TacSdk>;
|
|
13
16
|
closeConnections(): unknown;
|
|
14
17
|
get nativeTONAddress(): string;
|
|
18
|
+
getAgnosticProxySDK(agnosticProxyAddress?: string, smartAccountFactoryAddress?: string): AgnosticProxySDK;
|
|
19
|
+
getSmartAccountAddressForTvmWallet(tvmWallet: string, applicationAddress: string): Promise<string>;
|
|
15
20
|
nativeTACAddress(): Promise<string>;
|
|
16
21
|
get getTrustedTACExecutors(): string[];
|
|
17
22
|
get getTrustedTONExecutors(): string[];
|
|
18
|
-
|
|
19
|
-
getTVMExecutorFeeInfo(assets:
|
|
20
|
-
sendCrossChainTransaction(evmProxyMsg: EvmProxyMsg, sender: SenderAbstraction, assets?:
|
|
21
|
-
sendCrossChainTransactions(sender: SenderAbstraction, txs:
|
|
22
|
-
bridgeTokensToTON(signer: Wallet, value: bigint, tonTarget: string, assets?:
|
|
23
|
+
getSimulationInfo(evmProxyMsg: EvmProxyMsg, sender: SenderAbstraction, assets?: AssetLike[], options?: CrossChainTransactionOptions): Promise<ExecutionFeeEstimationResult>;
|
|
24
|
+
getTVMExecutorFeeInfo(assets: AssetLike[], feeSymbol: string, tvmValidExecutors?: string[]): Promise<SuggestedTVMExecutorFee>;
|
|
25
|
+
sendCrossChainTransaction(evmProxyMsg: EvmProxyMsg, sender: SenderAbstraction, assets?: AssetLike[], options?: CrossChainTransactionOptions, waitOptions?: WaitOptions<string>): Promise<TransactionLinkerWithOperationId>;
|
|
26
|
+
sendCrossChainTransactions(sender: SenderAbstraction, txs: CrosschainTxWithAssetLike[], waitOptions?: WaitOptions<OperationIdsByShardsKey>): Promise<TransactionLinkerWithOperationId[]>;
|
|
27
|
+
bridgeTokensToTON(signer: Wallet, value: bigint, tonTarget: string, assets?: AssetLike[], tvmExecutorFee?: bigint, tvmValidExecutors?: string[]): Promise<string>;
|
|
23
28
|
isContractDeployedOnTVM(address: string): Promise<boolean>;
|
|
24
|
-
simulateTACMessage(req:
|
|
25
|
-
simulateTransactions(sender: SenderAbstraction, txs: CrosschainTx[]): Promise<
|
|
29
|
+
simulateTACMessage(req: TACSimulationParams): Promise<TACSimulationResult>;
|
|
30
|
+
simulateTransactions(sender: SenderAbstraction, txs: CrosschainTx[]): Promise<ExecutionFeeEstimationResult[]>;
|
|
26
31
|
getAsset(args: AssetFromFTArg): Promise<FT>;
|
|
27
32
|
getAsset(args: AssetFromNFTCollectionArg): Promise<NFT>;
|
|
28
33
|
getAsset(args: AssetFromNFTItemArg): Promise<NFT>;
|
|
@@ -37,4 +42,5 @@ export declare class TacSdk implements ITacSDK {
|
|
|
37
42
|
getTVMTokenAddress(evmTokenAddress: string): Promise<string>;
|
|
38
43
|
getTVMNFTAddress(evmNFTAddress: string, tokenId?: number | bigint): Promise<string>;
|
|
39
44
|
getEVMNFTAddress(tvmNFTAddress: string, addressType: NFTAddressType): Promise<string>;
|
|
45
|
+
getOperationTracker(): IOperationTracker;
|
|
40
46
|
}
|