@t402/ton 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/cjs/exact/client/index.d.ts +61 -2
  2. package/dist/cjs/exact/client/index.js +26 -2
  3. package/dist/cjs/exact/client/index.js.map +1 -1
  4. package/dist/cjs/exact/facilitator/index.d.ts +62 -2
  5. package/dist/cjs/exact/facilitator/index.js +10 -2
  6. package/dist/cjs/exact/facilitator/index.js.map +1 -1
  7. package/dist/cjs/exact/server/index.d.ts +42 -1
  8. package/dist/cjs/exact/server/index.js +17 -2
  9. package/dist/cjs/exact/server/index.js.map +1 -1
  10. package/dist/cjs/index.d.ts +6 -1
  11. package/dist/cjs/index.js +503 -9
  12. package/dist/cjs/index.js.map +1 -1
  13. package/dist/esm/chunk-P6JLVB4E.mjs +300 -0
  14. package/dist/esm/chunk-P6JLVB4E.mjs.map +1 -0
  15. package/dist/esm/chunk-UB4QVIUC.mjs +266 -0
  16. package/dist/esm/chunk-UB4QVIUC.mjs.map +1 -0
  17. package/dist/esm/{chunk-ZCMWKFVA.mjs → chunk-XNO6XEWS.mjs} +25 -2
  18. package/dist/esm/chunk-XNO6XEWS.mjs.map +1 -0
  19. package/dist/esm/exact/client/index.d.mts +61 -2
  20. package/dist/esm/exact/client/index.mjs +5 -3
  21. package/dist/esm/exact/facilitator/index.d.mts +62 -2
  22. package/dist/esm/exact/facilitator/index.mjs +6 -254
  23. package/dist/esm/exact/facilitator/index.mjs.map +1 -1
  24. package/dist/esm/exact/server/index.d.mts +42 -1
  25. package/dist/esm/exact/server/index.mjs +6 -208
  26. package/dist/esm/exact/server/index.mjs.map +1 -1
  27. package/dist/esm/index.d.mts +6 -1
  28. package/dist/esm/index.mjs +12 -4
  29. package/dist/esm/index.mjs.map +1 -1
  30. package/package.json +1 -1
  31. package/dist/esm/chunk-RST5PY7E.mjs +0 -85
  32. package/dist/esm/chunk-RST5PY7E.mjs.map +0 -1
  33. package/dist/esm/chunk-ZCMWKFVA.mjs.map +0 -1
package/dist/cjs/index.js CHANGED
@@ -62,6 +62,9 @@ __export(src_exports, {
62
62
  normalizeNetwork: () => normalizeNetwork,
63
63
  parseJettonTransferBody: () => parseJettonTransferBody,
64
64
  parseTonAddress: () => parseTonAddress,
65
+ registerExactTonClientScheme: () => registerExactTonScheme,
66
+ registerExactTonFacilitatorScheme: () => registerExactTonScheme3,
67
+ registerExactTonServerScheme: () => registerExactTonScheme2,
65
68
  toClientTonSigner: () => toClientTonSigner,
66
69
  toFacilitatorTonSigner: () => toFacilitatorTonSigner,
67
70
  validateTonAddress: () => validateTonAddress
@@ -314,15 +317,26 @@ var ExactTonScheme = class {
314
317
  }
315
318
  };
316
319
 
317
- // src/signer.ts
318
- function toClientTonSigner(signer) {
319
- return signer;
320
- }
321
- function toFacilitatorTonSigner(client) {
322
- return {
323
- ...client,
324
- getAddresses: () => [client.address]
325
- };
320
+ // src/exact/client/register.ts
321
+ function registerExactTonScheme(client, config) {
322
+ const scheme = new ExactTonScheme(
323
+ config.signer,
324
+ config.getJettonWalletAddress,
325
+ config.schemeConfig
326
+ );
327
+ if (config.networks && config.networks.length > 0) {
328
+ config.networks.forEach((network) => {
329
+ client.register(network, scheme);
330
+ });
331
+ } else {
332
+ client.register("ton:*", scheme);
333
+ }
334
+ if (config.policies) {
335
+ config.policies.forEach((policy) => {
336
+ client.registerPolicy(policy);
337
+ });
338
+ }
339
+ return client;
326
340
  }
327
341
 
328
342
  // src/tokens.ts
@@ -391,6 +405,483 @@ function isNetworkSupported(network) {
391
405
  function getSupportedNetworks() {
392
406
  return Object.keys(JETTON_REGISTRY);
393
407
  }
408
+
409
+ // src/exact/server/scheme.ts
410
+ var ExactTonScheme2 = class {
411
+ constructor(config = {}) {
412
+ this.scheme = SCHEME_EXACT;
413
+ this.moneyParsers = [];
414
+ this.config = config;
415
+ }
416
+ /**
417
+ * Register a custom money parser in the parser chain.
418
+ * Multiple parsers can be registered - they will be tried in registration order.
419
+ * Each parser receives a decimal amount (e.g., 1.50 for $1.50).
420
+ * If a parser returns null, the next parser in the chain will be tried.
421
+ * The default parser is always the final fallback.
422
+ *
423
+ * @param parser - Custom function to convert amount to AssetAmount (or null to skip)
424
+ * @returns The server instance for chaining
425
+ *
426
+ * @example
427
+ * tonServer.registerMoneyParser(async (amount, network) => {
428
+ * // Use custom Jetton for large amounts
429
+ * if (amount > 1000) {
430
+ * return {
431
+ * amount: (amount * 1e9).toString(),
432
+ * asset: "EQCustomJettonAddress...",
433
+ * extra: { tier: "premium" }
434
+ * };
435
+ * }
436
+ * return null; // Use next parser
437
+ * });
438
+ */
439
+ registerMoneyParser(parser) {
440
+ this.moneyParsers.push(parser);
441
+ return this;
442
+ }
443
+ /**
444
+ * Parses a price into an asset amount.
445
+ * If price is already an AssetAmount, returns it directly.
446
+ * If price is Money (string | number), parses to decimal and tries custom parsers.
447
+ * Falls back to default conversion if all custom parsers return null.
448
+ *
449
+ * @param price - The price to parse
450
+ * @param network - The network to use
451
+ * @returns Promise that resolves to the parsed asset amount
452
+ */
453
+ async parsePrice(price, network) {
454
+ const normalizedNetwork = normalizeNetwork(network);
455
+ if (typeof price === "object" && price !== null && "amount" in price) {
456
+ if (!price.asset) {
457
+ throw new Error(`Asset address must be specified for AssetAmount on network ${network}`);
458
+ }
459
+ return {
460
+ amount: price.amount,
461
+ asset: price.asset,
462
+ extra: price.extra || {}
463
+ };
464
+ }
465
+ const amount = this.parseMoneyToDecimal(price);
466
+ for (const parser of this.moneyParsers) {
467
+ const result = await parser(amount, normalizedNetwork);
468
+ if (result !== null) {
469
+ return result;
470
+ }
471
+ }
472
+ return this.defaultMoneyConversion(amount, normalizedNetwork);
473
+ }
474
+ /**
475
+ * Build payment requirements for this scheme/network combination.
476
+ * Adds TON-specific fields like gas sponsor if provided by facilitator.
477
+ *
478
+ * @param paymentRequirements - Base payment requirements with amount/asset already set
479
+ * @param supportedKind - The supported kind from facilitator's /supported endpoint
480
+ * @param extensionKeys - Extensions supported by the facilitator (unused)
481
+ * @returns Enhanced payment requirements ready to be sent to clients
482
+ */
483
+ async enhancePaymentRequirements(paymentRequirements, supportedKind, extensionKeys) {
484
+ void extensionKeys;
485
+ const extra = { ...paymentRequirements.extra };
486
+ if (supportedKind.extra?.gasSponsor) {
487
+ extra.gasSponsor = supportedKind.extra.gasSponsor;
488
+ }
489
+ return {
490
+ ...paymentRequirements,
491
+ extra
492
+ };
493
+ }
494
+ /**
495
+ * Parse Money (string | number) to a decimal number.
496
+ * Handles formats like "$1.50", "1.50", 1.50, etc.
497
+ *
498
+ * @param money - The money value to parse
499
+ * @returns Decimal number
500
+ */
501
+ parseMoneyToDecimal(money) {
502
+ if (typeof money === "number") {
503
+ return money;
504
+ }
505
+ const cleanMoney = money.replace(/^\$/, "").trim();
506
+ const amount = parseFloat(cleanMoney);
507
+ if (isNaN(amount)) {
508
+ throw new Error(`Invalid money format: ${money}`);
509
+ }
510
+ return amount;
511
+ }
512
+ /**
513
+ * Default money conversion implementation.
514
+ * Converts decimal amount to the preferred Jetton on the specified network.
515
+ *
516
+ * @param amount - The decimal amount (e.g., 1.50)
517
+ * @param network - The network to use
518
+ * @returns The parsed asset amount
519
+ */
520
+ defaultMoneyConversion(amount, network) {
521
+ const jetton = this.getDefaultAsset(network);
522
+ const tokenAmount = this.convertToTokenAmount(amount.toString(), jetton.decimals);
523
+ return {
524
+ amount: tokenAmount,
525
+ asset: jetton.masterAddress,
526
+ extra: {
527
+ symbol: jetton.symbol,
528
+ name: jetton.name,
529
+ decimals: jetton.decimals
530
+ }
531
+ };
532
+ }
533
+ /**
534
+ * Convert decimal amount to token units (e.g., 0.10 -> 100000 for 6-decimal tokens)
535
+ *
536
+ * @param decimalAmount - The decimal amount to convert
537
+ * @param decimals - Number of decimals for the token
538
+ * @returns The token amount as a string
539
+ */
540
+ convertToTokenAmount(decimalAmount, decimals) {
541
+ const amount = parseFloat(decimalAmount);
542
+ if (isNaN(amount)) {
543
+ throw new Error(`Invalid amount: ${decimalAmount}`);
544
+ }
545
+ const tokenAmount = Math.floor(amount * Math.pow(10, decimals));
546
+ return tokenAmount.toString();
547
+ }
548
+ /**
549
+ * Get the default asset info for a network.
550
+ * Priority: configured preferredJetton > USDT > first available
551
+ *
552
+ * @param network - The network to get asset info for
553
+ * @returns The Jetton configuration
554
+ */
555
+ getDefaultAsset(network) {
556
+ if (this.config.preferredJetton) {
557
+ const preferred = getJettonConfig(network, this.config.preferredJetton);
558
+ if (preferred) return preferred;
559
+ }
560
+ const defaultJetton = getDefaultJetton(network);
561
+ if (defaultJetton) return defaultJetton;
562
+ throw new Error(`No Jettons configured for network ${network}`);
563
+ }
564
+ /**
565
+ * Get Jetton info for a given symbol on a network
566
+ *
567
+ * @param symbol - The Jetton symbol (e.g., "USDT")
568
+ * @param network - The network to use
569
+ * @returns The Jetton configuration
570
+ */
571
+ getAssetInfo(symbol, network) {
572
+ const jetton = getJettonConfig(network, symbol);
573
+ if (jetton) return jetton;
574
+ if (symbol.toUpperCase() === "USD") {
575
+ return this.getDefaultAsset(network);
576
+ }
577
+ throw new Error(`Unsupported Jetton: ${symbol} on network ${network}`);
578
+ }
579
+ /**
580
+ * Get the number of decimals for a Jetton on a network
581
+ *
582
+ * @param network - The network to use
583
+ * @param jettonAddress - Optional Jetton address to look up
584
+ * @returns The number of decimals for the Jetton
585
+ */
586
+ getAssetDecimals(network, jettonAddress) {
587
+ if (jettonAddress) {
588
+ const jetton = getJettonByAddress(network, jettonAddress);
589
+ if (jetton) return jetton.decimals;
590
+ }
591
+ return 6;
592
+ }
593
+ /**
594
+ * Get all supported networks
595
+ */
596
+ static getSupportedNetworks() {
597
+ return Object.keys(JETTON_REGISTRY);
598
+ }
599
+ /**
600
+ * Check if a network is supported
601
+ */
602
+ static isNetworkSupported(network) {
603
+ return network in JETTON_REGISTRY;
604
+ }
605
+ };
606
+
607
+ // src/exact/server/register.ts
608
+ function registerExactTonScheme2(server, config = {}) {
609
+ const scheme = new ExactTonScheme2(config.schemeConfig);
610
+ if (config.networks && config.networks.length > 0) {
611
+ config.networks.forEach((network) => {
612
+ server.register(network, scheme);
613
+ });
614
+ } else {
615
+ server.register("ton:*", scheme);
616
+ }
617
+ return server;
618
+ }
619
+
620
+ // src/exact/facilitator/scheme.ts
621
+ var import_core3 = require("@ton/core");
622
+ var ExactTonScheme3 = class {
623
+ /**
624
+ * Creates a new ExactTonScheme facilitator instance.
625
+ *
626
+ * @param signer - The TON signer for facilitator operations
627
+ * @param config - Optional configuration
628
+ */
629
+ constructor(signer, config = {}) {
630
+ this.signer = signer;
631
+ this.scheme = SCHEME_EXACT;
632
+ this.caipFamily = "ton:*";
633
+ this.config = config;
634
+ }
635
+ /**
636
+ * Get mechanism-specific extra data for the supported kinds endpoint.
637
+ * For TON, optionally returns gas sponsor address if configured.
638
+ *
639
+ * @param network - The network identifier
640
+ * @returns Extra data object or undefined
641
+ */
642
+ getExtra(network) {
643
+ void network;
644
+ if (this.config.canSponsorGas) {
645
+ const addresses = this.signer.getAddresses();
646
+ if (addresses.length > 0) {
647
+ return {
648
+ gasSponsor: addresses[0]
649
+ };
650
+ }
651
+ }
652
+ return void 0;
653
+ }
654
+ /**
655
+ * Get signer addresses used by this facilitator.
656
+ * Returns all addresses this facilitator can use for operations.
657
+ *
658
+ * @param network - The network identifier
659
+ * @returns Array of facilitator addresses
660
+ */
661
+ getSigners(network) {
662
+ void network;
663
+ return [...this.signer.getAddresses()];
664
+ }
665
+ /**
666
+ * Verifies a payment payload.
667
+ *
668
+ * Performs comprehensive validation:
669
+ * 1. Scheme and network matching
670
+ * 2. BOC format validation
671
+ * 3. Message structure verification
672
+ * 4. Authorization expiry check
673
+ * 5. Balance verification
674
+ * 6. Amount and recipient validation
675
+ *
676
+ * @param payload - The payment payload to verify
677
+ * @param requirements - The payment requirements
678
+ * @returns Promise resolving to verification response
679
+ */
680
+ async verify(payload, requirements) {
681
+ const tonPayload = payload.payload;
682
+ const authorization = tonPayload.authorization;
683
+ if (payload.accepted.scheme !== SCHEME_EXACT || requirements.scheme !== SCHEME_EXACT) {
684
+ return {
685
+ isValid: false,
686
+ invalidReason: "unsupported_scheme",
687
+ payer: authorization.from
688
+ };
689
+ }
690
+ try {
691
+ const payloadNetwork = normalizeNetwork(payload.accepted.network);
692
+ const requirementsNetwork = normalizeNetwork(requirements.network);
693
+ if (payloadNetwork !== requirementsNetwork) {
694
+ return {
695
+ isValid: false,
696
+ invalidReason: "network_mismatch",
697
+ payer: authorization.from
698
+ };
699
+ }
700
+ } catch {
701
+ return {
702
+ isValid: false,
703
+ invalidReason: "invalid_network",
704
+ payer: authorization.from
705
+ };
706
+ }
707
+ try {
708
+ import_core3.Cell.fromBase64(tonPayload.signedBoc);
709
+ } catch {
710
+ return {
711
+ isValid: false,
712
+ invalidReason: "invalid_boc_format",
713
+ payer: authorization.from
714
+ };
715
+ }
716
+ const verifyResult = await this.signer.verifyMessage({
717
+ signedBoc: tonPayload.signedBoc,
718
+ expectedFrom: authorization.from,
719
+ expectedTransfer: {
720
+ jettonAmount: BigInt(authorization.jettonAmount),
721
+ destination: requirements.payTo,
722
+ jettonMaster: requirements.asset
723
+ }
724
+ });
725
+ if (!verifyResult.valid) {
726
+ return {
727
+ isValid: false,
728
+ invalidReason: verifyResult.reason || "message_verification_failed",
729
+ payer: authorization.from
730
+ };
731
+ }
732
+ const now = Math.floor(Date.now() / 1e3);
733
+ if (authorization.validUntil < now + 30) {
734
+ return {
735
+ isValid: false,
736
+ invalidReason: "authorization_expired",
737
+ payer: authorization.from
738
+ };
739
+ }
740
+ try {
741
+ const balance = await this.signer.getJettonBalance({
742
+ ownerAddress: authorization.from,
743
+ jettonMasterAddress: requirements.asset
744
+ });
745
+ if (balance < BigInt(requirements.amount)) {
746
+ return {
747
+ isValid: false,
748
+ invalidReason: "insufficient_jetton_balance",
749
+ payer: authorization.from
750
+ };
751
+ }
752
+ } catch (error) {
753
+ console.warn("Could not verify Jetton balance:", error);
754
+ }
755
+ if (BigInt(authorization.jettonAmount) < BigInt(requirements.amount)) {
756
+ return {
757
+ isValid: false,
758
+ invalidReason: "insufficient_amount",
759
+ payer: authorization.from
760
+ };
761
+ }
762
+ if (!addressesEqual(authorization.to, requirements.payTo)) {
763
+ return {
764
+ isValid: false,
765
+ invalidReason: "recipient_mismatch",
766
+ payer: authorization.from
767
+ };
768
+ }
769
+ if (!addressesEqual(authorization.jettonMaster, requirements.asset)) {
770
+ return {
771
+ isValid: false,
772
+ invalidReason: "asset_mismatch",
773
+ payer: authorization.from
774
+ };
775
+ }
776
+ try {
777
+ const currentSeqno = await this.signer.getSeqno(authorization.from);
778
+ if (authorization.seqno < currentSeqno) {
779
+ return {
780
+ isValid: false,
781
+ invalidReason: "seqno_already_used",
782
+ payer: authorization.from
783
+ };
784
+ }
785
+ if (authorization.seqno > currentSeqno) {
786
+ return {
787
+ isValid: false,
788
+ invalidReason: "seqno_too_high",
789
+ payer: authorization.from
790
+ };
791
+ }
792
+ } catch (error) {
793
+ console.warn("Could not verify seqno:", error);
794
+ }
795
+ try {
796
+ const isDeployed = await this.signer.isDeployed(authorization.from);
797
+ if (!isDeployed) {
798
+ return {
799
+ isValid: false,
800
+ invalidReason: "wallet_not_deployed",
801
+ payer: authorization.from
802
+ };
803
+ }
804
+ } catch (error) {
805
+ console.warn("Could not verify wallet deployment:", error);
806
+ }
807
+ return {
808
+ isValid: true,
809
+ invalidReason: void 0,
810
+ payer: authorization.from
811
+ };
812
+ }
813
+ /**
814
+ * Settles a payment by broadcasting the signed message.
815
+ *
816
+ * @param payload - The payment payload to settle
817
+ * @param requirements - The payment requirements
818
+ * @returns Promise resolving to settlement response
819
+ */
820
+ async settle(payload, requirements) {
821
+ const tonPayload = payload.payload;
822
+ const verifyResult = await this.verify(payload, requirements);
823
+ if (!verifyResult.isValid) {
824
+ return {
825
+ success: false,
826
+ network: payload.accepted.network,
827
+ transaction: "",
828
+ errorReason: verifyResult.invalidReason ?? "verification_failed",
829
+ payer: tonPayload.authorization.from
830
+ };
831
+ }
832
+ try {
833
+ const txHash = await this.signer.sendExternalMessage(tonPayload.signedBoc);
834
+ const confirmation = await this.signer.waitForTransaction({
835
+ address: tonPayload.authorization.from,
836
+ seqno: tonPayload.authorization.seqno + 1,
837
+ // Wait for next seqno
838
+ timeout: 6e4
839
+ // 60 second timeout
840
+ });
841
+ if (!confirmation.success) {
842
+ return {
843
+ success: false,
844
+ errorReason: confirmation.error || "transaction_not_confirmed",
845
+ transaction: txHash,
846
+ network: payload.accepted.network,
847
+ payer: tonPayload.authorization.from
848
+ };
849
+ }
850
+ return {
851
+ success: true,
852
+ transaction: confirmation.hash || txHash,
853
+ network: payload.accepted.network,
854
+ payer: tonPayload.authorization.from
855
+ };
856
+ } catch (error) {
857
+ console.error("Failed to settle TON transaction:", error);
858
+ return {
859
+ success: false,
860
+ errorReason: "transaction_failed",
861
+ transaction: "",
862
+ network: payload.accepted.network,
863
+ payer: tonPayload.authorization.from
864
+ };
865
+ }
866
+ }
867
+ };
868
+
869
+ // src/exact/facilitator/register.ts
870
+ function registerExactTonScheme3(facilitator, config) {
871
+ facilitator.register(config.networks, new ExactTonScheme3(config.signer, config.schemeConfig));
872
+ return facilitator;
873
+ }
874
+
875
+ // src/signer.ts
876
+ function toClientTonSigner(signer) {
877
+ return signer;
878
+ }
879
+ function toFacilitatorTonSigner(client) {
880
+ return {
881
+ ...client,
882
+ getAddresses: () => [client.address]
883
+ };
884
+ }
394
885
  // Annotate the CommonJS export names for ESM import in node:
395
886
  0 && (module.exports = {
396
887
  DEFAULT_FORWARD_TON,
@@ -435,6 +926,9 @@ function getSupportedNetworks() {
435
926
  normalizeNetwork,
436
927
  parseJettonTransferBody,
437
928
  parseTonAddress,
929
+ registerExactTonClientScheme,
930
+ registerExactTonFacilitatorScheme,
931
+ registerExactTonServerScheme,
438
932
  toClientTonSigner,
439
933
  toFacilitatorTonSigner,
440
934
  validateTonAddress