@tonappchain/sdk 0.7.2 → 0.7.3-rc1
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/LICENSE +20 -20
- package/README.md +203 -199
- package/dist/src/adapters/BaseContractOpener.js +8 -5
- package/dist/src/assets/AssetFactory.d.ts +4 -1
- package/dist/src/assets/AssetFactory.js +4 -0
- package/dist/src/assets/FT.d.ts +3 -3
- package/dist/src/assets/FT.js +28 -15
- package/dist/src/assets/NFT.d.ts +3 -3
- package/dist/src/assets/NFT.js +10 -4
- package/dist/src/assets/TAC.d.ts +24 -0
- package/dist/src/assets/TAC.js +74 -0
- package/dist/src/assets/TON.d.ts +3 -3
- package/dist/src/assets/TON.js +12 -5
- package/dist/src/assets/index.d.ts +1 -0
- package/dist/src/assets/index.js +3 -1
- package/dist/src/errors/index.d.ts +1 -1
- package/dist/src/errors/index.js +6 -1
- package/dist/src/errors/instances.d.ts +4 -0
- package/dist/src/errors/instances.js +7 -1
- package/dist/src/interfaces/Asset.d.ts +11 -6
- package/dist/src/interfaces/IConfiguration.d.ts +1 -3
- package/dist/src/interfaces/ILiteSequencerClient.d.ts +9 -1
- package/dist/src/interfaces/IOperationTracker.d.ts +20 -1
- package/dist/src/interfaces/ITACTransactionManager.d.ts +19 -1
- package/dist/src/interfaces/ITacSDK.d.ts +29 -8
- package/dist/src/sdk/Consts.d.ts +4 -0
- package/dist/src/sdk/Consts.js +5 -1
- package/dist/src/sdk/LiteSequencerClient.d.ts +3 -1
- package/dist/src/sdk/LiteSequencerClient.js +37 -0
- package/dist/src/sdk/OperationTracker.d.ts +4 -1
- package/dist/src/sdk/OperationTracker.js +58 -0
- package/dist/src/sdk/TACTransactionManager.d.ts +3 -0
- package/dist/src/sdk/TACTransactionManager.js +170 -34
- package/dist/src/sdk/TONTransactionManager.js +28 -6
- package/dist/src/sdk/TacSdk.d.ts +4 -3
- package/dist/src/sdk/TacSdk.js +4 -0
- package/dist/src/sdk/Utils.d.ts +2 -1
- package/dist/src/sdk/Utils.js +4 -0
- package/dist/src/structs/InternalStruct.d.ts +35 -1
- package/dist/src/structs/Struct.d.ts +155 -5
- package/dist/src/structs/Struct.js +12 -1
- package/dist/src/wrappers/ContentUtils.d.ts +36 -13
- package/dist/src/wrappers/ContentUtils.js +197 -98
- package/package.json +121 -121
|
@@ -37,6 +37,11 @@ export declare enum OperationType {
|
|
|
37
37
|
TAC_TON = "TAC-TON",
|
|
38
38
|
UNKNOWN = "UNKNOWN"
|
|
39
39
|
}
|
|
40
|
+
export type OperationTypeV2 = OperationType.TON_TAC_TON | OperationType.TON_TAC | OperationType.TAC_TON;
|
|
41
|
+
export declare enum OperationExecutionStatus {
|
|
42
|
+
SUCCESS = "success",
|
|
43
|
+
FAILED = "failed"
|
|
44
|
+
}
|
|
40
45
|
export type TACParams = {
|
|
41
46
|
/**
|
|
42
47
|
* Provider for TAC side. Use your own provider for tests or to increase ratelimit
|
|
@@ -120,6 +125,16 @@ export type TransactionLinker = {
|
|
|
120
125
|
export type TransactionLinkerWithOperationId = TransactionLinker & {
|
|
121
126
|
operationId?: string;
|
|
122
127
|
};
|
|
128
|
+
export type TACCrossChainTransactionResult = {
|
|
129
|
+
/**
|
|
130
|
+
* Hash of the TAC transaction
|
|
131
|
+
*/
|
|
132
|
+
txHash: string;
|
|
133
|
+
/**
|
|
134
|
+
* Crosschain operation id
|
|
135
|
+
*/
|
|
136
|
+
operationId?: string;
|
|
137
|
+
};
|
|
123
138
|
export type TONAsset = {
|
|
124
139
|
amount: string;
|
|
125
140
|
tokenAddress: string;
|
|
@@ -225,7 +240,19 @@ export type ExecutionStages = {
|
|
|
225
240
|
operationType: OperationType;
|
|
226
241
|
metaInfo: MetaInfo;
|
|
227
242
|
} & Record<StageName, ProfilingStageData>;
|
|
243
|
+
export type OperationTypeV2Info = {
|
|
244
|
+
operationType: OperationTypeV2;
|
|
245
|
+
finalized: boolean;
|
|
246
|
+
};
|
|
247
|
+
export type ExecutionStagesV2 = {
|
|
248
|
+
operationType: OperationTypeV2;
|
|
249
|
+
metaInfo: MetaInfo;
|
|
250
|
+
finalized: boolean;
|
|
251
|
+
status: OperationExecutionStatus;
|
|
252
|
+
rollback: boolean;
|
|
253
|
+
} & Record<StageName, ProfilingStageData>;
|
|
228
254
|
export type ExecutionStagesByOperationId = Record<string, ExecutionStages>;
|
|
255
|
+
export type ExecutionStagesV2ByOperationId = Record<string, ExecutionStagesV2>;
|
|
229
256
|
export type StatusInfosByOperationId = Record<string, StatusInfo>;
|
|
230
257
|
export type OperationIds = {
|
|
231
258
|
operationIds: string[];
|
|
@@ -288,35 +315,153 @@ export type FeeParams = {
|
|
|
288
315
|
};
|
|
289
316
|
export type evmDataBuilder = (transactionLinker: TransactionLinker, evmProxyMsg: EvmProxyMsg, validExecutors: ValidExecutors) => Cell;
|
|
290
317
|
export type CrossChainTransactionOptions = {
|
|
318
|
+
/**
|
|
319
|
+
* Allows sending to continue even when TAC simulation returns an unsuccessful result.
|
|
320
|
+
* @default false
|
|
321
|
+
*/
|
|
291
322
|
allowSimulationError?: boolean;
|
|
292
323
|
/**
|
|
293
|
-
* If true,
|
|
324
|
+
* If true, validates the initial TON transaction tree right after sending the external message.
|
|
325
|
+
* This checks that the outbound TON transaction was actually executed before the SDK starts
|
|
326
|
+
* waiting for cross-chain tracking data.
|
|
294
327
|
* @default true
|
|
295
328
|
*/
|
|
296
329
|
ensureTxExecuted?: boolean;
|
|
297
330
|
/**
|
|
298
331
|
* If true, validates explicitly provided fee params against suggested values.
|
|
332
|
+
* Disable only when you intentionally want to send with custom fees.
|
|
299
333
|
* @default true
|
|
300
334
|
*/
|
|
301
335
|
shouldValidateFees?: boolean;
|
|
336
|
+
/**
|
|
337
|
+
* Forces the SDK to treat the operation as round-trip TON -> TAC -> TON.
|
|
338
|
+
* When omitted, the SDK derives this automatically from simulation results and attached assets.
|
|
339
|
+
*/
|
|
302
340
|
isRoundTrip?: boolean;
|
|
341
|
+
/**
|
|
342
|
+
* Explicit override for the protocol fee included in the transaction.
|
|
343
|
+
* Normally this is taken from simulation or on-chain configuration.
|
|
344
|
+
*/
|
|
303
345
|
protocolFee?: bigint;
|
|
346
|
+
/**
|
|
347
|
+
* Optional whitelist of TAC-side executor addresses allowed to execute the TAC part of the operation.
|
|
348
|
+
* When omitted, trusted executors from SDK configuration are used.
|
|
349
|
+
*/
|
|
304
350
|
evmValidExecutors?: string[];
|
|
351
|
+
/**
|
|
352
|
+
* Explicit fee paid to the TAC-side executor.
|
|
353
|
+
* Normally this is filled from simulation unless you provide custom manual fee params.
|
|
354
|
+
*/
|
|
305
355
|
evmExecutorFee?: bigint;
|
|
356
|
+
/**
|
|
357
|
+
* Optional whitelist of TON-side executor addresses allowed to execute the TON part of the operation.
|
|
358
|
+
* When omitted, trusted executors from SDK configuration are used.
|
|
359
|
+
*/
|
|
306
360
|
tvmValidExecutors?: string[];
|
|
361
|
+
/**
|
|
362
|
+
* Explicit fee paid to the TON-side executor for round-trip operations.
|
|
363
|
+
* Required in manual mode when `withoutSimulation` is true and `isRoundTrip` is true.
|
|
364
|
+
*/
|
|
307
365
|
tvmExecutorFee?: bigint;
|
|
366
|
+
/**
|
|
367
|
+
* Controls whether rollback fee should be included in TAC simulation.
|
|
368
|
+
* Keep enabled unless you intentionally want estimation without rollback costs.
|
|
369
|
+
* @default true
|
|
370
|
+
*/
|
|
308
371
|
calculateRollbackFee?: boolean;
|
|
372
|
+
/**
|
|
373
|
+
* Skips simulation entirely and uses only manually provided fee params.
|
|
374
|
+
* In this mode you must provide `protocolFee`, `evmExecutorFee`, and `evmProxyMsg.gasLimit`.
|
|
375
|
+
* If `isRoundTrip` is true, `tvmExecutorFee` is also required.
|
|
376
|
+
* @default false
|
|
377
|
+
*/
|
|
309
378
|
withoutSimulation?: boolean;
|
|
379
|
+
/**
|
|
380
|
+
* If true, validates that the sender currently owns or holds enough assets before building
|
|
381
|
+
* the cross-chain transaction payloads.
|
|
382
|
+
* Disable only if you already validated balances externally or need to skip this network call.
|
|
383
|
+
* @default true
|
|
384
|
+
*/
|
|
310
385
|
validateAssetsBalance?: boolean;
|
|
386
|
+
/**
|
|
387
|
+
* If true, waits until the operation ID becomes available after sending the TON transaction.
|
|
388
|
+
* Disable when you only need the raw send result and do not want any tracker polling.
|
|
389
|
+
* @default true
|
|
390
|
+
*/
|
|
311
391
|
waitOperationId?: boolean;
|
|
392
|
+
/**
|
|
393
|
+
* Retry policy used while resolving the operation ID.
|
|
394
|
+
* Use this to control timeout, attempts, delay, logger, callbacks, and success criteria.
|
|
395
|
+
*/
|
|
312
396
|
waitOptions?: WaitOptions<string>;
|
|
397
|
+
/**
|
|
398
|
+
* If true, waits for full cross-chain finalization after the operation ID is known:
|
|
399
|
+
* the SDK waits for a final operation type, fetches stage profiling, and validates
|
|
400
|
+
* TON transaction trees for `executedInTON` transactions when they exist.
|
|
401
|
+
* @default true
|
|
402
|
+
*/
|
|
403
|
+
waitFinalization?: boolean;
|
|
404
|
+
/**
|
|
405
|
+
* Wait options used to finalize the cross-chain operation after operationId resolution.
|
|
406
|
+
* Use this to configure retries, delay, logger, successCheck, and onSuccess callback
|
|
407
|
+
* for stage profiling retrieval and post-processing.
|
|
408
|
+
*/
|
|
409
|
+
finalizationWaitOptions?: WaitOptions<ExecutionStages>;
|
|
410
|
+
/**
|
|
411
|
+
* Custom builder for the EVM payload cell embedded into the TON message.
|
|
412
|
+
* Override only for advanced integrations that need non-standard TAC header encoding.
|
|
413
|
+
* By default the SDK uses `buildEvmDataCell`.
|
|
414
|
+
*/
|
|
313
415
|
evmDataBuilder?: evmDataBuilder;
|
|
314
416
|
};
|
|
315
|
-
export type BatchCrossChainTransactionOptions = Omit<CrossChainTransactionOptions, 'waitOperationId' | 'waitOptions' | 'ensureTxExecuted'>;
|
|
417
|
+
export type BatchCrossChainTransactionOptions = Omit<CrossChainTransactionOptions, 'waitOperationId' | 'waitOptions' | 'ensureTxExecuted' | 'waitFinalization' | 'finalizationWaitOptions'>;
|
|
316
418
|
export type CrossChainTransactionsOptions = {
|
|
317
419
|
waitOperationIds?: boolean;
|
|
318
420
|
waitOptions?: WaitOptions<OperationIdsByShardsKey>;
|
|
319
421
|
};
|
|
422
|
+
export type BridgeTokensToTONOptions = {
|
|
423
|
+
/**
|
|
424
|
+
* Optional explicit TON-side executor fee.
|
|
425
|
+
* When omitted, the SDK calls OperationTracker.getTVMExecutorFee().
|
|
426
|
+
*/
|
|
427
|
+
tvmExecutorFee?: bigint;
|
|
428
|
+
/**
|
|
429
|
+
* Optional whitelist of allowed TVM executors.
|
|
430
|
+
* Used both for fee estimation and for the out message itself.
|
|
431
|
+
*/
|
|
432
|
+
tvmValidExecutors?: string[];
|
|
433
|
+
/**
|
|
434
|
+
* If true, validates balances / ownership of non-native TAC assets before approvals and sending.
|
|
435
|
+
* Native TAC amount is still covered separately by the overall TAC balance sufficiency check.
|
|
436
|
+
* Disable only if you already validated balances externally or need to skip these RPC calls.
|
|
437
|
+
* @default true
|
|
438
|
+
*/
|
|
439
|
+
validateAssetsBalance?: boolean;
|
|
440
|
+
/**
|
|
441
|
+
* If true, waits until the operation ID becomes available after sending the TON transaction.
|
|
442
|
+
* Disable when you only need the raw send result and do not want any tracker polling.
|
|
443
|
+
* @default true
|
|
444
|
+
*/
|
|
445
|
+
waitOperationId?: boolean;
|
|
446
|
+
/**
|
|
447
|
+
* Retry policy used while resolving the operation ID.
|
|
448
|
+
* Use this to control timeout, attempts, delay, logger, callbacks, and success criteria.
|
|
449
|
+
*/
|
|
450
|
+
waitOptions?: WaitOptions<string>;
|
|
451
|
+
/**
|
|
452
|
+
* Wait options used to finalize the cross-chain operation after operationId resolution.
|
|
453
|
+
* Use this to configure retries, delay, logger, successCheck, and onSuccess callback
|
|
454
|
+
* for stage profiling retrieval and post-processing.
|
|
455
|
+
*/
|
|
456
|
+
waitFinalization?: boolean;
|
|
457
|
+
/**
|
|
458
|
+
* Wait options used to finalize the TAC->TON operation after operationId resolution.
|
|
459
|
+
* The SDK waits until the operation reaches a final operation type, fetches stage profiling,
|
|
460
|
+
* and, if profiling contains EXECUTED_IN_TON transactions, validates their TON transaction tree.
|
|
461
|
+
* This option is used only by sendCrossChainTransactionToTON and is ignored by bridgeTokensToTON.
|
|
462
|
+
*/
|
|
463
|
+
finalizationWaitOptions?: WaitOptions<ExecutionStages>;
|
|
464
|
+
};
|
|
320
465
|
export type ExecutionFeeEstimationResult = {
|
|
321
466
|
feeParams: FeeParams;
|
|
322
467
|
simulation?: TACSimulationResult;
|
|
@@ -461,6 +606,11 @@ export type TacGasPrice = {
|
|
|
461
606
|
fast: number;
|
|
462
607
|
slow: number;
|
|
463
608
|
};
|
|
609
|
+
export declare enum TransactionTreeDirection {
|
|
610
|
+
FORWARD = "forward",
|
|
611
|
+
BACKWARD = "backward",
|
|
612
|
+
BOTH = "both"
|
|
613
|
+
}
|
|
464
614
|
/**
|
|
465
615
|
* Parameters for tracking and validating transaction trees
|
|
466
616
|
*/
|
|
@@ -491,10 +641,10 @@ export type TrackTransactionTreeParams = {
|
|
|
491
641
|
* Direction to search the transaction tree:
|
|
492
642
|
* - 'forward': only search children (outgoing messages)
|
|
493
643
|
* - 'backward': only search parents (incoming messages)
|
|
494
|
-
* - 'both': search in both directions
|
|
495
|
-
* @default '
|
|
644
|
+
* - 'both': search in both directions
|
|
645
|
+
* @default 'forward'
|
|
496
646
|
*/
|
|
497
|
-
direction?:
|
|
647
|
+
direction?: TransactionTreeDirection;
|
|
498
648
|
/**
|
|
499
649
|
* Retry transaction lookup when `not_found` error occurs.
|
|
500
650
|
* Useful when transaction indexing has delays or when transactions appear gradually.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Origin = exports.defaultWaitOptions = exports.TokenSymbol = exports.StageName = exports.NFTAddressType = exports.AssetType = exports.OperationType = exports.CurrencyType = exports.BlockchainType = exports.Network = exports.SimplifiedStatuses = void 0;
|
|
3
|
+
exports.TransactionTreeDirection = exports.Origin = exports.defaultWaitOptions = exports.TokenSymbol = exports.StageName = exports.NFTAddressType = exports.AssetType = exports.OperationExecutionStatus = exports.OperationType = exports.CurrencyType = exports.BlockchainType = exports.Network = exports.SimplifiedStatuses = void 0;
|
|
4
4
|
const Consts_1 = require("../sdk/Consts");
|
|
5
5
|
var SimplifiedStatuses;
|
|
6
6
|
(function (SimplifiedStatuses) {
|
|
@@ -34,6 +34,11 @@ var OperationType;
|
|
|
34
34
|
OperationType["TAC_TON"] = "TAC-TON";
|
|
35
35
|
OperationType["UNKNOWN"] = "UNKNOWN";
|
|
36
36
|
})(OperationType || (exports.OperationType = OperationType = {}));
|
|
37
|
+
var OperationExecutionStatus;
|
|
38
|
+
(function (OperationExecutionStatus) {
|
|
39
|
+
OperationExecutionStatus["SUCCESS"] = "success";
|
|
40
|
+
OperationExecutionStatus["FAILED"] = "failed";
|
|
41
|
+
})(OperationExecutionStatus || (exports.OperationExecutionStatus = OperationExecutionStatus = {}));
|
|
37
42
|
var AssetType;
|
|
38
43
|
(function (AssetType) {
|
|
39
44
|
AssetType["NFT"] = "NFT";
|
|
@@ -68,3 +73,9 @@ var Origin;
|
|
|
68
73
|
Origin["TON"] = "TON";
|
|
69
74
|
Origin["TAC"] = "TAC";
|
|
70
75
|
})(Origin || (exports.Origin = Origin = {}));
|
|
76
|
+
var TransactionTreeDirection;
|
|
77
|
+
(function (TransactionTreeDirection) {
|
|
78
|
+
TransactionTreeDirection["FORWARD"] = "forward";
|
|
79
|
+
TransactionTreeDirection["BACKWARD"] = "backward";
|
|
80
|
+
TransactionTreeDirection["BOTH"] = "both";
|
|
81
|
+
})(TransactionTreeDirection || (exports.TransactionTreeDirection = TransactionTreeDirection = {}));
|
|
@@ -1,25 +1,48 @@
|
|
|
1
1
|
import { Cell } from '@ton/ton';
|
|
2
|
+
import { MetadataPersistenceType } from '../structs/InternalStruct';
|
|
2
3
|
export declare const ONCHAIN_CONTENT_PREFIX = 0;
|
|
3
4
|
export declare const OFFCHAIN_CONTENT_PREFIX = 1;
|
|
4
|
-
export
|
|
5
|
+
export type JsonMetadataValue = string | number | boolean | null | JsonMetadataValue[] | {
|
|
6
|
+
[key: string]: JsonMetadataValue;
|
|
7
|
+
};
|
|
8
|
+
export type MetadataRecord<TKey extends string = string> = Partial<Record<TKey, JsonMetadataValue>> & {
|
|
9
|
+
[key: string]: JsonMetadataValue | undefined;
|
|
10
|
+
};
|
|
11
|
+
export type CommonMetadataKeys = 'uri' | 'name' | 'description' | 'image' | 'image_data';
|
|
12
|
+
export type JettonMetaDataKeys = CommonMetadataKeys | 'symbol' | 'decimals' | 'amount_style' | 'render_type';
|
|
13
|
+
export type NFTMetadataKeys = CommonMetadataKeys;
|
|
14
|
+
export interface JettonMetadata extends MetadataRecord<JettonMetaDataKeys> {
|
|
5
15
|
uri?: string;
|
|
6
|
-
name
|
|
7
|
-
description
|
|
16
|
+
name?: string;
|
|
17
|
+
description?: string;
|
|
8
18
|
image?: string;
|
|
9
19
|
image_data?: string;
|
|
10
|
-
symbol
|
|
11
|
-
decimals?: string;
|
|
20
|
+
symbol?: string;
|
|
21
|
+
decimals?: string | number;
|
|
22
|
+
amount_style?: string;
|
|
23
|
+
render_type?: string;
|
|
12
24
|
}
|
|
13
|
-
export
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
25
|
+
export interface NFTMetadata extends MetadataRecord<NFTMetadataKeys> {
|
|
26
|
+
uri?: string;
|
|
27
|
+
name?: string;
|
|
28
|
+
description?: string;
|
|
29
|
+
image?: string;
|
|
30
|
+
image_data?: string;
|
|
31
|
+
}
|
|
32
|
+
type ExtendedMetadata<TKey extends string> = {
|
|
33
|
+
persistenceType: MetadataPersistenceType;
|
|
34
|
+
metadata: MetadataRecord<TKey> | null;
|
|
19
35
|
contentUri?: string;
|
|
20
36
|
};
|
|
37
|
+
export type JettonExtendedMetadata = ExtendedMetadata<JettonMetaDataKeys> & {
|
|
38
|
+
isJettonDeployerFaultyOnChainData?: boolean;
|
|
39
|
+
};
|
|
40
|
+
export type NFTExtendedMetadata = ExtendedMetadata<NFTMetadataKeys>;
|
|
21
41
|
export declare function buildJettonOffChainMetadata(contentUri: string): Cell;
|
|
22
|
-
export
|
|
42
|
+
export declare function buildNFTOffChainMetadata(contentUri: string): Cell;
|
|
23
43
|
export declare function buildJettonOnchainMetadata(data: JettonMetadata): Cell;
|
|
24
|
-
export
|
|
44
|
+
export declare function buildNFTOnchainMetadata(data: NFTMetadata): Cell;
|
|
45
|
+
export type persistenceType = MetadataPersistenceType;
|
|
25
46
|
export declare function readJettonMetadata(contentCell: Cell): Promise<JettonExtendedMetadata>;
|
|
47
|
+
export declare function readNFTMetadata(contentCell: Cell): Promise<NFTExtendedMetadata>;
|
|
48
|
+
export {};
|
|
@@ -5,155 +5,254 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.OFFCHAIN_CONTENT_PREFIX = exports.ONCHAIN_CONTENT_PREFIX = void 0;
|
|
7
7
|
exports.buildJettonOffChainMetadata = buildJettonOffChainMetadata;
|
|
8
|
+
exports.buildNFTOffChainMetadata = buildNFTOffChainMetadata;
|
|
8
9
|
exports.buildJettonOnchainMetadata = buildJettonOnchainMetadata;
|
|
10
|
+
exports.buildNFTOnchainMetadata = buildNFTOnchainMetadata;
|
|
9
11
|
exports.readJettonMetadata = readJettonMetadata;
|
|
12
|
+
exports.readNFTMetadata = readNFTMetadata;
|
|
10
13
|
const sha256_js_1 = require("@aws-crypto/sha256-js");
|
|
11
14
|
const ton_1 = require("@ton/ton");
|
|
12
15
|
const axios_1 = __importDefault(require("axios"));
|
|
13
|
-
const bn_js_1 = __importDefault(require("bn.js"));
|
|
14
16
|
const errors_1 = require("../errors");
|
|
15
17
|
exports.ONCHAIN_CONTENT_PREFIX = 0x00;
|
|
16
18
|
exports.OFFCHAIN_CONTENT_PREFIX = 0x01;
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const jettonOnChainMetadataSpec = {
|
|
19
|
+
const SNAKE_CONTENT_PREFIX = 0x00;
|
|
20
|
+
const CHUNKED_CONTENT_PREFIX = 0x01;
|
|
21
|
+
const CELL_MAX_BITS = 1023;
|
|
22
|
+
const commonMetadataSpec = {
|
|
22
23
|
uri: 'ascii',
|
|
23
24
|
name: 'utf8',
|
|
24
25
|
description: 'utf8',
|
|
25
26
|
image: 'ascii',
|
|
26
27
|
image_data: 'ascii',
|
|
28
|
+
};
|
|
29
|
+
const jettonOnChainMetadataSpec = {
|
|
30
|
+
...commonMetadataSpec,
|
|
27
31
|
symbol: 'utf8',
|
|
28
32
|
decimals: 'utf8',
|
|
33
|
+
amount_style: 'utf8',
|
|
34
|
+
render_type: 'utf8',
|
|
29
35
|
};
|
|
30
|
-
const
|
|
36
|
+
const nftOnChainMetadataSpec = {
|
|
37
|
+
...commonMetadataSpec,
|
|
38
|
+
};
|
|
39
|
+
const sha256 = (value) => {
|
|
31
40
|
const sha = new sha256_js_1.Sha256();
|
|
32
|
-
sha.update(
|
|
41
|
+
sha.update(value);
|
|
33
42
|
return Buffer.from(sha.digestSync());
|
|
34
43
|
};
|
|
35
|
-
function
|
|
36
|
-
|
|
44
|
+
function getAvailableBytes(withPrefix) {
|
|
45
|
+
return Math.floor((CELL_MAX_BITS - (withPrefix ? 8 : 0)) / 8);
|
|
46
|
+
}
|
|
47
|
+
function buildSnakeCell(content, withPrefix) {
|
|
37
48
|
const cell = new ton_1.Builder();
|
|
38
|
-
if (
|
|
39
|
-
cell.storeUint(
|
|
49
|
+
if (withPrefix) {
|
|
50
|
+
cell.storeUint(SNAKE_CONTENT_PREFIX, 8);
|
|
40
51
|
}
|
|
41
|
-
|
|
42
|
-
|
|
52
|
+
const bytesToStore = getAvailableBytes(withPrefix);
|
|
53
|
+
cell.storeBuffer(content.subarray(0, bytesToStore));
|
|
54
|
+
const remainingContent = content.subarray(bytesToStore);
|
|
43
55
|
if (remainingContent.length > 0) {
|
|
44
|
-
cell.storeRef(
|
|
56
|
+
cell.storeRef(buildSnakeCell(remainingContent, false));
|
|
45
57
|
}
|
|
46
58
|
return cell.endCell();
|
|
47
59
|
}
|
|
48
|
-
function
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
60
|
+
function buildOffchainContent(contentUri) {
|
|
61
|
+
const content = Buffer.from(contentUri, 'ascii');
|
|
62
|
+
const root = (0, ton_1.beginCell)().storeUint(exports.OFFCHAIN_CONTENT_PREFIX, 8);
|
|
63
|
+
const bytesToStore = getAvailableBytes(true);
|
|
64
|
+
root.storeBuffer(content.subarray(0, bytesToStore));
|
|
65
|
+
const remainingContent = content.subarray(bytesToStore);
|
|
66
|
+
if (remainingContent.length > 0) {
|
|
67
|
+
root.storeRef(buildSnakeCell(remainingContent, false));
|
|
68
|
+
}
|
|
69
|
+
return root.endCell();
|
|
70
|
+
}
|
|
71
|
+
function ensureByteAligned(slice) {
|
|
72
|
+
if (slice.remainingBits % 8 !== 0) {
|
|
73
|
+
throw errors_1.notMultiplyOf8Error;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function readSnakeData(slice, withPrefix) {
|
|
77
|
+
if (withPrefix) {
|
|
78
|
+
if (slice.loadUint(8) !== SNAKE_CONTENT_PREFIX) {
|
|
79
|
+
throw errors_1.unsupportedFormatError;
|
|
53
80
|
}
|
|
54
|
-
|
|
55
|
-
|
|
81
|
+
}
|
|
82
|
+
ensureByteAligned(slice);
|
|
83
|
+
const current = slice.remainingBits > 0 ? slice.loadBuffer(slice.remainingBits / 8) : Buffer.alloc(0);
|
|
84
|
+
if (slice.remainingRefs === 0) {
|
|
85
|
+
return current;
|
|
86
|
+
}
|
|
87
|
+
if (slice.remainingRefs > 1) {
|
|
88
|
+
throw errors_1.unsupportedFormatError;
|
|
89
|
+
}
|
|
90
|
+
return Buffer.concat([current, readSnakeData(slice.loadRef().beginParse(), false)]);
|
|
91
|
+
}
|
|
92
|
+
function readChunkedData(slice) {
|
|
93
|
+
const dict = slice.loadDict(ton_1.Dictionary.Keys.Uint(32), ton_1.Dictionary.Values.Cell());
|
|
94
|
+
const chunks = dict.keys().sort((a, b) => a - b);
|
|
95
|
+
return Buffer.concat(chunks.map((chunkIndex) => {
|
|
96
|
+
const chunk = dict.get(chunkIndex);
|
|
97
|
+
if (!chunk) {
|
|
98
|
+
return Buffer.alloc(0);
|
|
56
99
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
});
|
|
60
|
-
return (0, ton_1.beginCell)()
|
|
61
|
-
.storeInt(exports.ONCHAIN_CONTENT_PREFIX, 8)
|
|
62
|
-
.storeDict(dict, ton_1.Dictionary.Keys.Buffer(32), ton_1.Dictionary.Values.Cell())
|
|
63
|
-
.endCell();
|
|
100
|
+
return readSnakeData(chunk.beginParse(), true);
|
|
101
|
+
}));
|
|
64
102
|
}
|
|
65
|
-
function
|
|
66
|
-
|
|
67
|
-
|
|
103
|
+
function readContentData(cell) {
|
|
104
|
+
const slice = cell.beginParse();
|
|
105
|
+
const prefix = slice.loadUint(8);
|
|
106
|
+
if (prefix === SNAKE_CONTENT_PREFIX) {
|
|
107
|
+
return readSnakeData(slice, false);
|
|
68
108
|
}
|
|
69
|
-
if (
|
|
70
|
-
|
|
109
|
+
if (prefix === CHUNKED_CONTENT_PREFIX) {
|
|
110
|
+
return readChunkedData(slice);
|
|
71
111
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
// and upon parsing, it reads it back to a BN(base10)
|
|
86
|
-
// tl;dr if we want to read the map back to a JSON with string keys, we have to convert BN(10) back to hex
|
|
87
|
-
const toKey = (str) => BigInt(new bn_js_1.default(str, 'hex').toString(10));
|
|
88
|
-
const isJettonDeployerFaultyOnChainData = false;
|
|
89
|
-
const cellDict = contentSlice.loadDict(ton_1.Dictionary.Keys.BigUint(256), ton_1.Dictionary.Values.Cell());
|
|
90
|
-
const dict = new Map();
|
|
91
|
-
cellDict.values().forEach((item, index) => {
|
|
92
|
-
dict.set(cellDict.keys()[index], readSnakeContent(item.beginParse(), true));
|
|
93
|
-
});
|
|
94
|
-
const res = {};
|
|
95
|
-
Object.keys(jettonOnChainMetadataSpec).forEach((k) => {
|
|
96
|
-
const val = dict
|
|
97
|
-
.get(toKey(sha256(k).toString('hex')))
|
|
98
|
-
?.toString(jettonOnChainMetadataSpec[k]);
|
|
99
|
-
if (val) {
|
|
100
|
-
res[k] = val;
|
|
112
|
+
throw errors_1.unsupportedFormatError;
|
|
113
|
+
}
|
|
114
|
+
function parseOnchainMetadata(contentSlice, metadataSpec) {
|
|
115
|
+
const dict = contentSlice.loadDict(ton_1.Dictionary.Keys.Buffer(32), ton_1.Dictionary.Values.Cell());
|
|
116
|
+
const metadata = {};
|
|
117
|
+
for (const key of Object.keys(metadataSpec)) {
|
|
118
|
+
const encoding = metadataSpec[key];
|
|
119
|
+
if (!encoding) {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
const cell = dict.get(sha256(key));
|
|
123
|
+
if (!cell) {
|
|
124
|
+
continue;
|
|
101
125
|
}
|
|
102
|
-
|
|
126
|
+
const value = readContentData(cell).toString(encoding);
|
|
127
|
+
if (value) {
|
|
128
|
+
metadata[key] = value;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
103
131
|
return {
|
|
104
|
-
metadata
|
|
105
|
-
isJettonDeployerFaultyOnChainData,
|
|
132
|
+
metadata,
|
|
133
|
+
isJettonDeployerFaultyOnChainData: false,
|
|
106
134
|
};
|
|
107
135
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
136
|
+
function normalizeContentUri(contentUri) {
|
|
137
|
+
if (contentUri.startsWith('ipfs://')) {
|
|
138
|
+
const ipfsPath = contentUri.slice('ipfs://'.length).replace(/^ipfs\//, '');
|
|
139
|
+
return {
|
|
140
|
+
contentUri: `https://ipfs.io/ipfs/${ipfsPath}`,
|
|
141
|
+
isIpfs: true,
|
|
142
|
+
};
|
|
112
143
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
144
|
+
return {
|
|
145
|
+
contentUri,
|
|
146
|
+
isIpfs: /(^|\/)ipfs[.:/]/i.test(contentUri),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
async function fetchOffchainMetadata(contentUri) {
|
|
150
|
+
const normalized = normalizeContentUri(contentUri);
|
|
119
151
|
try {
|
|
120
|
-
|
|
121
|
-
|
|
152
|
+
const response = await axios_1.default.get(normalized.contentUri);
|
|
153
|
+
const metadata = response.data && typeof response.data === 'object' && !Array.isArray(response.data)
|
|
154
|
+
? response.data
|
|
155
|
+
: null;
|
|
156
|
+
return {
|
|
157
|
+
metadata,
|
|
158
|
+
isIpfs: normalized.isIpfs,
|
|
159
|
+
contentUri: normalized.contentUri,
|
|
160
|
+
};
|
|
122
161
|
}
|
|
123
162
|
catch {
|
|
124
|
-
|
|
163
|
+
return {
|
|
164
|
+
metadata: null,
|
|
165
|
+
isIpfs: normalized.isIpfs,
|
|
166
|
+
contentUri: normalized.contentUri,
|
|
167
|
+
};
|
|
125
168
|
}
|
|
126
|
-
return {
|
|
127
|
-
metadata,
|
|
128
|
-
isIpfs,
|
|
129
|
-
contentUri: jsonURI,
|
|
130
|
-
};
|
|
131
169
|
}
|
|
132
|
-
async function
|
|
170
|
+
async function parseOffchainMetadata(contentSlice) {
|
|
171
|
+
const contentUri = readSnakeData(contentSlice, false).toString('ascii');
|
|
172
|
+
return fetchOffchainMetadata(contentUri);
|
|
173
|
+
}
|
|
174
|
+
async function readMetadata(contentCell, metadataSpec) {
|
|
133
175
|
if (contentCell.bits.length <= 0) {
|
|
134
176
|
return {
|
|
135
177
|
contentUri: undefined,
|
|
136
|
-
isJettonDeployerFaultyOnChainData: false,
|
|
137
178
|
metadata: {},
|
|
138
179
|
persistenceType: 'none',
|
|
139
180
|
};
|
|
140
181
|
}
|
|
141
182
|
const contentSlice = contentCell.beginParse();
|
|
142
|
-
|
|
143
|
-
|
|
183
|
+
const prefix = contentSlice.loadUint(8);
|
|
184
|
+
if (prefix === exports.ONCHAIN_CONTENT_PREFIX) {
|
|
185
|
+
const { metadata: onchainMetadata, isJettonDeployerFaultyOnChainData } = parseOnchainMetadata(contentSlice, metadataSpec);
|
|
186
|
+
const uri = onchainMetadata.uri;
|
|
187
|
+
if (typeof uri === 'string' && uri.length > 0) {
|
|
188
|
+
const { metadata: offchainMetadata, isIpfs, contentUri } = await fetchOffchainMetadata(uri);
|
|
144
189
|
return {
|
|
145
|
-
persistenceType: 'onchain',
|
|
146
|
-
...parseJettonOnchainMetadata(contentSlice),
|
|
147
|
-
};
|
|
148
|
-
case exports.OFFCHAIN_CONTENT_PREFIX: {
|
|
149
|
-
const { metadata, isIpfs, contentUri } = await parseJettonOffchainMetadata(contentSlice);
|
|
150
|
-
return {
|
|
151
|
-
persistenceType: isIpfs ? 'offchain_ipfs' : 'offchain_private_domain',
|
|
152
190
|
contentUri,
|
|
153
|
-
|
|
191
|
+
isJettonDeployerFaultyOnChainData,
|
|
192
|
+
metadata: {
|
|
193
|
+
...(offchainMetadata ?? {}),
|
|
194
|
+
...onchainMetadata,
|
|
195
|
+
},
|
|
196
|
+
persistenceType: isIpfs ? 'semichain_ipfs' : 'semichain_private_domain',
|
|
154
197
|
};
|
|
155
198
|
}
|
|
156
|
-
|
|
157
|
-
|
|
199
|
+
return {
|
|
200
|
+
isJettonDeployerFaultyOnChainData,
|
|
201
|
+
metadata: onchainMetadata,
|
|
202
|
+
persistenceType: 'onchain',
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
if (prefix === exports.OFFCHAIN_CONTENT_PREFIX) {
|
|
206
|
+
const { metadata, isIpfs, contentUri } = await parseOffchainMetadata(contentSlice);
|
|
207
|
+
return {
|
|
208
|
+
contentUri,
|
|
209
|
+
metadata,
|
|
210
|
+
persistenceType: isIpfs ? 'offchain_ipfs' : 'offchain_private_domain',
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
throw errors_1.prefixError;
|
|
214
|
+
}
|
|
215
|
+
function buildOnchainMetadata(data, metadataSpec) {
|
|
216
|
+
const dict = ton_1.Dictionary.empty(ton_1.Dictionary.Keys.Buffer(32), ton_1.Dictionary.Values.Cell());
|
|
217
|
+
for (const [key, value] of Object.entries(data)) {
|
|
218
|
+
const encoding = metadataSpec[key];
|
|
219
|
+
if (!encoding) {
|
|
220
|
+
throw (0, errors_1.unsupportedKeyError)(key);
|
|
221
|
+
}
|
|
222
|
+
if (value === '' || value == null) {
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
dict.set(sha256(key), buildSnakeCell(Buffer.from(String(value), encoding), true));
|
|
158
226
|
}
|
|
227
|
+
return (0, ton_1.beginCell)()
|
|
228
|
+
.storeUint(exports.ONCHAIN_CONTENT_PREFIX, 8)
|
|
229
|
+
.storeDict(dict, ton_1.Dictionary.Keys.Buffer(32), ton_1.Dictionary.Values.Cell())
|
|
230
|
+
.endCell();
|
|
231
|
+
}
|
|
232
|
+
function buildJettonOffChainMetadata(contentUri) {
|
|
233
|
+
return buildOffchainContent(contentUri);
|
|
234
|
+
}
|
|
235
|
+
function buildNFTOffChainMetadata(contentUri) {
|
|
236
|
+
return buildOffchainContent(contentUri);
|
|
237
|
+
}
|
|
238
|
+
function buildJettonOnchainMetadata(data) {
|
|
239
|
+
return buildOnchainMetadata(data, jettonOnChainMetadataSpec);
|
|
240
|
+
}
|
|
241
|
+
function buildNFTOnchainMetadata(data) {
|
|
242
|
+
return buildOnchainMetadata(data, nftOnChainMetadataSpec);
|
|
243
|
+
}
|
|
244
|
+
async function readJettonMetadata(contentCell) {
|
|
245
|
+
const metadata = await readMetadata(contentCell, jettonOnChainMetadataSpec);
|
|
246
|
+
return {
|
|
247
|
+
...metadata,
|
|
248
|
+
isJettonDeployerFaultyOnChainData: metadata.isJettonDeployerFaultyOnChainData ?? false,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
async function readNFTMetadata(contentCell) {
|
|
252
|
+
const metadata = await readMetadata(contentCell, nftOnChainMetadataSpec);
|
|
253
|
+
return {
|
|
254
|
+
contentUri: metadata.contentUri,
|
|
255
|
+
metadata: metadata.metadata,
|
|
256
|
+
persistenceType: metadata.persistenceType,
|
|
257
|
+
};
|
|
159
258
|
}
|