@t402/ton 2.3.0 → 2.4.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 (37) hide show
  1. package/README.md +154 -0
  2. package/dist/cjs/exact/client/index.js +3 -2
  3. package/dist/cjs/exact/client/index.js.map +1 -1
  4. package/dist/cjs/exact/facilitator/index.js +1 -0
  5. package/dist/cjs/exact/facilitator/index.js.map +1 -1
  6. package/dist/cjs/exact/server/index.js +4 -2
  7. package/dist/cjs/exact/server/index.js.map +1 -1
  8. package/dist/cjs/index.d.ts +3 -2
  9. package/dist/cjs/index.js +19 -4
  10. package/dist/cjs/index.js.map +1 -1
  11. package/dist/cjs/upto/index.d.ts +126 -0
  12. package/dist/cjs/upto/index.js +41 -0
  13. package/dist/cjs/upto/index.js.map +1 -0
  14. package/dist/esm/{chunk-3BN2G4M7.mjs → chunk-2EIGZGJU.mjs} +4 -8
  15. package/dist/esm/chunk-2EIGZGJU.mjs.map +1 -0
  16. package/dist/esm/{chunk-NGYEU24R.mjs → chunk-HIUPRMQV.mjs} +8 -5
  17. package/dist/esm/chunk-HIUPRMQV.mjs.map +1 -0
  18. package/dist/esm/chunk-NSSMTXJJ.mjs +8 -0
  19. package/dist/esm/chunk-NSSMTXJJ.mjs.map +1 -0
  20. package/dist/esm/{chunk-ZJA7AWWH.mjs → chunk-OOC4E2LE.mjs} +5 -3
  21. package/dist/esm/{chunk-ZJA7AWWH.mjs.map → chunk-OOC4E2LE.mjs.map} +1 -1
  22. package/dist/esm/{chunk-7OE2PWYP.mjs → chunk-QTEA4ZJU.mjs} +5 -3
  23. package/dist/esm/{chunk-7OE2PWYP.mjs.map → chunk-QTEA4ZJU.mjs.map} +1 -1
  24. package/dist/esm/chunk-TC3EU5ZU.mjs +15 -0
  25. package/dist/esm/chunk-TC3EU5ZU.mjs.map +1 -0
  26. package/dist/esm/exact/client/index.mjs +3 -2
  27. package/dist/esm/exact/facilitator/index.mjs +3 -2
  28. package/dist/esm/exact/server/index.mjs +3 -2
  29. package/dist/esm/index.d.mts +3 -2
  30. package/dist/esm/index.mjs +9 -4
  31. package/dist/esm/index.mjs.map +1 -1
  32. package/dist/esm/upto/index.d.mts +126 -0
  33. package/dist/esm/upto/index.mjs +8 -0
  34. package/dist/esm/upto/index.mjs.map +1 -0
  35. package/package.json +14 -4
  36. package/dist/esm/chunk-3BN2G4M7.mjs.map +0 -1
  37. package/dist/esm/chunk-NGYEU24R.mjs.map +0 -1
package/README.md ADDED
@@ -0,0 +1,154 @@
1
+ # @t402/ton
2
+
3
+ TON blockchain implementation of the t402 payment protocol using the **Exact** payment scheme with Jetton (TEP-74) transfers.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @t402/ton
9
+ ```
10
+
11
+ ## Overview
12
+
13
+ This package provides three main components for handling t402 payments on TON:
14
+
15
+ - **Client** - For applications that need to make payments (sign Jetton transfer messages)
16
+ - **Server** - For resource servers that accept payments and build payment requirements
17
+ - **Facilitator** - For payment processors that verify signatures and execute on-chain settlements
18
+
19
+ ## Quick Start
20
+
21
+ ### Client
22
+
23
+ ```typescript
24
+ import { ExactTonScheme, toClientTonSigner } from '@t402/ton'
25
+ import { mnemonicToPrivateKey } from '@ton/crypto'
26
+
27
+ const keyPair = await mnemonicToPrivateKey(mnemonic.split(' '))
28
+ const signer = toClientTonSigner(keyPair)
29
+
30
+ const client = new ExactTonScheme(signer)
31
+ const payload = await client.createPaymentPayload(requirements)
32
+ ```
33
+
34
+ ### Server
35
+
36
+ ```typescript
37
+ import { registerExactTonServerScheme } from '@t402/ton'
38
+ import { t402ResourceServer } from '@t402/express'
39
+ import { HTTPFacilitatorClient } from '@t402/core/server'
40
+
41
+ const facilitator = new HTTPFacilitatorClient({ url: 'https://facilitator.t402.io' })
42
+ const resourceServer = new t402ResourceServer(facilitator)
43
+ registerExactTonServerScheme(resourceServer)
44
+ ```
45
+
46
+ ### Facilitator
47
+
48
+ ```typescript
49
+ import { registerExactTonFacilitatorScheme, toFacilitatorTonSigner } from '@t402/ton'
50
+ import { mnemonicToPrivateKey } from '@ton/crypto'
51
+
52
+ const keyPair = await mnemonicToPrivateKey(mnemonic.split(' '))
53
+ const signer = toFacilitatorTonSigner(keyPair)
54
+
55
+ registerExactTonFacilitatorScheme(facilitator, { signer })
56
+ ```
57
+
58
+ ## Package Exports
59
+
60
+ ### Main Package (`@t402/ton`)
61
+
62
+ - `ExactTonScheme` - Main scheme class
63
+ - `registerExactTonClientScheme` - Register client with t402Client
64
+ - `registerExactTonServerScheme` - Register server with t402ResourceServer
65
+ - `registerExactTonFacilitatorScheme` - Register with facilitator
66
+ - `toClientTonSigner` / `toFacilitatorTonSigner` - Signer converters
67
+
68
+ ### Subpath Exports
69
+
70
+ - `@t402/ton/exact/client` - Client-only imports
71
+ - `@t402/ton/exact/server` - Server-only imports
72
+ - `@t402/ton/exact/facilitator` - Facilitator-only imports
73
+
74
+ ## Supported Networks
75
+
76
+ | Network | CAIP-2 Identifier |
77
+ | ----------- | ----------------- |
78
+ | TON Mainnet | `ton:mainnet` |
79
+ | TON Testnet | `ton:testnet` |
80
+
81
+ ## Supported Assets
82
+
83
+ | Token | Network | Jetton Address |
84
+ | ----- | ------- | -------------------------------------------------- |
85
+ | USDT | Mainnet | `EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs` |
86
+ | USDT | Testnet | `kQD0GKBM8ZbryVk2aESmzfU6b9b_8era_IkvBSELujFZPsyy` |
87
+
88
+ ## Token Utilities
89
+
90
+ ```typescript
91
+ import {
92
+ getJettonConfig,
93
+ getNetworkJettons,
94
+ getDefaultJetton,
95
+ getSupportedNetworks,
96
+ isNetworkSupported,
97
+ } from '@t402/ton'
98
+
99
+ // Check if a network is supported
100
+ isNetworkSupported('ton:mainnet') // true
101
+
102
+ // Get USDT config for mainnet
103
+ const config = getJettonConfig('ton:mainnet', 'USDT')
104
+
105
+ // Get all supported networks
106
+ const networks = getSupportedNetworks()
107
+ ```
108
+
109
+ ## Address & Amount Utilities
110
+
111
+ ```typescript
112
+ import {
113
+ validateTonAddress,
114
+ convertToJettonAmount,
115
+ convertFromJettonAmount,
116
+ buildJettonTransferBody,
117
+ estimateJettonTransferGas,
118
+ } from '@t402/ton'
119
+
120
+ // Validate TON address
121
+ validateTonAddress('EQ...') // true/false
122
+
123
+ // Convert amounts (6 decimals for USDT)
124
+ convertToJettonAmount('1.5', 6) // 1500000n
125
+ convertFromJettonAmount(1500000n, 6) // "1.5"
126
+ ```
127
+
128
+ ## Security
129
+
130
+ - Client signs Jetton transfer messages off-chain using Ed25519
131
+ - Replay protection via unique `query_id` per transaction
132
+ - Facilitator verifies signatures before broadcasting to TON network
133
+
134
+ ## Development
135
+
136
+ ```bash
137
+ pnpm build # Build the package
138
+ pnpm test # Run unit tests
139
+ pnpm test:integration # Run integration tests
140
+ ```
141
+
142
+ ## Related Packages
143
+
144
+ - `@t402/core` - Core protocol types and client
145
+ - `@t402/fetch` - HTTP wrapper with automatic payment handling
146
+ - `@t402/evm` - EVM implementation
147
+ - `@t402/svm` - Solana implementation
148
+ - `@t402/tron` - TRON implementation
149
+ - `@t402/near` - NEAR Protocol implementation
150
+ - `@t402/aptos` - Aptos implementation
151
+ - `@t402/tezos` - Tezos implementation
152
+ - `@t402/polkadot` - Polkadot Asset Hub implementation
153
+ - `@t402/stacks` - Stacks implementation
154
+ - `@t402/cosmos` - Cosmos (Noble) implementation
@@ -53,6 +53,7 @@ var SCHEME_EXACT = "exact";
53
53
 
54
54
  // src/utils.ts
55
55
  var import_core = require("@ton/core");
56
+ var import_utils = require("@t402/core/utils");
56
57
  function normalizeNetwork(network) {
57
58
  if (network.startsWith("ton:")) {
58
59
  if (!TON_NETWORKS.includes(network)) {
@@ -78,8 +79,8 @@ function parseTonAddress(address) {
78
79
  }
79
80
  function generateQueryId() {
80
81
  const timestamp = BigInt(Date.now());
81
- const random = BigInt(Math.floor(Math.random() * 1e6));
82
- return timestamp * 1000000n + random;
82
+ const random = (0, import_utils.cryptoRandomBigInt)(20);
83
+ return timestamp * 1048576n + random;
83
84
  }
84
85
  function buildJettonTransferBody(params) {
85
86
  const builder = (0, import_core.beginCell)().storeUint(JETTON_TRANSFER_OP, 32).storeUint(params.queryId, 64).storeCoins(params.amount).storeAddress(params.destination).storeAddress(params.responseDestination).storeBit(false);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/exact/client/index.ts","../../../../src/exact/client/scheme.ts","../../../../src/constants.ts","../../../../src/utils.ts","../../../../src/exact/client/register.ts"],"sourcesContent":["export { ExactTonScheme } from './scheme.js'\nexport type { ExactTonSchemeConfig } from './scheme.js'\nexport { registerExactTonScheme } from './register.js'\nexport type { TonClientConfig } from './register.js'\n","/**\n * TON Client Scheme Implementation\n *\n * Creates payment payloads for TON Jetton transfers using the exact scheme.\n * Builds and signs external messages for Jetton wallet interactions.\n */\n\nimport { Address, SendMode } from '@ton/core'\nimport type { PaymentPayload, PaymentRequirements, SchemeNetworkClient } from '@t402/core/types'\nimport type { ClientTonSigner } from '../../signer.js'\nimport type { ExactTonPayloadV2 } from '../../types.js'\nimport { SCHEME_EXACT, DEFAULT_JETTON_TRANSFER_TON, DEFAULT_FORWARD_TON } from '../../constants.js'\nimport {\n generateQueryId,\n buildJettonTransferBody,\n normalizeNetwork,\n parseTonAddress,\n} from '../../utils.js'\n\n/**\n * Configuration for ExactTonScheme client\n */\nexport interface ExactTonSchemeConfig {\n /** Override the TON amount attached for gas (in nanoTON) */\n gasAmount?: bigint\n /** Override the forward TON amount (in nanoTON) */\n forwardAmount?: bigint\n}\n\n/**\n * TON client implementation for the Exact payment scheme.\n *\n * Creates signed Jetton transfer messages that can be broadcast\n * by a facilitator to complete the payment.\n */\nexport class ExactTonScheme implements SchemeNetworkClient {\n readonly scheme = SCHEME_EXACT\n\n /**\n * Creates a new ExactTonScheme instance.\n *\n * @param signer - The TON signer for client operations\n * @param getJettonWalletAddress - Function to derive Jetton wallet address\n * @param config - Optional configuration overrides\n */\n constructor(\n private readonly signer: ClientTonSigner,\n private readonly getJettonWalletAddress: (\n ownerAddress: string,\n jettonMasterAddress: string,\n ) => Promise<string>,\n private readonly config: ExactTonSchemeConfig = {},\n ) {}\n\n /**\n * Creates a payment payload for the Exact scheme.\n *\n * The payload contains a pre-signed external message that performs\n * a Jetton transfer from the client to the recipient.\n *\n * @param t402Version - The t402 protocol version\n * @param paymentRequirements - The payment requirements\n * @returns Promise resolving to a payment payload\n */\n async createPaymentPayload(\n t402Version: number,\n paymentRequirements: PaymentRequirements,\n ): Promise<Pick<PaymentPayload, 't402Version' | 'payload'>> {\n // Normalize and validate network\n const network = normalizeNetwork(paymentRequirements.network)\n\n // Validate required fields\n if (!paymentRequirements.asset) {\n throw new Error('Asset (Jetton master address) is required')\n }\n if (!paymentRequirements.payTo) {\n throw new Error('PayTo address is required')\n }\n if (!paymentRequirements.amount) {\n throw new Error('Amount is required')\n }\n\n // Parse addresses\n const jettonMasterAddress = paymentRequirements.asset\n const destinationAddress = parseTonAddress(paymentRequirements.payTo)\n\n // Get client's Jetton wallet address\n const senderJettonWalletAddress = await this.getJettonWalletAddress(\n this.signer.address.toString(),\n jettonMasterAddress,\n )\n const senderJettonWallet = Address.parse(senderJettonWalletAddress)\n\n // Get current seqno for replay protection\n const seqno = await this.signer.getSeqno()\n\n // Calculate validity period\n const now = Math.floor(Date.now() / 1000)\n const validUntil = now + paymentRequirements.maxTimeoutSeconds\n\n // Generate unique query ID\n const queryId = generateQueryId()\n\n // Parse amount\n const jettonAmount = BigInt(paymentRequirements.amount)\n\n // Build Jetton transfer body\n const jettonTransferBody = buildJettonTransferBody({\n queryId,\n amount: jettonAmount,\n destination: destinationAddress,\n responseDestination: this.signer.address,\n forwardAmount: this.config.forwardAmount ?? DEFAULT_FORWARD_TON,\n })\n\n // Gas amount for the transfer\n const tonAmount = this.config.gasAmount ?? DEFAULT_JETTON_TRANSFER_TON\n\n // Sign the message\n const signedMessage = await this.signer.signMessage({\n to: senderJettonWallet,\n value: tonAmount,\n body: jettonTransferBody,\n sendMode: SendMode.PAY_GAS_SEPARATELY,\n timeout: paymentRequirements.maxTimeoutSeconds,\n })\n\n // Encode to base64\n const signedBoc = signedMessage.toBoc().toString('base64')\n\n // Build authorization metadata\n const authorization: ExactTonPayloadV2['authorization'] = {\n from: this.signer.address.toString({ bounceable: true }),\n to: paymentRequirements.payTo,\n jettonMaster: jettonMasterAddress,\n jettonAmount: jettonAmount.toString(),\n tonAmount: tonAmount.toString(),\n validUntil,\n seqno,\n queryId: queryId.toString(),\n }\n\n // Create payload\n const payload: ExactTonPayloadV2 = {\n signedBoc,\n authorization,\n }\n\n // Mark network as used\n void network\n\n return {\n t402Version,\n payload,\n }\n }\n}\n","/**\n * TON Network Constants\n *\n * This module provides constants for TON blockchain integration including:\n * - CAIP-2 network identifiers\n * - RPC endpoints\n * - Jetton transfer operation codes\n * - Default gas amounts\n */\n\n/**\n * CAIP-2 Network Identifiers for TON\n * Using simple identifiers for mainnet/testnet\n */\nexport const TON_MAINNET_CAIP2 = 'ton:mainnet'\nexport const TON_TESTNET_CAIP2 = 'ton:testnet'\n\n/**\n * Supported TON networks\n */\nexport const TON_NETWORKS = [TON_MAINNET_CAIP2, TON_TESTNET_CAIP2] as const\n\nexport type TonNetwork = (typeof TON_NETWORKS)[number]\n\n/**\n * Default RPC endpoints (TonCenter API v2)\n */\nexport const TON_MAINNET_ENDPOINT = 'https://toncenter.com/api/v2/jsonRPC'\nexport const TON_TESTNET_ENDPOINT = 'https://testnet.toncenter.com/api/v2/jsonRPC'\n\n/**\n * TON API v4 endpoints (for @ton/ton TonClient4)\n */\nexport const TON_MAINNET_V4_ENDPOINT = 'https://mainnet-v4.tonhubapi.com'\nexport const TON_TESTNET_V4_ENDPOINT = 'https://testnet-v4.tonhubapi.com'\n\n/**\n * Network endpoint mapping\n */\nexport const NETWORK_ENDPOINTS: Record<string, string> = {\n [TON_MAINNET_CAIP2]: TON_MAINNET_ENDPOINT,\n [TON_TESTNET_CAIP2]: TON_TESTNET_ENDPOINT,\n}\n\nexport const NETWORK_V4_ENDPOINTS: Record<string, string> = {\n [TON_MAINNET_CAIP2]: TON_MAINNET_V4_ENDPOINT,\n [TON_TESTNET_CAIP2]: TON_TESTNET_V4_ENDPOINT,\n}\n\n/**\n * Jetton Transfer Operation Codes (TEP-74)\n * @see https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md\n */\nexport const JETTON_TRANSFER_OP = 0x0f8a7ea5 // transfer\nexport const JETTON_INTERNAL_TRANSFER_OP = 0x178d4519 // internal_transfer\nexport const JETTON_TRANSFER_NOTIFICATION_OP = 0x7362d09c // transfer_notification\nexport const JETTON_BURN_OP = 0x595f07bc // burn\n\n/**\n * Default gas amounts for Jetton transfers\n * TON requires attaching TON for gas to internal messages\n */\nexport const DEFAULT_JETTON_TRANSFER_TON = 100_000_000n // 0.1 TON for gas\nexport const DEFAULT_FORWARD_TON = 1n // Minimal forward amount (notification)\nexport const MIN_JETTON_TRANSFER_TON = 50_000_000n // 0.05 TON minimum\n\n/**\n * Maximum gas amounts to prevent excessive fees\n */\nexport const MAX_JETTON_TRANSFER_TON = 500_000_000n // 0.5 TON maximum\n\n/**\n * Scheme identifier for exact payments\n */\nexport const SCHEME_EXACT = 'exact'\n\n/**\n * Default timeout for payment validity (in seconds)\n */\nexport const DEFAULT_VALIDITY_DURATION = 3600 // 1 hour\n\n/**\n * Address format constants\n */\nexport const TON_ADDRESS_LENGTH = 48 // Friendly format length (base64url)\nexport const TON_RAW_ADDRESS_LENGTH = 66 // Raw format: workchain:hash (0:64hex)\n","/**\n * TON Utility Functions\n *\n * Helper functions for TON address handling, message building,\n * and network operations.\n */\n\nimport { Address, beginCell, Cell } from '@ton/core'\nimport type { Network } from '@t402/core/types'\nimport {\n TON_MAINNET_CAIP2,\n TON_TESTNET_CAIP2,\n TON_NETWORKS,\n NETWORK_ENDPOINTS,\n JETTON_TRANSFER_OP,\n} from './constants.js'\n\n/**\n * Normalize network identifier to CAIP-2 format\n *\n * @param network - Network identifier (may be legacy format)\n * @returns Normalized CAIP-2 network identifier\n * @throws Error if network is not supported\n */\nexport function normalizeNetwork(network: Network): Network {\n // Already in CAIP-2 format\n if (network.startsWith('ton:')) {\n if (!TON_NETWORKS.includes(network as (typeof TON_NETWORKS)[number])) {\n throw new Error(`Unsupported TON network: ${network}`)\n }\n return network as Network\n }\n\n // Handle legacy format conversions\n const mapping: Record<string, Network> = {\n ton: TON_MAINNET_CAIP2 as Network,\n 'ton-mainnet': TON_MAINNET_CAIP2 as Network,\n mainnet: TON_MAINNET_CAIP2 as Network,\n 'ton-testnet': TON_TESTNET_CAIP2 as Network,\n testnet: TON_TESTNET_CAIP2 as Network,\n }\n\n const caip2 = mapping[network.toLowerCase()]\n if (!caip2) {\n throw new Error(`Unsupported TON network: ${network}`)\n }\n return caip2\n}\n\n/**\n * Get RPC endpoint for a network\n *\n * @param network - Network identifier\n * @returns RPC endpoint URL\n */\nexport function getEndpoint(network: Network): string {\n const caip2 = normalizeNetwork(network)\n const endpoint = NETWORK_ENDPOINTS[caip2]\n if (!endpoint) {\n throw new Error(`No endpoint configured for network: ${network}`)\n }\n return endpoint\n}\n\n/**\n * Check if a network identifier is a supported TON network\n *\n * @param network - Network identifier to check\n * @returns true if supported\n */\nexport function isTonNetwork(network: string): boolean {\n try {\n normalizeNetwork(network as Network)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Validate TON address format\n *\n * @param address - Address to validate\n * @returns true if valid TON address\n */\nexport function validateTonAddress(address: string): boolean {\n try {\n Address.parse(address)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Parse TON address from string\n *\n * @param address - Address string (friendly or raw format)\n * @returns Parsed Address object\n * @throws Error if invalid format\n */\nexport function parseTonAddress(address: string): Address {\n return Address.parse(address)\n}\n\n/**\n * Compare two TON addresses for equality\n * Handles different address formats (friendly, raw, bounceable/non-bounceable)\n *\n * @param addr1 - First address\n * @param addr2 - Second address\n * @returns true if addresses are equal\n */\nexport function addressesEqual(addr1: string, addr2: string): boolean {\n try {\n const a1 = Address.parse(addr1)\n const a2 = Address.parse(addr2)\n return a1.equals(a2)\n } catch {\n return false\n }\n}\n\n/**\n * Format address to friendly format\n *\n * @param address - Address to format\n * @param options - Formatting options\n * @returns Friendly format address string\n */\nexport function formatAddress(\n address: string | Address,\n options?: { bounceable?: boolean; testOnly?: boolean },\n): string {\n const addr = typeof address === 'string' ? Address.parse(address) : address\n return addr.toString({\n bounceable: options?.bounceable ?? true,\n testOnly: options?.testOnly ?? false,\n })\n}\n\n/**\n * Convert decimal amount to smallest units (e.g., nano-Jettons)\n *\n * @param decimalAmount - Amount in decimal format (e.g., \"1.50\")\n * @param decimals - Number of decimal places\n * @returns Amount in smallest units as string\n */\nexport function convertToJettonAmount(decimalAmount: string, decimals: number): string {\n const amount = parseFloat(decimalAmount)\n if (isNaN(amount)) {\n throw new Error(`Invalid amount: ${decimalAmount}`)\n }\n const jettonAmount = Math.floor(amount * Math.pow(10, decimals))\n return jettonAmount.toString()\n}\n\n/**\n * Convert smallest units to decimal amount\n *\n * @param jettonAmount - Amount in smallest units\n * @param decimals - Number of decimal places\n * @returns Amount in decimal format as string\n */\nexport function convertFromJettonAmount(jettonAmount: string | bigint, decimals: number): string {\n const amount = typeof jettonAmount === 'string' ? BigInt(jettonAmount) : jettonAmount\n const divisor = BigInt(Math.pow(10, decimals))\n const wholePart = amount / divisor\n const fractionalPart = amount % divisor\n\n if (fractionalPart === 0n) {\n return wholePart.toString()\n }\n\n const fractionalStr = fractionalPart.toString().padStart(decimals, '0')\n return `${wholePart}.${fractionalStr}`.replace(/\\.?0+$/, '')\n}\n\n/**\n * Generate a unique query ID for Jetton transfer\n * Uses timestamp + random component for uniqueness\n *\n * @returns BigInt query ID\n */\nexport function generateQueryId(): bigint {\n const timestamp = BigInt(Date.now())\n const random = BigInt(Math.floor(Math.random() * 1000000))\n return timestamp * 1000000n + random\n}\n\n/**\n * Build Jetton transfer message body (TEP-74)\n *\n * @param params - Transfer parameters\n * @returns Cell containing the transfer message\n */\nexport function buildJettonTransferBody(params: {\n queryId: bigint\n amount: bigint\n destination: Address\n responseDestination: Address\n forwardAmount?: bigint\n forwardPayload?: Cell\n}): Cell {\n const builder = beginCell()\n .storeUint(JETTON_TRANSFER_OP, 32) // op: transfer\n .storeUint(params.queryId, 64) // query_id\n .storeCoins(params.amount) // amount\n .storeAddress(params.destination) // destination\n .storeAddress(params.responseDestination) // response_destination\n .storeBit(false) // no custom payload\n\n // Forward amount (for notification)\n builder.storeCoins(params.forwardAmount ?? 1n)\n\n // Forward payload (optional)\n if (params.forwardPayload) {\n builder.storeBit(true).storeRef(params.forwardPayload)\n } else {\n builder.storeBit(false)\n }\n\n return builder.endCell()\n}\n\n/**\n * Parse Jetton transfer message from Cell\n *\n * @param body - Cell containing the message\n * @returns Parsed transfer parameters\n * @throws Error if not a valid Jetton transfer message\n */\nexport function parseJettonTransferBody(body: Cell): {\n op: number\n queryId: bigint\n amount: bigint\n destination: Address\n responseDestination: Address\n forwardAmount: bigint\n forwardPayload?: Cell\n} {\n const slice = body.beginParse()\n\n const op = slice.loadUint(32)\n if (op !== JETTON_TRANSFER_OP) {\n throw new Error(`Not a Jetton transfer message. Expected op ${JETTON_TRANSFER_OP}, got ${op}`)\n }\n\n const queryId = slice.loadUintBig(64)\n const amount = slice.loadCoins()\n const destination = slice.loadAddress()\n const responseDestination = slice.loadAddress()\n\n // Skip custom_payload bit\n const hasCustomPayload = slice.loadBit()\n if (hasCustomPayload) {\n slice.loadRef() // Skip custom payload\n }\n\n const forwardAmount = slice.loadCoins()\n\n // Forward payload\n const hasForwardPayload = slice.loadBit()\n const forwardPayload = hasForwardPayload ? slice.loadRef() : undefined\n\n return {\n op,\n queryId,\n amount,\n destination,\n responseDestination,\n forwardAmount,\n forwardPayload,\n }\n}\n\n/**\n * Calculate estimated gas for Jetton transfer\n * Based on typical TON network fees\n *\n * @param params - Optional parameters for estimation\n * @returns Estimated gas in nanoTON\n */\nexport function estimateJettonTransferGas(_params?: { hasForwardPayload?: boolean }): bigint {\n // Base cost for Jetton transfer (typical)\n // Includes: external message, wallet internal message, Jetton wallet message\n return 100_000_000n // 0.1 TON (conservative estimate)\n}\n","import { t402Client, PaymentPolicy } from '@t402/core/client'\nimport { Network } from '@t402/core/types'\nimport { ClientTonSigner } from '../../signer.js'\nimport { ExactTonScheme, ExactTonSchemeConfig } from './scheme.js'\n\n/**\n * Configuration options for registering TON schemes to an t402Client\n */\nexport interface TonClientConfig {\n /**\n * The TON signer to use for creating payment payloads\n */\n signer: ClientTonSigner\n\n /**\n * Function to get Jetton wallet address for a given owner and Jetton master\n * This is required for building Jetton transfer messages\n */\n getJettonWalletAddress: (ownerAddress: string, jettonMasterAddress: string) => Promise<string>\n\n /**\n * Optional policies to apply to the client\n */\n policies?: PaymentPolicy[]\n\n /**\n * Optional specific networks to register\n * If not provided, registers wildcard support (ton:*)\n */\n networks?: Network[]\n\n /**\n * Optional scheme configuration (gas amounts, etc.)\n */\n schemeConfig?: ExactTonSchemeConfig\n}\n\n/**\n * Registers TON exact payment schemes to an t402Client instance.\n *\n * This function registers:\n * - V2: ton:* wildcard scheme with ExactTonScheme (or specific networks if provided)\n *\n * @param client - The t402Client instance to register schemes to\n * @param config - Configuration for TON client registration\n * @returns The client instance for chaining\n *\n * @example\n * ```typescript\n * import { registerExactTonScheme } from \"@t402/ton/exact/client/register\";\n * import { t402Client } from \"@t402/core/client\";\n * import { TonClient } from \"@ton/ton\";\n *\n * const tonClient = new TonClient({ endpoint: \"...\" });\n * const wallet = WalletContractV4.create({ ... });\n *\n * const client = new t402Client();\n * registerExactTonScheme(client, {\n * signer: toClientTonSigner(wallet, tonClient),\n * getJettonWalletAddress: async (owner, master) => {\n * // Derive Jetton wallet address\n * return jettonWalletAddress;\n * }\n * });\n * ```\n */\nexport function registerExactTonScheme(client: t402Client, config: TonClientConfig): t402Client {\n const scheme = new ExactTonScheme(\n config.signer,\n config.getJettonWalletAddress,\n config.schemeConfig,\n )\n\n // Register V2 scheme\n if (config.networks && config.networks.length > 0) {\n // Register specific networks\n config.networks.forEach((network) => {\n client.register(network, scheme)\n })\n } else {\n // Register wildcard for all TON networks\n client.register('ton:*', scheme)\n }\n\n // Apply policies if provided\n if (config.policies) {\n config.policies.forEach((policy) => {\n client.registerPolicy(policy)\n })\n }\n\n return client\n}\n"],"mappings":"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","names":["import_core"]}
1
+ {"version":3,"sources":["../../../../src/exact/client/index.ts","../../../../src/exact/client/scheme.ts","../../../../src/constants.ts","../../../../src/utils.ts","../../../../src/exact/client/register.ts"],"sourcesContent":["export { ExactTonScheme } from './scheme.js'\nexport type { ExactTonSchemeConfig } from './scheme.js'\nexport { registerExactTonScheme } from './register.js'\nexport type { TonClientConfig } from './register.js'\n","/**\n * TON Client Scheme Implementation\n *\n * Creates payment payloads for TON Jetton transfers using the exact scheme.\n * Builds and signs external messages for Jetton wallet interactions.\n */\n\nimport { Address, SendMode } from '@ton/core'\nimport type { PaymentPayload, PaymentRequirements, SchemeNetworkClient } from '@t402/core/types'\nimport type { ClientTonSigner } from '../../signer.js'\nimport type { ExactTonPayloadV2 } from '../../types.js'\nimport { SCHEME_EXACT, DEFAULT_JETTON_TRANSFER_TON, DEFAULT_FORWARD_TON } from '../../constants.js'\nimport {\n generateQueryId,\n buildJettonTransferBody,\n normalizeNetwork,\n parseTonAddress,\n} from '../../utils.js'\n\n/**\n * Configuration for ExactTonScheme client\n */\nexport interface ExactTonSchemeConfig {\n /** Override the TON amount attached for gas (in nanoTON) */\n gasAmount?: bigint\n /** Override the forward TON amount (in nanoTON) */\n forwardAmount?: bigint\n}\n\n/**\n * TON client implementation for the Exact payment scheme.\n *\n * Creates signed Jetton transfer messages that can be broadcast\n * by a facilitator to complete the payment.\n */\nexport class ExactTonScheme implements SchemeNetworkClient {\n readonly scheme = SCHEME_EXACT\n\n /**\n * Creates a new ExactTonScheme instance.\n *\n * @param signer - The TON signer for client operations\n * @param getJettonWalletAddress - Function to derive Jetton wallet address\n * @param config - Optional configuration overrides\n */\n constructor(\n private readonly signer: ClientTonSigner,\n private readonly getJettonWalletAddress: (\n ownerAddress: string,\n jettonMasterAddress: string,\n ) => Promise<string>,\n private readonly config: ExactTonSchemeConfig = {},\n ) {}\n\n /**\n * Creates a payment payload for the Exact scheme.\n *\n * The payload contains a pre-signed external message that performs\n * a Jetton transfer from the client to the recipient.\n *\n * @param t402Version - The t402 protocol version\n * @param paymentRequirements - The payment requirements\n * @returns Promise resolving to a payment payload\n */\n async createPaymentPayload(\n t402Version: number,\n paymentRequirements: PaymentRequirements,\n ): Promise<Pick<PaymentPayload, 't402Version' | 'payload'>> {\n // Normalize and validate network\n const network = normalizeNetwork(paymentRequirements.network)\n\n // Validate required fields\n if (!paymentRequirements.asset) {\n throw new Error('Asset (Jetton master address) is required')\n }\n if (!paymentRequirements.payTo) {\n throw new Error('PayTo address is required')\n }\n if (!paymentRequirements.amount) {\n throw new Error('Amount is required')\n }\n\n // Parse addresses\n const jettonMasterAddress = paymentRequirements.asset\n const destinationAddress = parseTonAddress(paymentRequirements.payTo)\n\n // Get client's Jetton wallet address\n const senderJettonWalletAddress = await this.getJettonWalletAddress(\n this.signer.address.toString(),\n jettonMasterAddress,\n )\n const senderJettonWallet = Address.parse(senderJettonWalletAddress)\n\n // Get current seqno for replay protection\n const seqno = await this.signer.getSeqno()\n\n // Calculate validity period\n const now = Math.floor(Date.now() / 1000)\n const validUntil = now + paymentRequirements.maxTimeoutSeconds\n\n // Generate unique query ID\n const queryId = generateQueryId()\n\n // Parse amount\n const jettonAmount = BigInt(paymentRequirements.amount)\n\n // Build Jetton transfer body\n const jettonTransferBody = buildJettonTransferBody({\n queryId,\n amount: jettonAmount,\n destination: destinationAddress,\n responseDestination: this.signer.address,\n forwardAmount: this.config.forwardAmount ?? DEFAULT_FORWARD_TON,\n })\n\n // Gas amount for the transfer\n const tonAmount = this.config.gasAmount ?? DEFAULT_JETTON_TRANSFER_TON\n\n // Sign the message\n const signedMessage = await this.signer.signMessage({\n to: senderJettonWallet,\n value: tonAmount,\n body: jettonTransferBody,\n sendMode: SendMode.PAY_GAS_SEPARATELY,\n timeout: paymentRequirements.maxTimeoutSeconds,\n })\n\n // Encode to base64\n const signedBoc = signedMessage.toBoc().toString('base64')\n\n // Build authorization metadata\n const authorization: ExactTonPayloadV2['authorization'] = {\n from: this.signer.address.toString({ bounceable: true }),\n to: paymentRequirements.payTo,\n jettonMaster: jettonMasterAddress,\n jettonAmount: jettonAmount.toString(),\n tonAmount: tonAmount.toString(),\n validUntil,\n seqno,\n queryId: queryId.toString(),\n }\n\n // Create payload\n const payload: ExactTonPayloadV2 = {\n signedBoc,\n authorization,\n }\n\n // Mark network as used\n void network\n\n return {\n t402Version,\n payload,\n }\n }\n}\n","/**\n * TON Network Constants\n *\n * This module provides constants for TON blockchain integration including:\n * - CAIP-2 network identifiers\n * - RPC endpoints\n * - Jetton transfer operation codes\n * - Default gas amounts\n */\n\n/**\n * CAIP-2 Network Identifiers for TON\n * Using simple identifiers for mainnet/testnet\n */\nexport const TON_MAINNET_CAIP2 = 'ton:mainnet'\nexport const TON_TESTNET_CAIP2 = 'ton:testnet'\n\n/**\n * Supported TON networks\n */\nexport const TON_NETWORKS = [TON_MAINNET_CAIP2, TON_TESTNET_CAIP2] as const\n\nexport type TonNetwork = (typeof TON_NETWORKS)[number]\n\n/**\n * Default RPC endpoints (TonCenter API v2)\n */\nexport const TON_MAINNET_ENDPOINT = 'https://toncenter.com/api/v2/jsonRPC'\nexport const TON_TESTNET_ENDPOINT = 'https://testnet.toncenter.com/api/v2/jsonRPC'\n\n/**\n * TON API v4 endpoints (for @ton/ton TonClient4)\n */\nexport const TON_MAINNET_V4_ENDPOINT = 'https://mainnet-v4.tonhubapi.com'\nexport const TON_TESTNET_V4_ENDPOINT = 'https://testnet-v4.tonhubapi.com'\n\n/**\n * Network endpoint mapping\n */\nexport const NETWORK_ENDPOINTS: Record<string, string> = {\n [TON_MAINNET_CAIP2]: TON_MAINNET_ENDPOINT,\n [TON_TESTNET_CAIP2]: TON_TESTNET_ENDPOINT,\n}\n\nexport const NETWORK_V4_ENDPOINTS: Record<string, string> = {\n [TON_MAINNET_CAIP2]: TON_MAINNET_V4_ENDPOINT,\n [TON_TESTNET_CAIP2]: TON_TESTNET_V4_ENDPOINT,\n}\n\n/**\n * Jetton Transfer Operation Codes (TEP-74)\n * @see https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md\n */\nexport const JETTON_TRANSFER_OP = 0x0f8a7ea5 // transfer\nexport const JETTON_INTERNAL_TRANSFER_OP = 0x178d4519 // internal_transfer\nexport const JETTON_TRANSFER_NOTIFICATION_OP = 0x7362d09c // transfer_notification\nexport const JETTON_BURN_OP = 0x595f07bc // burn\n\n/**\n * Default gas amounts for Jetton transfers\n * TON requires attaching TON for gas to internal messages\n */\nexport const DEFAULT_JETTON_TRANSFER_TON = 100_000_000n // 0.1 TON for gas\nexport const DEFAULT_FORWARD_TON = 1n // Minimal forward amount (notification)\nexport const MIN_JETTON_TRANSFER_TON = 50_000_000n // 0.05 TON minimum\n\n/**\n * Maximum gas amounts to prevent excessive fees\n */\nexport const MAX_JETTON_TRANSFER_TON = 500_000_000n // 0.5 TON maximum\n\n/**\n * Scheme identifier for exact payments\n */\nexport const SCHEME_EXACT = 'exact'\n\n/**\n * Default timeout for payment validity (in seconds)\n */\nexport const DEFAULT_VALIDITY_DURATION = 3600 // 1 hour\n\n/**\n * Address format constants\n */\nexport const TON_ADDRESS_LENGTH = 48 // Friendly format length (base64url)\nexport const TON_RAW_ADDRESS_LENGTH = 66 // Raw format: workchain:hash (0:64hex)\n","/**\n * TON Utility Functions\n *\n * Helper functions for TON address handling, message building,\n * and network operations.\n */\n\nimport { Address, beginCell, Cell } from '@ton/core'\nimport type { Network } from '@t402/core/types'\nimport { cryptoRandomBigInt } from '@t402/core/utils'\nimport {\n TON_MAINNET_CAIP2,\n TON_TESTNET_CAIP2,\n TON_NETWORKS,\n NETWORK_ENDPOINTS,\n JETTON_TRANSFER_OP,\n} from './constants.js'\n\n/**\n * Normalize network identifier to CAIP-2 format\n *\n * @param network - Network identifier (may be legacy format)\n * @returns Normalized CAIP-2 network identifier\n * @throws Error if network is not supported\n */\nexport function normalizeNetwork(network: Network): Network {\n // Already in CAIP-2 format\n if (network.startsWith('ton:')) {\n if (!TON_NETWORKS.includes(network as (typeof TON_NETWORKS)[number])) {\n throw new Error(`Unsupported TON network: ${network}`)\n }\n return network as Network\n }\n\n // Handle legacy format conversions\n const mapping: Record<string, Network> = {\n ton: TON_MAINNET_CAIP2 as Network,\n 'ton-mainnet': TON_MAINNET_CAIP2 as Network,\n mainnet: TON_MAINNET_CAIP2 as Network,\n 'ton-testnet': TON_TESTNET_CAIP2 as Network,\n testnet: TON_TESTNET_CAIP2 as Network,\n }\n\n const caip2 = mapping[network.toLowerCase()]\n if (!caip2) {\n throw new Error(`Unsupported TON network: ${network}`)\n }\n return caip2\n}\n\n/**\n * Get RPC endpoint for a network\n *\n * @param network - Network identifier\n * @returns RPC endpoint URL\n */\nexport function getEndpoint(network: Network): string {\n const caip2 = normalizeNetwork(network)\n const endpoint = NETWORK_ENDPOINTS[caip2]\n if (!endpoint) {\n throw new Error(`No endpoint configured for network: ${network}`)\n }\n return endpoint\n}\n\n/**\n * Check if a network identifier is a supported TON network\n *\n * @param network - Network identifier to check\n * @returns true if supported\n */\nexport function isTonNetwork(network: string): boolean {\n try {\n normalizeNetwork(network as Network)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Validate TON address format\n *\n * @param address - Address to validate\n * @returns true if valid TON address\n */\nexport function validateTonAddress(address: string): boolean {\n try {\n Address.parse(address)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Parse TON address from string\n *\n * @param address - Address string (friendly or raw format)\n * @returns Parsed Address object\n * @throws Error if invalid format\n */\nexport function parseTonAddress(address: string): Address {\n return Address.parse(address)\n}\n\n/**\n * Compare two TON addresses for equality\n * Handles different address formats (friendly, raw, bounceable/non-bounceable)\n *\n * @param addr1 - First address\n * @param addr2 - Second address\n * @returns true if addresses are equal\n */\nexport function addressesEqual(addr1: string, addr2: string): boolean {\n try {\n const a1 = Address.parse(addr1)\n const a2 = Address.parse(addr2)\n return a1.equals(a2)\n } catch {\n return false\n }\n}\n\n/**\n * Format address to friendly format\n *\n * @param address - Address to format\n * @param options - Formatting options\n * @returns Friendly format address string\n */\nexport function formatAddress(\n address: string | Address,\n options?: { bounceable?: boolean; testOnly?: boolean },\n): string {\n const addr = typeof address === 'string' ? Address.parse(address) : address\n return addr.toString({\n bounceable: options?.bounceable ?? true,\n testOnly: options?.testOnly ?? false,\n })\n}\n\n/**\n * Convert decimal amount to smallest units (e.g., nano-Jettons)\n *\n * @param decimalAmount - Amount in decimal format (e.g., \"1.50\")\n * @param decimals - Number of decimal places\n * @returns Amount in smallest units as string\n */\nexport function convertToJettonAmount(decimalAmount: string, decimals: number): string {\n const amount = parseFloat(decimalAmount)\n if (isNaN(amount)) {\n throw new Error(`Invalid amount: ${decimalAmount}`)\n }\n const jettonAmount = Math.floor(amount * Math.pow(10, decimals))\n return jettonAmount.toString()\n}\n\n/**\n * Convert smallest units to decimal amount\n *\n * @param jettonAmount - Amount in smallest units\n * @param decimals - Number of decimal places\n * @returns Amount in decimal format as string\n */\nexport function convertFromJettonAmount(jettonAmount: string | bigint, decimals: number): string {\n const amount = typeof jettonAmount === 'string' ? BigInt(jettonAmount) : jettonAmount\n const divisor = BigInt(Math.pow(10, decimals))\n const wholePart = amount / divisor\n const fractionalPart = amount % divisor\n\n if (fractionalPart === 0n) {\n return wholePart.toString()\n }\n\n const fractionalStr = fractionalPart.toString().padStart(decimals, '0')\n return `${wholePart}.${fractionalStr}`.replace(/\\.?0+$/, '')\n}\n\n/**\n * Generate a unique query ID for Jetton transfer\n * Uses timestamp + cryptographically secure random component for uniqueness\n *\n * @returns BigInt query ID (64-bit)\n */\nexport function generateQueryId(): bigint {\n const timestamp = BigInt(Date.now())\n // Use 20 bits of cryptographic randomness (0-1048575)\n const random = cryptoRandomBigInt(20)\n return timestamp * 1048576n + random\n}\n\n/**\n * Build Jetton transfer message body (TEP-74)\n *\n * @param params - Transfer parameters\n * @returns Cell containing the transfer message\n */\nexport function buildJettonTransferBody(params: {\n queryId: bigint\n amount: bigint\n destination: Address\n responseDestination: Address\n forwardAmount?: bigint\n forwardPayload?: Cell\n}): Cell {\n const builder = beginCell()\n .storeUint(JETTON_TRANSFER_OP, 32) // op: transfer\n .storeUint(params.queryId, 64) // query_id\n .storeCoins(params.amount) // amount\n .storeAddress(params.destination) // destination\n .storeAddress(params.responseDestination) // response_destination\n .storeBit(false) // no custom payload\n\n // Forward amount (for notification)\n builder.storeCoins(params.forwardAmount ?? 1n)\n\n // Forward payload (optional)\n if (params.forwardPayload) {\n builder.storeBit(true).storeRef(params.forwardPayload)\n } else {\n builder.storeBit(false)\n }\n\n return builder.endCell()\n}\n\n/**\n * Parse Jetton transfer message from Cell\n *\n * @param body - Cell containing the message\n * @returns Parsed transfer parameters\n * @throws Error if not a valid Jetton transfer message\n */\nexport function parseJettonTransferBody(body: Cell): {\n op: number\n queryId: bigint\n amount: bigint\n destination: Address\n responseDestination: Address\n forwardAmount: bigint\n forwardPayload?: Cell\n} {\n const slice = body.beginParse()\n\n const op = slice.loadUint(32)\n if (op !== JETTON_TRANSFER_OP) {\n throw new Error(`Not a Jetton transfer message. Expected op ${JETTON_TRANSFER_OP}, got ${op}`)\n }\n\n const queryId = slice.loadUintBig(64)\n const amount = slice.loadCoins()\n const destination = slice.loadAddress()\n const responseDestination = slice.loadAddress()\n\n // Skip custom_payload bit\n const hasCustomPayload = slice.loadBit()\n if (hasCustomPayload) {\n slice.loadRef() // Skip custom payload\n }\n\n const forwardAmount = slice.loadCoins()\n\n // Forward payload\n const hasForwardPayload = slice.loadBit()\n const forwardPayload = hasForwardPayload ? slice.loadRef() : undefined\n\n return {\n op,\n queryId,\n amount,\n destination,\n responseDestination,\n forwardAmount,\n forwardPayload,\n }\n}\n\n/**\n * Calculate estimated gas for Jetton transfer\n * Based on typical TON network fees\n *\n * @param params - Optional parameters for estimation\n * @returns Estimated gas in nanoTON\n */\nexport function estimateJettonTransferGas(_params?: { hasForwardPayload?: boolean }): bigint {\n // Base cost for Jetton transfer (typical)\n // Includes: external message, wallet internal message, Jetton wallet message\n return 100_000_000n // 0.1 TON (conservative estimate)\n}\n","import { t402Client, PaymentPolicy } from '@t402/core/client'\nimport { Network } from '@t402/core/types'\nimport { ClientTonSigner } from '../../signer.js'\nimport { ExactTonScheme, ExactTonSchemeConfig } from './scheme.js'\n\n/**\n * Configuration options for registering TON schemes to an t402Client\n */\nexport interface TonClientConfig {\n /**\n * The TON signer to use for creating payment payloads\n */\n signer: ClientTonSigner\n\n /**\n * Function to get Jetton wallet address for a given owner and Jetton master\n * This is required for building Jetton transfer messages\n */\n getJettonWalletAddress: (ownerAddress: string, jettonMasterAddress: string) => Promise<string>\n\n /**\n * Optional policies to apply to the client\n */\n policies?: PaymentPolicy[]\n\n /**\n * Optional specific networks to register\n * If not provided, registers wildcard support (ton:*)\n */\n networks?: Network[]\n\n /**\n * Optional scheme configuration (gas amounts, etc.)\n */\n schemeConfig?: ExactTonSchemeConfig\n}\n\n/**\n * Registers TON exact payment schemes to an t402Client instance.\n *\n * This function registers:\n * - V2: ton:* wildcard scheme with ExactTonScheme (or specific networks if provided)\n *\n * @param client - The t402Client instance to register schemes to\n * @param config - Configuration for TON client registration\n * @returns The client instance for chaining\n *\n * @example\n * ```typescript\n * import { registerExactTonScheme } from \"@t402/ton/exact/client/register\";\n * import { t402Client } from \"@t402/core/client\";\n * import { TonClient } from \"@ton/ton\";\n *\n * const tonClient = new TonClient({ endpoint: \"...\" });\n * const wallet = WalletContractV4.create({ ... });\n *\n * const client = new t402Client();\n * registerExactTonScheme(client, {\n * signer: toClientTonSigner(wallet, tonClient),\n * getJettonWalletAddress: async (owner, master) => {\n * // Derive Jetton wallet address\n * return jettonWalletAddress;\n * }\n * });\n * ```\n */\nexport function registerExactTonScheme(client: t402Client, config: TonClientConfig): t402Client {\n const scheme = new ExactTonScheme(\n config.signer,\n config.getJettonWalletAddress,\n config.schemeConfig,\n )\n\n // Register V2 scheme\n if (config.networks && config.networks.length > 0) {\n // Register specific networks\n config.networks.forEach((network) => {\n client.register(network, scheme)\n })\n } else {\n // Register wildcard for all TON networks\n client.register('ton:*', scheme)\n }\n\n // Apply policies if provided\n if (config.policies) {\n config.policies.forEach((policy) => {\n client.registerPolicy(policy)\n })\n }\n\n return client\n}\n"],"mappings":"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","names":["import_core"]}
@@ -50,6 +50,7 @@ var SCHEME_EXACT = "exact";
50
50
 
51
51
  // src/utils.ts
52
52
  var import_core = require("@ton/core");
53
+ var import_utils = require("@t402/core/utils");
53
54
  function normalizeNetwork(network) {
54
55
  if (network.startsWith("ton:")) {
55
56
  if (!TON_NETWORKS.includes(network)) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/exact/facilitator/index.ts","../../../../src/exact/facilitator/scheme.ts","../../../../src/constants.ts","../../../../src/utils.ts","../../../../src/exact/facilitator/register.ts"],"sourcesContent":["export { ExactTonScheme } from './scheme.js'\nexport type { ExactTonSchemeConfig } from './scheme.js'\nexport { registerExactTonScheme } from './register.js'\nexport type { TonFacilitatorConfig } from './register.js'\n","/**\n * TON Facilitator Scheme Implementation\n *\n * Verifies and settles TON Jetton payments using the exact scheme.\n * Validates signed messages and broadcasts transactions to the network.\n */\n\nimport { Cell } from '@ton/core'\nimport type {\n PaymentPayload,\n PaymentRequirements,\n SchemeNetworkFacilitator,\n SettleResponse,\n VerifyResponse,\n} from '@t402/core/types'\nimport type { FacilitatorTonSigner } from '../../signer.js'\nimport type { ExactTonPayloadV2 } from '../../types.js'\nimport { SCHEME_EXACT } from '../../constants.js'\nimport { addressesEqual, normalizeNetwork } from '../../utils.js'\n\n/**\n * Configuration options for ExactTonScheme facilitator\n */\nexport interface ExactTonSchemeConfig {\n /** Whether this facilitator can sponsor gas for transactions */\n canSponsorGas?: boolean\n}\n\n/**\n * TON facilitator implementation for the Exact payment scheme.\n *\n * Verifies signed Jetton transfer messages and broadcasts them\n * to the TON network to complete payments.\n */\nexport class ExactTonScheme implements SchemeNetworkFacilitator {\n readonly scheme = SCHEME_EXACT\n readonly caipFamily = 'ton:*'\n private config: ExactTonSchemeConfig\n\n /**\n * Creates a new ExactTonScheme facilitator instance.\n *\n * @param signer - The TON signer for facilitator operations\n * @param config - Optional configuration\n */\n constructor(\n private readonly signer: FacilitatorTonSigner,\n config: ExactTonSchemeConfig = {},\n ) {\n this.config = config\n }\n\n /**\n * Get mechanism-specific extra data for the supported kinds endpoint.\n * For TON, optionally returns gas sponsor address if configured.\n *\n * @param network - The network identifier\n * @returns Extra data object or undefined\n */\n getExtra(network: string): Record<string, unknown> | undefined {\n void network\n\n if (this.config.canSponsorGas) {\n const addresses = this.signer.getAddresses()\n if (addresses.length > 0) {\n return {\n gasSponsor: addresses[0],\n }\n }\n }\n\n return undefined\n }\n\n /**\n * Get signer addresses used by this facilitator.\n * Returns all addresses this facilitator can use for operations.\n *\n * @param network - The network identifier\n * @returns Array of facilitator addresses\n */\n getSigners(network: string): string[] {\n void network\n return [...this.signer.getAddresses()]\n }\n\n /**\n * Verifies a payment payload.\n *\n * Performs comprehensive validation:\n * 1. Scheme and network matching\n * 2. BOC format validation\n * 3. Message structure verification\n * 4. Authorization expiry check\n * 5. Balance verification\n * 6. Amount and recipient validation\n *\n * @param payload - The payment payload to verify\n * @param requirements - The payment requirements\n * @returns Promise resolving to verification response\n */\n async verify(\n payload: PaymentPayload,\n requirements: PaymentRequirements,\n ): Promise<VerifyResponse> {\n const tonPayload = payload.payload as ExactTonPayloadV2 | undefined\n\n // Validate payload structure\n if (!tonPayload?.authorization?.from || !tonPayload?.signedBoc) {\n return {\n isValid: false,\n invalidReason: 'invalid_payload_structure',\n payer: '',\n }\n }\n\n const authorization = tonPayload.authorization\n\n // Step 1: Verify scheme matches\n if (payload.accepted.scheme !== SCHEME_EXACT || requirements.scheme !== SCHEME_EXACT) {\n return {\n isValid: false,\n invalidReason: 'unsupported_scheme',\n payer: authorization.from,\n }\n }\n\n // Step 2: Verify network matches\n try {\n const payloadNetwork = normalizeNetwork(payload.accepted.network)\n const requirementsNetwork = normalizeNetwork(requirements.network)\n\n if (payloadNetwork !== requirementsNetwork) {\n return {\n isValid: false,\n invalidReason: 'network_mismatch',\n payer: authorization.from,\n }\n }\n } catch {\n return {\n isValid: false,\n invalidReason: 'invalid_network',\n payer: authorization.from,\n }\n }\n\n // Step 3: Parse and validate the signed BOC\n try {\n Cell.fromBase64(tonPayload.signedBoc)\n } catch {\n return {\n isValid: false,\n invalidReason: 'invalid_boc_format',\n payer: authorization.from,\n }\n }\n\n // Step 4: Verify the message structure and parameters\n const verifyResult = await this.signer.verifyMessage({\n signedBoc: tonPayload.signedBoc,\n expectedFrom: authorization.from,\n expectedTransfer: {\n jettonAmount: BigInt(authorization.jettonAmount),\n destination: requirements.payTo,\n jettonMaster: requirements.asset,\n },\n })\n\n if (!verifyResult.valid) {\n return {\n isValid: false,\n invalidReason: verifyResult.reason || 'message_verification_failed',\n payer: authorization.from,\n }\n }\n\n // Step 5: Check validUntil is in the future (with 30 second buffer)\n const now = Math.floor(Date.now() / 1000)\n if (authorization.validUntil < now + 30) {\n return {\n isValid: false,\n invalidReason: 'authorization_expired',\n payer: authorization.from,\n }\n }\n\n // Step 6: Verify Jetton balance\n try {\n const balance = await this.signer.getJettonBalance({\n ownerAddress: authorization.from,\n jettonMasterAddress: requirements.asset,\n })\n\n if (balance < BigInt(requirements.amount)) {\n return {\n isValid: false,\n invalidReason: 'insufficient_jetton_balance',\n payer: authorization.from,\n }\n }\n } catch (error) {\n // If we can't check balance, log warning but continue\n console.warn('Could not verify Jetton balance:', error)\n }\n\n // Step 7: Verify amount is sufficient\n if (BigInt(authorization.jettonAmount) < BigInt(requirements.amount)) {\n return {\n isValid: false,\n invalidReason: 'insufficient_amount',\n payer: authorization.from,\n }\n }\n\n // Step 8: Verify recipient matches\n if (!addressesEqual(authorization.to, requirements.payTo)) {\n return {\n isValid: false,\n invalidReason: 'recipient_mismatch',\n payer: authorization.from,\n }\n }\n\n // Step 9: Verify Jetton master matches\n if (!addressesEqual(authorization.jettonMaster, requirements.asset)) {\n return {\n isValid: false,\n invalidReason: 'asset_mismatch',\n payer: authorization.from,\n }\n }\n\n // Step 10: Check seqno hasn't been used (replay protection)\n try {\n const currentSeqno = await this.signer.getSeqno(authorization.from)\n if (authorization.seqno < currentSeqno) {\n return {\n isValid: false,\n invalidReason: 'seqno_already_used',\n payer: authorization.from,\n }\n }\n if (authorization.seqno > currentSeqno) {\n return {\n isValid: false,\n invalidReason: 'seqno_too_high',\n payer: authorization.from,\n }\n }\n } catch (error) {\n console.warn('Could not verify seqno:', error)\n }\n\n // Step 11: Verify wallet is deployed\n try {\n const isDeployed = await this.signer.isDeployed(authorization.from)\n if (!isDeployed) {\n return {\n isValid: false,\n invalidReason: 'wallet_not_deployed',\n payer: authorization.from,\n }\n }\n } catch (error) {\n console.warn('Could not verify wallet deployment:', error)\n }\n\n return {\n isValid: true,\n invalidReason: undefined,\n payer: authorization.from,\n }\n }\n\n /**\n * Settles a payment by broadcasting the signed message.\n *\n * @param payload - The payment payload to settle\n * @param requirements - The payment requirements\n * @returns Promise resolving to settlement response\n */\n async settle(\n payload: PaymentPayload,\n requirements: PaymentRequirements,\n ): Promise<SettleResponse> {\n const tonPayload = payload.payload as ExactTonPayloadV2 | undefined\n\n // Validate payload structure\n if (!tonPayload?.authorization?.from || !tonPayload?.signedBoc) {\n return {\n success: false,\n network: payload.accepted.network,\n transaction: '',\n errorReason: 'invalid_payload_structure',\n payer: '',\n }\n }\n\n // Re-verify before settling\n const verifyResult = await this.verify(payload, requirements)\n if (!verifyResult.isValid) {\n return {\n success: false,\n network: payload.accepted.network,\n transaction: '',\n errorReason: verifyResult.invalidReason ?? 'verification_failed',\n payer: tonPayload.authorization.from,\n }\n }\n\n try {\n // Send the pre-signed external message\n const txHash = await this.signer.sendExternalMessage(tonPayload.signedBoc)\n\n // Wait for confirmation\n const confirmation = await this.signer.waitForTransaction({\n address: tonPayload.authorization.from,\n seqno: tonPayload.authorization.seqno + 1, // Wait for next seqno\n timeout: 60000, // 60 second timeout\n })\n\n if (!confirmation.success) {\n return {\n success: false,\n errorReason: confirmation.error || 'transaction_not_confirmed',\n transaction: txHash,\n network: payload.accepted.network,\n payer: tonPayload.authorization.from,\n }\n }\n\n return {\n success: true,\n transaction: confirmation.hash || txHash,\n network: payload.accepted.network,\n payer: tonPayload.authorization.from,\n }\n } catch (error) {\n console.error('Failed to settle TON transaction:', error)\n return {\n success: false,\n errorReason: 'transaction_failed',\n transaction: '',\n network: payload.accepted.network,\n payer: tonPayload.authorization.from,\n }\n }\n }\n}\n","/**\n * TON Network Constants\n *\n * This module provides constants for TON blockchain integration including:\n * - CAIP-2 network identifiers\n * - RPC endpoints\n * - Jetton transfer operation codes\n * - Default gas amounts\n */\n\n/**\n * CAIP-2 Network Identifiers for TON\n * Using simple identifiers for mainnet/testnet\n */\nexport const TON_MAINNET_CAIP2 = 'ton:mainnet'\nexport const TON_TESTNET_CAIP2 = 'ton:testnet'\n\n/**\n * Supported TON networks\n */\nexport const TON_NETWORKS = [TON_MAINNET_CAIP2, TON_TESTNET_CAIP2] as const\n\nexport type TonNetwork = (typeof TON_NETWORKS)[number]\n\n/**\n * Default RPC endpoints (TonCenter API v2)\n */\nexport const TON_MAINNET_ENDPOINT = 'https://toncenter.com/api/v2/jsonRPC'\nexport const TON_TESTNET_ENDPOINT = 'https://testnet.toncenter.com/api/v2/jsonRPC'\n\n/**\n * TON API v4 endpoints (for @ton/ton TonClient4)\n */\nexport const TON_MAINNET_V4_ENDPOINT = 'https://mainnet-v4.tonhubapi.com'\nexport const TON_TESTNET_V4_ENDPOINT = 'https://testnet-v4.tonhubapi.com'\n\n/**\n * Network endpoint mapping\n */\nexport const NETWORK_ENDPOINTS: Record<string, string> = {\n [TON_MAINNET_CAIP2]: TON_MAINNET_ENDPOINT,\n [TON_TESTNET_CAIP2]: TON_TESTNET_ENDPOINT,\n}\n\nexport const NETWORK_V4_ENDPOINTS: Record<string, string> = {\n [TON_MAINNET_CAIP2]: TON_MAINNET_V4_ENDPOINT,\n [TON_TESTNET_CAIP2]: TON_TESTNET_V4_ENDPOINT,\n}\n\n/**\n * Jetton Transfer Operation Codes (TEP-74)\n * @see https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md\n */\nexport const JETTON_TRANSFER_OP = 0x0f8a7ea5 // transfer\nexport const JETTON_INTERNAL_TRANSFER_OP = 0x178d4519 // internal_transfer\nexport const JETTON_TRANSFER_NOTIFICATION_OP = 0x7362d09c // transfer_notification\nexport const JETTON_BURN_OP = 0x595f07bc // burn\n\n/**\n * Default gas amounts for Jetton transfers\n * TON requires attaching TON for gas to internal messages\n */\nexport const DEFAULT_JETTON_TRANSFER_TON = 100_000_000n // 0.1 TON for gas\nexport const DEFAULT_FORWARD_TON = 1n // Minimal forward amount (notification)\nexport const MIN_JETTON_TRANSFER_TON = 50_000_000n // 0.05 TON minimum\n\n/**\n * Maximum gas amounts to prevent excessive fees\n */\nexport const MAX_JETTON_TRANSFER_TON = 500_000_000n // 0.5 TON maximum\n\n/**\n * Scheme identifier for exact payments\n */\nexport const SCHEME_EXACT = 'exact'\n\n/**\n * Default timeout for payment validity (in seconds)\n */\nexport const DEFAULT_VALIDITY_DURATION = 3600 // 1 hour\n\n/**\n * Address format constants\n */\nexport const TON_ADDRESS_LENGTH = 48 // Friendly format length (base64url)\nexport const TON_RAW_ADDRESS_LENGTH = 66 // Raw format: workchain:hash (0:64hex)\n","/**\n * TON Utility Functions\n *\n * Helper functions for TON address handling, message building,\n * and network operations.\n */\n\nimport { Address, beginCell, Cell } from '@ton/core'\nimport type { Network } from '@t402/core/types'\nimport {\n TON_MAINNET_CAIP2,\n TON_TESTNET_CAIP2,\n TON_NETWORKS,\n NETWORK_ENDPOINTS,\n JETTON_TRANSFER_OP,\n} from './constants.js'\n\n/**\n * Normalize network identifier to CAIP-2 format\n *\n * @param network - Network identifier (may be legacy format)\n * @returns Normalized CAIP-2 network identifier\n * @throws Error if network is not supported\n */\nexport function normalizeNetwork(network: Network): Network {\n // Already in CAIP-2 format\n if (network.startsWith('ton:')) {\n if (!TON_NETWORKS.includes(network as (typeof TON_NETWORKS)[number])) {\n throw new Error(`Unsupported TON network: ${network}`)\n }\n return network as Network\n }\n\n // Handle legacy format conversions\n const mapping: Record<string, Network> = {\n ton: TON_MAINNET_CAIP2 as Network,\n 'ton-mainnet': TON_MAINNET_CAIP2 as Network,\n mainnet: TON_MAINNET_CAIP2 as Network,\n 'ton-testnet': TON_TESTNET_CAIP2 as Network,\n testnet: TON_TESTNET_CAIP2 as Network,\n }\n\n const caip2 = mapping[network.toLowerCase()]\n if (!caip2) {\n throw new Error(`Unsupported TON network: ${network}`)\n }\n return caip2\n}\n\n/**\n * Get RPC endpoint for a network\n *\n * @param network - Network identifier\n * @returns RPC endpoint URL\n */\nexport function getEndpoint(network: Network): string {\n const caip2 = normalizeNetwork(network)\n const endpoint = NETWORK_ENDPOINTS[caip2]\n if (!endpoint) {\n throw new Error(`No endpoint configured for network: ${network}`)\n }\n return endpoint\n}\n\n/**\n * Check if a network identifier is a supported TON network\n *\n * @param network - Network identifier to check\n * @returns true if supported\n */\nexport function isTonNetwork(network: string): boolean {\n try {\n normalizeNetwork(network as Network)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Validate TON address format\n *\n * @param address - Address to validate\n * @returns true if valid TON address\n */\nexport function validateTonAddress(address: string): boolean {\n try {\n Address.parse(address)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Parse TON address from string\n *\n * @param address - Address string (friendly or raw format)\n * @returns Parsed Address object\n * @throws Error if invalid format\n */\nexport function parseTonAddress(address: string): Address {\n return Address.parse(address)\n}\n\n/**\n * Compare two TON addresses for equality\n * Handles different address formats (friendly, raw, bounceable/non-bounceable)\n *\n * @param addr1 - First address\n * @param addr2 - Second address\n * @returns true if addresses are equal\n */\nexport function addressesEqual(addr1: string, addr2: string): boolean {\n try {\n const a1 = Address.parse(addr1)\n const a2 = Address.parse(addr2)\n return a1.equals(a2)\n } catch {\n return false\n }\n}\n\n/**\n * Format address to friendly format\n *\n * @param address - Address to format\n * @param options - Formatting options\n * @returns Friendly format address string\n */\nexport function formatAddress(\n address: string | Address,\n options?: { bounceable?: boolean; testOnly?: boolean },\n): string {\n const addr = typeof address === 'string' ? Address.parse(address) : address\n return addr.toString({\n bounceable: options?.bounceable ?? true,\n testOnly: options?.testOnly ?? false,\n })\n}\n\n/**\n * Convert decimal amount to smallest units (e.g., nano-Jettons)\n *\n * @param decimalAmount - Amount in decimal format (e.g., \"1.50\")\n * @param decimals - Number of decimal places\n * @returns Amount in smallest units as string\n */\nexport function convertToJettonAmount(decimalAmount: string, decimals: number): string {\n const amount = parseFloat(decimalAmount)\n if (isNaN(amount)) {\n throw new Error(`Invalid amount: ${decimalAmount}`)\n }\n const jettonAmount = Math.floor(amount * Math.pow(10, decimals))\n return jettonAmount.toString()\n}\n\n/**\n * Convert smallest units to decimal amount\n *\n * @param jettonAmount - Amount in smallest units\n * @param decimals - Number of decimal places\n * @returns Amount in decimal format as string\n */\nexport function convertFromJettonAmount(jettonAmount: string | bigint, decimals: number): string {\n const amount = typeof jettonAmount === 'string' ? BigInt(jettonAmount) : jettonAmount\n const divisor = BigInt(Math.pow(10, decimals))\n const wholePart = amount / divisor\n const fractionalPart = amount % divisor\n\n if (fractionalPart === 0n) {\n return wholePart.toString()\n }\n\n const fractionalStr = fractionalPart.toString().padStart(decimals, '0')\n return `${wholePart}.${fractionalStr}`.replace(/\\.?0+$/, '')\n}\n\n/**\n * Generate a unique query ID for Jetton transfer\n * Uses timestamp + random component for uniqueness\n *\n * @returns BigInt query ID\n */\nexport function generateQueryId(): bigint {\n const timestamp = BigInt(Date.now())\n const random = BigInt(Math.floor(Math.random() * 1000000))\n return timestamp * 1000000n + random\n}\n\n/**\n * Build Jetton transfer message body (TEP-74)\n *\n * @param params - Transfer parameters\n * @returns Cell containing the transfer message\n */\nexport function buildJettonTransferBody(params: {\n queryId: bigint\n amount: bigint\n destination: Address\n responseDestination: Address\n forwardAmount?: bigint\n forwardPayload?: Cell\n}): Cell {\n const builder = beginCell()\n .storeUint(JETTON_TRANSFER_OP, 32) // op: transfer\n .storeUint(params.queryId, 64) // query_id\n .storeCoins(params.amount) // amount\n .storeAddress(params.destination) // destination\n .storeAddress(params.responseDestination) // response_destination\n .storeBit(false) // no custom payload\n\n // Forward amount (for notification)\n builder.storeCoins(params.forwardAmount ?? 1n)\n\n // Forward payload (optional)\n if (params.forwardPayload) {\n builder.storeBit(true).storeRef(params.forwardPayload)\n } else {\n builder.storeBit(false)\n }\n\n return builder.endCell()\n}\n\n/**\n * Parse Jetton transfer message from Cell\n *\n * @param body - Cell containing the message\n * @returns Parsed transfer parameters\n * @throws Error if not a valid Jetton transfer message\n */\nexport function parseJettonTransferBody(body: Cell): {\n op: number\n queryId: bigint\n amount: bigint\n destination: Address\n responseDestination: Address\n forwardAmount: bigint\n forwardPayload?: Cell\n} {\n const slice = body.beginParse()\n\n const op = slice.loadUint(32)\n if (op !== JETTON_TRANSFER_OP) {\n throw new Error(`Not a Jetton transfer message. Expected op ${JETTON_TRANSFER_OP}, got ${op}`)\n }\n\n const queryId = slice.loadUintBig(64)\n const amount = slice.loadCoins()\n const destination = slice.loadAddress()\n const responseDestination = slice.loadAddress()\n\n // Skip custom_payload bit\n const hasCustomPayload = slice.loadBit()\n if (hasCustomPayload) {\n slice.loadRef() // Skip custom payload\n }\n\n const forwardAmount = slice.loadCoins()\n\n // Forward payload\n const hasForwardPayload = slice.loadBit()\n const forwardPayload = hasForwardPayload ? slice.loadRef() : undefined\n\n return {\n op,\n queryId,\n amount,\n destination,\n responseDestination,\n forwardAmount,\n forwardPayload,\n }\n}\n\n/**\n * Calculate estimated gas for Jetton transfer\n * Based on typical TON network fees\n *\n * @param params - Optional parameters for estimation\n * @returns Estimated gas in nanoTON\n */\nexport function estimateJettonTransferGas(_params?: { hasForwardPayload?: boolean }): bigint {\n // Base cost for Jetton transfer (typical)\n // Includes: external message, wallet internal message, Jetton wallet message\n return 100_000_000n // 0.1 TON (conservative estimate)\n}\n","import { t402Facilitator } from '@t402/core/facilitator'\nimport { Network } from '@t402/core/types'\nimport { FacilitatorTonSigner } from '../../signer.js'\nimport { ExactTonScheme, ExactTonSchemeConfig } from './scheme.js'\n\n/**\n * Configuration options for registering TON schemes to an t402Facilitator\n */\nexport interface TonFacilitatorConfig {\n /**\n * The TON signer for facilitator operations (verify and settle)\n */\n signer: FacilitatorTonSigner\n\n /**\n * Networks to register (single network or array of networks)\n * Examples: \"ton:mainnet\", [\"ton:mainnet\", \"ton:testnet\"]\n */\n networks: Network | Network[]\n\n /**\n * Optional scheme configuration (gas sponsorship, etc.)\n */\n schemeConfig?: ExactTonSchemeConfig\n}\n\n/**\n * Registers TON exact payment schemes to an t402Facilitator instance.\n *\n * This function registers:\n * - V2: Specified networks with ExactTonScheme\n *\n * @param facilitator - The t402Facilitator instance to register schemes to\n * @param config - Configuration for TON facilitator registration\n * @returns The facilitator instance for chaining\n *\n * @example\n * ```typescript\n * import { registerExactTonScheme } from \"@t402/ton/exact/facilitator/register\";\n * import { t402Facilitator } from \"@t402/core/facilitator\";\n * import { TonClient } from \"@ton/ton\";\n *\n * const tonClient = new TonClient({ endpoint: \"...\" });\n * const facilitator = new t402Facilitator();\n *\n * // Single network\n * registerExactTonScheme(facilitator, {\n * signer: toFacilitatorTonSigner(tonClient),\n * networks: \"ton:mainnet\"\n * });\n *\n * // Multiple networks\n * registerExactTonScheme(facilitator, {\n * signer: toFacilitatorTonSigner(tonClient),\n * networks: [\"ton:mainnet\", \"ton:testnet\"]\n * });\n *\n * // With gas sponsorship\n * registerExactTonScheme(facilitator, {\n * signer: toFacilitatorTonSigner(tonClient),\n * networks: \"ton:mainnet\",\n * schemeConfig: { canSponsorGas: true }\n * });\n * ```\n */\nexport function registerExactTonScheme(\n facilitator: t402Facilitator,\n config: TonFacilitatorConfig,\n): t402Facilitator {\n // Register V2 scheme with specified networks\n facilitator.register(config.networks, new ExactTonScheme(config.signer, config.schemeConfig))\n\n return facilitator\n}\n"],"mappings":"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","names":["import_core"]}
1
+ {"version":3,"sources":["../../../../src/exact/facilitator/index.ts","../../../../src/exact/facilitator/scheme.ts","../../../../src/constants.ts","../../../../src/utils.ts","../../../../src/exact/facilitator/register.ts"],"sourcesContent":["export { ExactTonScheme } from './scheme.js'\nexport type { ExactTonSchemeConfig } from './scheme.js'\nexport { registerExactTonScheme } from './register.js'\nexport type { TonFacilitatorConfig } from './register.js'\n","/**\n * TON Facilitator Scheme Implementation\n *\n * Verifies and settles TON Jetton payments using the exact scheme.\n * Validates signed messages and broadcasts transactions to the network.\n */\n\nimport { Cell } from '@ton/core'\nimport type {\n PaymentPayload,\n PaymentRequirements,\n SchemeNetworkFacilitator,\n SettleResponse,\n VerifyResponse,\n} from '@t402/core/types'\nimport type { FacilitatorTonSigner } from '../../signer.js'\nimport type { ExactTonPayloadV2 } from '../../types.js'\nimport { SCHEME_EXACT } from '../../constants.js'\nimport { addressesEqual, normalizeNetwork } from '../../utils.js'\n\n/**\n * Configuration options for ExactTonScheme facilitator\n */\nexport interface ExactTonSchemeConfig {\n /** Whether this facilitator can sponsor gas for transactions */\n canSponsorGas?: boolean\n}\n\n/**\n * TON facilitator implementation for the Exact payment scheme.\n *\n * Verifies signed Jetton transfer messages and broadcasts them\n * to the TON network to complete payments.\n */\nexport class ExactTonScheme implements SchemeNetworkFacilitator {\n readonly scheme = SCHEME_EXACT\n readonly caipFamily = 'ton:*'\n private config: ExactTonSchemeConfig\n\n /**\n * Creates a new ExactTonScheme facilitator instance.\n *\n * @param signer - The TON signer for facilitator operations\n * @param config - Optional configuration\n */\n constructor(\n private readonly signer: FacilitatorTonSigner,\n config: ExactTonSchemeConfig = {},\n ) {\n this.config = config\n }\n\n /**\n * Get mechanism-specific extra data for the supported kinds endpoint.\n * For TON, optionally returns gas sponsor address if configured.\n *\n * @param network - The network identifier\n * @returns Extra data object or undefined\n */\n getExtra(network: string): Record<string, unknown> | undefined {\n void network\n\n if (this.config.canSponsorGas) {\n const addresses = this.signer.getAddresses()\n if (addresses.length > 0) {\n return {\n gasSponsor: addresses[0],\n }\n }\n }\n\n return undefined\n }\n\n /**\n * Get signer addresses used by this facilitator.\n * Returns all addresses this facilitator can use for operations.\n *\n * @param network - The network identifier\n * @returns Array of facilitator addresses\n */\n getSigners(network: string): string[] {\n void network\n return [...this.signer.getAddresses()]\n }\n\n /**\n * Verifies a payment payload.\n *\n * Performs comprehensive validation:\n * 1. Scheme and network matching\n * 2. BOC format validation\n * 3. Message structure verification\n * 4. Authorization expiry check\n * 5. Balance verification\n * 6. Amount and recipient validation\n *\n * @param payload - The payment payload to verify\n * @param requirements - The payment requirements\n * @returns Promise resolving to verification response\n */\n async verify(\n payload: PaymentPayload,\n requirements: PaymentRequirements,\n ): Promise<VerifyResponse> {\n const tonPayload = payload.payload as ExactTonPayloadV2 | undefined\n\n // Validate payload structure\n if (!tonPayload?.authorization?.from || !tonPayload?.signedBoc) {\n return {\n isValid: false,\n invalidReason: 'invalid_payload_structure',\n payer: '',\n }\n }\n\n const authorization = tonPayload.authorization\n\n // Step 1: Verify scheme matches\n if (payload.accepted.scheme !== SCHEME_EXACT || requirements.scheme !== SCHEME_EXACT) {\n return {\n isValid: false,\n invalidReason: 'unsupported_scheme',\n payer: authorization.from,\n }\n }\n\n // Step 2: Verify network matches\n try {\n const payloadNetwork = normalizeNetwork(payload.accepted.network)\n const requirementsNetwork = normalizeNetwork(requirements.network)\n\n if (payloadNetwork !== requirementsNetwork) {\n return {\n isValid: false,\n invalidReason: 'network_mismatch',\n payer: authorization.from,\n }\n }\n } catch {\n return {\n isValid: false,\n invalidReason: 'invalid_network',\n payer: authorization.from,\n }\n }\n\n // Step 3: Parse and validate the signed BOC\n try {\n Cell.fromBase64(tonPayload.signedBoc)\n } catch {\n return {\n isValid: false,\n invalidReason: 'invalid_boc_format',\n payer: authorization.from,\n }\n }\n\n // Step 4: Verify the message structure and parameters\n const verifyResult = await this.signer.verifyMessage({\n signedBoc: tonPayload.signedBoc,\n expectedFrom: authorization.from,\n expectedTransfer: {\n jettonAmount: BigInt(authorization.jettonAmount),\n destination: requirements.payTo,\n jettonMaster: requirements.asset,\n },\n })\n\n if (!verifyResult.valid) {\n return {\n isValid: false,\n invalidReason: verifyResult.reason || 'message_verification_failed',\n payer: authorization.from,\n }\n }\n\n // Step 5: Check validUntil is in the future (with 30 second buffer)\n const now = Math.floor(Date.now() / 1000)\n if (authorization.validUntil < now + 30) {\n return {\n isValid: false,\n invalidReason: 'authorization_expired',\n payer: authorization.from,\n }\n }\n\n // Step 6: Verify Jetton balance\n try {\n const balance = await this.signer.getJettonBalance({\n ownerAddress: authorization.from,\n jettonMasterAddress: requirements.asset,\n })\n\n if (balance < BigInt(requirements.amount)) {\n return {\n isValid: false,\n invalidReason: 'insufficient_jetton_balance',\n payer: authorization.from,\n }\n }\n } catch (error) {\n // If we can't check balance, log warning but continue\n console.warn('Could not verify Jetton balance:', error)\n }\n\n // Step 7: Verify amount is sufficient\n if (BigInt(authorization.jettonAmount) < BigInt(requirements.amount)) {\n return {\n isValid: false,\n invalidReason: 'insufficient_amount',\n payer: authorization.from,\n }\n }\n\n // Step 8: Verify recipient matches\n if (!addressesEqual(authorization.to, requirements.payTo)) {\n return {\n isValid: false,\n invalidReason: 'recipient_mismatch',\n payer: authorization.from,\n }\n }\n\n // Step 9: Verify Jetton master matches\n if (!addressesEqual(authorization.jettonMaster, requirements.asset)) {\n return {\n isValid: false,\n invalidReason: 'asset_mismatch',\n payer: authorization.from,\n }\n }\n\n // Step 10: Check seqno hasn't been used (replay protection)\n try {\n const currentSeqno = await this.signer.getSeqno(authorization.from)\n if (authorization.seqno < currentSeqno) {\n return {\n isValid: false,\n invalidReason: 'seqno_already_used',\n payer: authorization.from,\n }\n }\n if (authorization.seqno > currentSeqno) {\n return {\n isValid: false,\n invalidReason: 'seqno_too_high',\n payer: authorization.from,\n }\n }\n } catch (error) {\n console.warn('Could not verify seqno:', error)\n }\n\n // Step 11: Verify wallet is deployed\n try {\n const isDeployed = await this.signer.isDeployed(authorization.from)\n if (!isDeployed) {\n return {\n isValid: false,\n invalidReason: 'wallet_not_deployed',\n payer: authorization.from,\n }\n }\n } catch (error) {\n console.warn('Could not verify wallet deployment:', error)\n }\n\n return {\n isValid: true,\n invalidReason: undefined,\n payer: authorization.from,\n }\n }\n\n /**\n * Settles a payment by broadcasting the signed message.\n *\n * @param payload - The payment payload to settle\n * @param requirements - The payment requirements\n * @returns Promise resolving to settlement response\n */\n async settle(\n payload: PaymentPayload,\n requirements: PaymentRequirements,\n ): Promise<SettleResponse> {\n const tonPayload = payload.payload as ExactTonPayloadV2 | undefined\n\n // Validate payload structure\n if (!tonPayload?.authorization?.from || !tonPayload?.signedBoc) {\n return {\n success: false,\n network: payload.accepted.network,\n transaction: '',\n errorReason: 'invalid_payload_structure',\n payer: '',\n }\n }\n\n // Re-verify before settling\n const verifyResult = await this.verify(payload, requirements)\n if (!verifyResult.isValid) {\n return {\n success: false,\n network: payload.accepted.network,\n transaction: '',\n errorReason: verifyResult.invalidReason ?? 'verification_failed',\n payer: tonPayload.authorization.from,\n }\n }\n\n try {\n // Send the pre-signed external message\n const txHash = await this.signer.sendExternalMessage(tonPayload.signedBoc)\n\n // Wait for confirmation\n const confirmation = await this.signer.waitForTransaction({\n address: tonPayload.authorization.from,\n seqno: tonPayload.authorization.seqno + 1, // Wait for next seqno\n timeout: 60000, // 60 second timeout\n })\n\n if (!confirmation.success) {\n return {\n success: false,\n errorReason: confirmation.error || 'transaction_not_confirmed',\n transaction: txHash,\n network: payload.accepted.network,\n payer: tonPayload.authorization.from,\n }\n }\n\n return {\n success: true,\n transaction: confirmation.hash || txHash,\n network: payload.accepted.network,\n payer: tonPayload.authorization.from,\n }\n } catch (error) {\n console.error('Failed to settle TON transaction:', error)\n return {\n success: false,\n errorReason: 'transaction_failed',\n transaction: '',\n network: payload.accepted.network,\n payer: tonPayload.authorization.from,\n }\n }\n }\n}\n","/**\n * TON Network Constants\n *\n * This module provides constants for TON blockchain integration including:\n * - CAIP-2 network identifiers\n * - RPC endpoints\n * - Jetton transfer operation codes\n * - Default gas amounts\n */\n\n/**\n * CAIP-2 Network Identifiers for TON\n * Using simple identifiers for mainnet/testnet\n */\nexport const TON_MAINNET_CAIP2 = 'ton:mainnet'\nexport const TON_TESTNET_CAIP2 = 'ton:testnet'\n\n/**\n * Supported TON networks\n */\nexport const TON_NETWORKS = [TON_MAINNET_CAIP2, TON_TESTNET_CAIP2] as const\n\nexport type TonNetwork = (typeof TON_NETWORKS)[number]\n\n/**\n * Default RPC endpoints (TonCenter API v2)\n */\nexport const TON_MAINNET_ENDPOINT = 'https://toncenter.com/api/v2/jsonRPC'\nexport const TON_TESTNET_ENDPOINT = 'https://testnet.toncenter.com/api/v2/jsonRPC'\n\n/**\n * TON API v4 endpoints (for @ton/ton TonClient4)\n */\nexport const TON_MAINNET_V4_ENDPOINT = 'https://mainnet-v4.tonhubapi.com'\nexport const TON_TESTNET_V4_ENDPOINT = 'https://testnet-v4.tonhubapi.com'\n\n/**\n * Network endpoint mapping\n */\nexport const NETWORK_ENDPOINTS: Record<string, string> = {\n [TON_MAINNET_CAIP2]: TON_MAINNET_ENDPOINT,\n [TON_TESTNET_CAIP2]: TON_TESTNET_ENDPOINT,\n}\n\nexport const NETWORK_V4_ENDPOINTS: Record<string, string> = {\n [TON_MAINNET_CAIP2]: TON_MAINNET_V4_ENDPOINT,\n [TON_TESTNET_CAIP2]: TON_TESTNET_V4_ENDPOINT,\n}\n\n/**\n * Jetton Transfer Operation Codes (TEP-74)\n * @see https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md\n */\nexport const JETTON_TRANSFER_OP = 0x0f8a7ea5 // transfer\nexport const JETTON_INTERNAL_TRANSFER_OP = 0x178d4519 // internal_transfer\nexport const JETTON_TRANSFER_NOTIFICATION_OP = 0x7362d09c // transfer_notification\nexport const JETTON_BURN_OP = 0x595f07bc // burn\n\n/**\n * Default gas amounts for Jetton transfers\n * TON requires attaching TON for gas to internal messages\n */\nexport const DEFAULT_JETTON_TRANSFER_TON = 100_000_000n // 0.1 TON for gas\nexport const DEFAULT_FORWARD_TON = 1n // Minimal forward amount (notification)\nexport const MIN_JETTON_TRANSFER_TON = 50_000_000n // 0.05 TON minimum\n\n/**\n * Maximum gas amounts to prevent excessive fees\n */\nexport const MAX_JETTON_TRANSFER_TON = 500_000_000n // 0.5 TON maximum\n\n/**\n * Scheme identifier for exact payments\n */\nexport const SCHEME_EXACT = 'exact'\n\n/**\n * Default timeout for payment validity (in seconds)\n */\nexport const DEFAULT_VALIDITY_DURATION = 3600 // 1 hour\n\n/**\n * Address format constants\n */\nexport const TON_ADDRESS_LENGTH = 48 // Friendly format length (base64url)\nexport const TON_RAW_ADDRESS_LENGTH = 66 // Raw format: workchain:hash (0:64hex)\n","/**\n * TON Utility Functions\n *\n * Helper functions for TON address handling, message building,\n * and network operations.\n */\n\nimport { Address, beginCell, Cell } from '@ton/core'\nimport type { Network } from '@t402/core/types'\nimport { cryptoRandomBigInt } from '@t402/core/utils'\nimport {\n TON_MAINNET_CAIP2,\n TON_TESTNET_CAIP2,\n TON_NETWORKS,\n NETWORK_ENDPOINTS,\n JETTON_TRANSFER_OP,\n} from './constants.js'\n\n/**\n * Normalize network identifier to CAIP-2 format\n *\n * @param network - Network identifier (may be legacy format)\n * @returns Normalized CAIP-2 network identifier\n * @throws Error if network is not supported\n */\nexport function normalizeNetwork(network: Network): Network {\n // Already in CAIP-2 format\n if (network.startsWith('ton:')) {\n if (!TON_NETWORKS.includes(network as (typeof TON_NETWORKS)[number])) {\n throw new Error(`Unsupported TON network: ${network}`)\n }\n return network as Network\n }\n\n // Handle legacy format conversions\n const mapping: Record<string, Network> = {\n ton: TON_MAINNET_CAIP2 as Network,\n 'ton-mainnet': TON_MAINNET_CAIP2 as Network,\n mainnet: TON_MAINNET_CAIP2 as Network,\n 'ton-testnet': TON_TESTNET_CAIP2 as Network,\n testnet: TON_TESTNET_CAIP2 as Network,\n }\n\n const caip2 = mapping[network.toLowerCase()]\n if (!caip2) {\n throw new Error(`Unsupported TON network: ${network}`)\n }\n return caip2\n}\n\n/**\n * Get RPC endpoint for a network\n *\n * @param network - Network identifier\n * @returns RPC endpoint URL\n */\nexport function getEndpoint(network: Network): string {\n const caip2 = normalizeNetwork(network)\n const endpoint = NETWORK_ENDPOINTS[caip2]\n if (!endpoint) {\n throw new Error(`No endpoint configured for network: ${network}`)\n }\n return endpoint\n}\n\n/**\n * Check if a network identifier is a supported TON network\n *\n * @param network - Network identifier to check\n * @returns true if supported\n */\nexport function isTonNetwork(network: string): boolean {\n try {\n normalizeNetwork(network as Network)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Validate TON address format\n *\n * @param address - Address to validate\n * @returns true if valid TON address\n */\nexport function validateTonAddress(address: string): boolean {\n try {\n Address.parse(address)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Parse TON address from string\n *\n * @param address - Address string (friendly or raw format)\n * @returns Parsed Address object\n * @throws Error if invalid format\n */\nexport function parseTonAddress(address: string): Address {\n return Address.parse(address)\n}\n\n/**\n * Compare two TON addresses for equality\n * Handles different address formats (friendly, raw, bounceable/non-bounceable)\n *\n * @param addr1 - First address\n * @param addr2 - Second address\n * @returns true if addresses are equal\n */\nexport function addressesEqual(addr1: string, addr2: string): boolean {\n try {\n const a1 = Address.parse(addr1)\n const a2 = Address.parse(addr2)\n return a1.equals(a2)\n } catch {\n return false\n }\n}\n\n/**\n * Format address to friendly format\n *\n * @param address - Address to format\n * @param options - Formatting options\n * @returns Friendly format address string\n */\nexport function formatAddress(\n address: string | Address,\n options?: { bounceable?: boolean; testOnly?: boolean },\n): string {\n const addr = typeof address === 'string' ? Address.parse(address) : address\n return addr.toString({\n bounceable: options?.bounceable ?? true,\n testOnly: options?.testOnly ?? false,\n })\n}\n\n/**\n * Convert decimal amount to smallest units (e.g., nano-Jettons)\n *\n * @param decimalAmount - Amount in decimal format (e.g., \"1.50\")\n * @param decimals - Number of decimal places\n * @returns Amount in smallest units as string\n */\nexport function convertToJettonAmount(decimalAmount: string, decimals: number): string {\n const amount = parseFloat(decimalAmount)\n if (isNaN(amount)) {\n throw new Error(`Invalid amount: ${decimalAmount}`)\n }\n const jettonAmount = Math.floor(amount * Math.pow(10, decimals))\n return jettonAmount.toString()\n}\n\n/**\n * Convert smallest units to decimal amount\n *\n * @param jettonAmount - Amount in smallest units\n * @param decimals - Number of decimal places\n * @returns Amount in decimal format as string\n */\nexport function convertFromJettonAmount(jettonAmount: string | bigint, decimals: number): string {\n const amount = typeof jettonAmount === 'string' ? BigInt(jettonAmount) : jettonAmount\n const divisor = BigInt(Math.pow(10, decimals))\n const wholePart = amount / divisor\n const fractionalPart = amount % divisor\n\n if (fractionalPart === 0n) {\n return wholePart.toString()\n }\n\n const fractionalStr = fractionalPart.toString().padStart(decimals, '0')\n return `${wholePart}.${fractionalStr}`.replace(/\\.?0+$/, '')\n}\n\n/**\n * Generate a unique query ID for Jetton transfer\n * Uses timestamp + cryptographically secure random component for uniqueness\n *\n * @returns BigInt query ID (64-bit)\n */\nexport function generateQueryId(): bigint {\n const timestamp = BigInt(Date.now())\n // Use 20 bits of cryptographic randomness (0-1048575)\n const random = cryptoRandomBigInt(20)\n return timestamp * 1048576n + random\n}\n\n/**\n * Build Jetton transfer message body (TEP-74)\n *\n * @param params - Transfer parameters\n * @returns Cell containing the transfer message\n */\nexport function buildJettonTransferBody(params: {\n queryId: bigint\n amount: bigint\n destination: Address\n responseDestination: Address\n forwardAmount?: bigint\n forwardPayload?: Cell\n}): Cell {\n const builder = beginCell()\n .storeUint(JETTON_TRANSFER_OP, 32) // op: transfer\n .storeUint(params.queryId, 64) // query_id\n .storeCoins(params.amount) // amount\n .storeAddress(params.destination) // destination\n .storeAddress(params.responseDestination) // response_destination\n .storeBit(false) // no custom payload\n\n // Forward amount (for notification)\n builder.storeCoins(params.forwardAmount ?? 1n)\n\n // Forward payload (optional)\n if (params.forwardPayload) {\n builder.storeBit(true).storeRef(params.forwardPayload)\n } else {\n builder.storeBit(false)\n }\n\n return builder.endCell()\n}\n\n/**\n * Parse Jetton transfer message from Cell\n *\n * @param body - Cell containing the message\n * @returns Parsed transfer parameters\n * @throws Error if not a valid Jetton transfer message\n */\nexport function parseJettonTransferBody(body: Cell): {\n op: number\n queryId: bigint\n amount: bigint\n destination: Address\n responseDestination: Address\n forwardAmount: bigint\n forwardPayload?: Cell\n} {\n const slice = body.beginParse()\n\n const op = slice.loadUint(32)\n if (op !== JETTON_TRANSFER_OP) {\n throw new Error(`Not a Jetton transfer message. Expected op ${JETTON_TRANSFER_OP}, got ${op}`)\n }\n\n const queryId = slice.loadUintBig(64)\n const amount = slice.loadCoins()\n const destination = slice.loadAddress()\n const responseDestination = slice.loadAddress()\n\n // Skip custom_payload bit\n const hasCustomPayload = slice.loadBit()\n if (hasCustomPayload) {\n slice.loadRef() // Skip custom payload\n }\n\n const forwardAmount = slice.loadCoins()\n\n // Forward payload\n const hasForwardPayload = slice.loadBit()\n const forwardPayload = hasForwardPayload ? slice.loadRef() : undefined\n\n return {\n op,\n queryId,\n amount,\n destination,\n responseDestination,\n forwardAmount,\n forwardPayload,\n }\n}\n\n/**\n * Calculate estimated gas for Jetton transfer\n * Based on typical TON network fees\n *\n * @param params - Optional parameters for estimation\n * @returns Estimated gas in nanoTON\n */\nexport function estimateJettonTransferGas(_params?: { hasForwardPayload?: boolean }): bigint {\n // Base cost for Jetton transfer (typical)\n // Includes: external message, wallet internal message, Jetton wallet message\n return 100_000_000n // 0.1 TON (conservative estimate)\n}\n","import { t402Facilitator } from '@t402/core/facilitator'\nimport { Network } from '@t402/core/types'\nimport { FacilitatorTonSigner } from '../../signer.js'\nimport { ExactTonScheme, ExactTonSchemeConfig } from './scheme.js'\n\n/**\n * Configuration options for registering TON schemes to an t402Facilitator\n */\nexport interface TonFacilitatorConfig {\n /**\n * The TON signer for facilitator operations (verify and settle)\n */\n signer: FacilitatorTonSigner\n\n /**\n * Networks to register (single network or array of networks)\n * Examples: \"ton:mainnet\", [\"ton:mainnet\", \"ton:testnet\"]\n */\n networks: Network | Network[]\n\n /**\n * Optional scheme configuration (gas sponsorship, etc.)\n */\n schemeConfig?: ExactTonSchemeConfig\n}\n\n/**\n * Registers TON exact payment schemes to an t402Facilitator instance.\n *\n * This function registers:\n * - V2: Specified networks with ExactTonScheme\n *\n * @param facilitator - The t402Facilitator instance to register schemes to\n * @param config - Configuration for TON facilitator registration\n * @returns The facilitator instance for chaining\n *\n * @example\n * ```typescript\n * import { registerExactTonScheme } from \"@t402/ton/exact/facilitator/register\";\n * import { t402Facilitator } from \"@t402/core/facilitator\";\n * import { TonClient } from \"@ton/ton\";\n *\n * const tonClient = new TonClient({ endpoint: \"...\" });\n * const facilitator = new t402Facilitator();\n *\n * // Single network\n * registerExactTonScheme(facilitator, {\n * signer: toFacilitatorTonSigner(tonClient),\n * networks: \"ton:mainnet\"\n * });\n *\n * // Multiple networks\n * registerExactTonScheme(facilitator, {\n * signer: toFacilitatorTonSigner(tonClient),\n * networks: [\"ton:mainnet\", \"ton:testnet\"]\n * });\n *\n * // With gas sponsorship\n * registerExactTonScheme(facilitator, {\n * signer: toFacilitatorTonSigner(tonClient),\n * networks: \"ton:mainnet\",\n * schemeConfig: { canSponsorGas: true }\n * });\n * ```\n */\nexport function registerExactTonScheme(\n facilitator: t402Facilitator,\n config: TonFacilitatorConfig,\n): t402Facilitator {\n // Register V2 scheme with specified networks\n facilitator.register(config.networks, new ExactTonScheme(config.signer, config.schemeConfig))\n\n return facilitator\n}\n"],"mappings":"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","names":["import_core"]}
@@ -49,8 +49,9 @@ var SCHEME_EXACT = "exact";
49
49
  var USDT_ADDRESSES = {
50
50
  // TON Mainnet - Official Tether USDT
51
51
  [TON_MAINNET_CAIP2]: "EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs",
52
- // TON Testnet - Test USDT (may vary)
53
- [TON_TESTNET_CAIP2]: "kQBqSpvo4S87mX9tTc4FX3Sfqf4uSp3Tx-Fz4RBUfTRWBx"
52
+ // TON Testnet - Test USDT from TON documentation
53
+ // @see https://docs.ton.org/v3/guidelines/dapps/cookbook
54
+ [TON_TESTNET_CAIP2]: "kQD0GKBM8ZbryVk2aESmzfU6b9b_8era_IkvBSELujFZPsyy"
54
55
  };
55
56
  var JETTON_REGISTRY = {
56
57
  // TON Mainnet
@@ -89,6 +90,7 @@ function getDefaultJetton(network) {
89
90
 
90
91
  // src/utils.ts
91
92
  var import_core = require("@ton/core");
93
+ var import_utils = require("@t402/core/utils");
92
94
  function normalizeNetwork(network) {
93
95
  if (network.startsWith("ton:")) {
94
96
  if (!TON_NETWORKS.includes(network)) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/exact/server/index.ts","../../../../src/constants.ts","../../../../src/tokens.ts","../../../../src/utils.ts","../../../../src/exact/server/scheme.ts","../../../../src/exact/server/register.ts"],"sourcesContent":["export { ExactTonScheme } from './scheme.js'\nexport type { ExactTonSchemeConfig } from './scheme.js'\nexport { registerExactTonScheme } from './register.js'\nexport type { TonResourceServerConfig } from './register.js'\n","/**\n * TON Network Constants\n *\n * This module provides constants for TON blockchain integration including:\n * - CAIP-2 network identifiers\n * - RPC endpoints\n * - Jetton transfer operation codes\n * - Default gas amounts\n */\n\n/**\n * CAIP-2 Network Identifiers for TON\n * Using simple identifiers for mainnet/testnet\n */\nexport const TON_MAINNET_CAIP2 = 'ton:mainnet'\nexport const TON_TESTNET_CAIP2 = 'ton:testnet'\n\n/**\n * Supported TON networks\n */\nexport const TON_NETWORKS = [TON_MAINNET_CAIP2, TON_TESTNET_CAIP2] as const\n\nexport type TonNetwork = (typeof TON_NETWORKS)[number]\n\n/**\n * Default RPC endpoints (TonCenter API v2)\n */\nexport const TON_MAINNET_ENDPOINT = 'https://toncenter.com/api/v2/jsonRPC'\nexport const TON_TESTNET_ENDPOINT = 'https://testnet.toncenter.com/api/v2/jsonRPC'\n\n/**\n * TON API v4 endpoints (for @ton/ton TonClient4)\n */\nexport const TON_MAINNET_V4_ENDPOINT = 'https://mainnet-v4.tonhubapi.com'\nexport const TON_TESTNET_V4_ENDPOINT = 'https://testnet-v4.tonhubapi.com'\n\n/**\n * Network endpoint mapping\n */\nexport const NETWORK_ENDPOINTS: Record<string, string> = {\n [TON_MAINNET_CAIP2]: TON_MAINNET_ENDPOINT,\n [TON_TESTNET_CAIP2]: TON_TESTNET_ENDPOINT,\n}\n\nexport const NETWORK_V4_ENDPOINTS: Record<string, string> = {\n [TON_MAINNET_CAIP2]: TON_MAINNET_V4_ENDPOINT,\n [TON_TESTNET_CAIP2]: TON_TESTNET_V4_ENDPOINT,\n}\n\n/**\n * Jetton Transfer Operation Codes (TEP-74)\n * @see https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md\n */\nexport const JETTON_TRANSFER_OP = 0x0f8a7ea5 // transfer\nexport const JETTON_INTERNAL_TRANSFER_OP = 0x178d4519 // internal_transfer\nexport const JETTON_TRANSFER_NOTIFICATION_OP = 0x7362d09c // transfer_notification\nexport const JETTON_BURN_OP = 0x595f07bc // burn\n\n/**\n * Default gas amounts for Jetton transfers\n * TON requires attaching TON for gas to internal messages\n */\nexport const DEFAULT_JETTON_TRANSFER_TON = 100_000_000n // 0.1 TON for gas\nexport const DEFAULT_FORWARD_TON = 1n // Minimal forward amount (notification)\nexport const MIN_JETTON_TRANSFER_TON = 50_000_000n // 0.05 TON minimum\n\n/**\n * Maximum gas amounts to prevent excessive fees\n */\nexport const MAX_JETTON_TRANSFER_TON = 500_000_000n // 0.5 TON maximum\n\n/**\n * Scheme identifier for exact payments\n */\nexport const SCHEME_EXACT = 'exact'\n\n/**\n * Default timeout for payment validity (in seconds)\n */\nexport const DEFAULT_VALIDITY_DURATION = 3600 // 1 hour\n\n/**\n * Address format constants\n */\nexport const TON_ADDRESS_LENGTH = 48 // Friendly format length (base64url)\nexport const TON_RAW_ADDRESS_LENGTH = 66 // Raw format: workchain:hash (0:64hex)\n","/**\n * TON Jetton Token Configuration\n *\n * This module provides comprehensive Jetton token definitions including:\n * - USDT (Tether USD on TON)\n * - Network-specific configurations\n * - Helper functions for token lookups\n */\n\nimport { TON_MAINNET_CAIP2, TON_TESTNET_CAIP2 } from './constants.js'\n\n/**\n * Jetton token configuration\n */\nexport interface JettonConfig {\n /** Jetton master contract address (friendly format) */\n masterAddress: string\n /** Token symbol */\n symbol: string\n /** Token name */\n name: string\n /** Number of decimal places */\n decimals: number\n /** Payment priority (lower = higher priority) */\n priority: number\n}\n\n/**\n * Network token registry mapping network -> symbol -> config\n */\nexport type NetworkJettonRegistry = Record<string, Record<string, JettonConfig>>\n\n/**\n * USDT Jetton Master Contract Addresses by Network\n *\n * USDT on TON follows the TEP-74 Jetton standard.\n * @see https://docs.tether.to/tether-on-ton\n */\nexport const USDT_ADDRESSES: Record<string, string> = {\n // TON Mainnet - Official Tether USDT\n [TON_MAINNET_CAIP2]: 'EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs',\n // TON Testnet - Test USDT (may vary)\n [TON_TESTNET_CAIP2]: 'kQBqSpvo4S87mX9tTc4FX3Sfqf4uSp3Tx-Fz4RBUfTRWBx',\n}\n\n/**\n * Complete Jetton registry with all supported tokens per network\n */\nexport const JETTON_REGISTRY: NetworkJettonRegistry = {\n // TON Mainnet\n [TON_MAINNET_CAIP2]: {\n USDT: {\n masterAddress: USDT_ADDRESSES[TON_MAINNET_CAIP2],\n symbol: 'USDT',\n name: 'Tether USD',\n decimals: 6,\n priority: 1,\n },\n },\n\n // TON Testnet\n [TON_TESTNET_CAIP2]: {\n USDT: {\n masterAddress: USDT_ADDRESSES[TON_TESTNET_CAIP2],\n symbol: 'USDT',\n name: 'Tether USD (Testnet)',\n decimals: 6,\n priority: 1,\n },\n },\n}\n\n/**\n * Get Jetton configuration for a specific token on a network\n *\n * @param network - Network identifier (CAIP-2 format)\n * @param symbol - Token symbol (e.g., \"USDT\")\n * @returns Jetton configuration or undefined\n */\nexport function getJettonConfig(network: string, symbol: string): JettonConfig | undefined {\n return JETTON_REGISTRY[network]?.[symbol.toUpperCase()]\n}\n\n/**\n * Get all Jettons available on a network\n *\n * @param network - Network identifier\n * @returns Array of Jetton configurations sorted by priority\n */\nexport function getNetworkJettons(network: string): JettonConfig[] {\n const jettons = JETTON_REGISTRY[network]\n if (!jettons) return []\n return Object.values(jettons).sort((a, b) => a.priority - b.priority)\n}\n\n/**\n * Get the default/preferred Jetton for a network\n * Prefers USDT based on priority\n *\n * @param network - Network identifier\n * @returns Default Jetton configuration or undefined\n */\nexport function getDefaultJetton(network: string): JettonConfig | undefined {\n const jettons = getNetworkJettons(network)\n return jettons[0] // Already sorted by priority\n}\n\n/**\n * Get Jetton by master contract address on a network\n *\n * @param network - Network identifier\n * @param address - Jetton master contract address\n * @returns Jetton configuration or undefined\n */\nexport function getJettonByAddress(network: string, address: string): JettonConfig | undefined {\n const jettons = JETTON_REGISTRY[network]\n if (!jettons) return undefined\n\n // Normalize address comparison (case-insensitive for base64)\n return Object.values(jettons).find((j) => j.masterAddress.toLowerCase() === address.toLowerCase())\n}\n\n/**\n * Get all networks that support a specific Jetton\n *\n * @param symbol - Token symbol\n * @returns Array of network identifiers\n */\nexport function getNetworksForJetton(symbol: string): string[] {\n const networks: string[] = []\n for (const [network, jettons] of Object.entries(JETTON_REGISTRY)) {\n if (jettons[symbol.toUpperCase()]) {\n networks.push(network)\n }\n }\n return networks\n}\n\n/**\n * Get USDT networks on TON\n *\n * @returns Array of networks supporting USDT\n */\nexport function getUsdtNetworks(): string[] {\n return getNetworksForJetton('USDT')\n}\n\n/**\n * Check if a network is supported\n *\n * @param network - Network identifier to check\n * @returns true if network has configured Jettons\n */\nexport function isNetworkSupported(network: string): boolean {\n return network in JETTON_REGISTRY\n}\n\n/**\n * Get all supported networks\n *\n * @returns Array of all supported network identifiers\n */\nexport function getSupportedNetworks(): string[] {\n return Object.keys(JETTON_REGISTRY)\n}\n","/**\n * TON Utility Functions\n *\n * Helper functions for TON address handling, message building,\n * and network operations.\n */\n\nimport { Address, beginCell, Cell } from '@ton/core'\nimport type { Network } from '@t402/core/types'\nimport {\n TON_MAINNET_CAIP2,\n TON_TESTNET_CAIP2,\n TON_NETWORKS,\n NETWORK_ENDPOINTS,\n JETTON_TRANSFER_OP,\n} from './constants.js'\n\n/**\n * Normalize network identifier to CAIP-2 format\n *\n * @param network - Network identifier (may be legacy format)\n * @returns Normalized CAIP-2 network identifier\n * @throws Error if network is not supported\n */\nexport function normalizeNetwork(network: Network): Network {\n // Already in CAIP-2 format\n if (network.startsWith('ton:')) {\n if (!TON_NETWORKS.includes(network as (typeof TON_NETWORKS)[number])) {\n throw new Error(`Unsupported TON network: ${network}`)\n }\n return network as Network\n }\n\n // Handle legacy format conversions\n const mapping: Record<string, Network> = {\n ton: TON_MAINNET_CAIP2 as Network,\n 'ton-mainnet': TON_MAINNET_CAIP2 as Network,\n mainnet: TON_MAINNET_CAIP2 as Network,\n 'ton-testnet': TON_TESTNET_CAIP2 as Network,\n testnet: TON_TESTNET_CAIP2 as Network,\n }\n\n const caip2 = mapping[network.toLowerCase()]\n if (!caip2) {\n throw new Error(`Unsupported TON network: ${network}`)\n }\n return caip2\n}\n\n/**\n * Get RPC endpoint for a network\n *\n * @param network - Network identifier\n * @returns RPC endpoint URL\n */\nexport function getEndpoint(network: Network): string {\n const caip2 = normalizeNetwork(network)\n const endpoint = NETWORK_ENDPOINTS[caip2]\n if (!endpoint) {\n throw new Error(`No endpoint configured for network: ${network}`)\n }\n return endpoint\n}\n\n/**\n * Check if a network identifier is a supported TON network\n *\n * @param network - Network identifier to check\n * @returns true if supported\n */\nexport function isTonNetwork(network: string): boolean {\n try {\n normalizeNetwork(network as Network)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Validate TON address format\n *\n * @param address - Address to validate\n * @returns true if valid TON address\n */\nexport function validateTonAddress(address: string): boolean {\n try {\n Address.parse(address)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Parse TON address from string\n *\n * @param address - Address string (friendly or raw format)\n * @returns Parsed Address object\n * @throws Error if invalid format\n */\nexport function parseTonAddress(address: string): Address {\n return Address.parse(address)\n}\n\n/**\n * Compare two TON addresses for equality\n * Handles different address formats (friendly, raw, bounceable/non-bounceable)\n *\n * @param addr1 - First address\n * @param addr2 - Second address\n * @returns true if addresses are equal\n */\nexport function addressesEqual(addr1: string, addr2: string): boolean {\n try {\n const a1 = Address.parse(addr1)\n const a2 = Address.parse(addr2)\n return a1.equals(a2)\n } catch {\n return false\n }\n}\n\n/**\n * Format address to friendly format\n *\n * @param address - Address to format\n * @param options - Formatting options\n * @returns Friendly format address string\n */\nexport function formatAddress(\n address: string | Address,\n options?: { bounceable?: boolean; testOnly?: boolean },\n): string {\n const addr = typeof address === 'string' ? Address.parse(address) : address\n return addr.toString({\n bounceable: options?.bounceable ?? true,\n testOnly: options?.testOnly ?? false,\n })\n}\n\n/**\n * Convert decimal amount to smallest units (e.g., nano-Jettons)\n *\n * @param decimalAmount - Amount in decimal format (e.g., \"1.50\")\n * @param decimals - Number of decimal places\n * @returns Amount in smallest units as string\n */\nexport function convertToJettonAmount(decimalAmount: string, decimals: number): string {\n const amount = parseFloat(decimalAmount)\n if (isNaN(amount)) {\n throw new Error(`Invalid amount: ${decimalAmount}`)\n }\n const jettonAmount = Math.floor(amount * Math.pow(10, decimals))\n return jettonAmount.toString()\n}\n\n/**\n * Convert smallest units to decimal amount\n *\n * @param jettonAmount - Amount in smallest units\n * @param decimals - Number of decimal places\n * @returns Amount in decimal format as string\n */\nexport function convertFromJettonAmount(jettonAmount: string | bigint, decimals: number): string {\n const amount = typeof jettonAmount === 'string' ? BigInt(jettonAmount) : jettonAmount\n const divisor = BigInt(Math.pow(10, decimals))\n const wholePart = amount / divisor\n const fractionalPart = amount % divisor\n\n if (fractionalPart === 0n) {\n return wholePart.toString()\n }\n\n const fractionalStr = fractionalPart.toString().padStart(decimals, '0')\n return `${wholePart}.${fractionalStr}`.replace(/\\.?0+$/, '')\n}\n\n/**\n * Generate a unique query ID for Jetton transfer\n * Uses timestamp + random component for uniqueness\n *\n * @returns BigInt query ID\n */\nexport function generateQueryId(): bigint {\n const timestamp = BigInt(Date.now())\n const random = BigInt(Math.floor(Math.random() * 1000000))\n return timestamp * 1000000n + random\n}\n\n/**\n * Build Jetton transfer message body (TEP-74)\n *\n * @param params - Transfer parameters\n * @returns Cell containing the transfer message\n */\nexport function buildJettonTransferBody(params: {\n queryId: bigint\n amount: bigint\n destination: Address\n responseDestination: Address\n forwardAmount?: bigint\n forwardPayload?: Cell\n}): Cell {\n const builder = beginCell()\n .storeUint(JETTON_TRANSFER_OP, 32) // op: transfer\n .storeUint(params.queryId, 64) // query_id\n .storeCoins(params.amount) // amount\n .storeAddress(params.destination) // destination\n .storeAddress(params.responseDestination) // response_destination\n .storeBit(false) // no custom payload\n\n // Forward amount (for notification)\n builder.storeCoins(params.forwardAmount ?? 1n)\n\n // Forward payload (optional)\n if (params.forwardPayload) {\n builder.storeBit(true).storeRef(params.forwardPayload)\n } else {\n builder.storeBit(false)\n }\n\n return builder.endCell()\n}\n\n/**\n * Parse Jetton transfer message from Cell\n *\n * @param body - Cell containing the message\n * @returns Parsed transfer parameters\n * @throws Error if not a valid Jetton transfer message\n */\nexport function parseJettonTransferBody(body: Cell): {\n op: number\n queryId: bigint\n amount: bigint\n destination: Address\n responseDestination: Address\n forwardAmount: bigint\n forwardPayload?: Cell\n} {\n const slice = body.beginParse()\n\n const op = slice.loadUint(32)\n if (op !== JETTON_TRANSFER_OP) {\n throw new Error(`Not a Jetton transfer message. Expected op ${JETTON_TRANSFER_OP}, got ${op}`)\n }\n\n const queryId = slice.loadUintBig(64)\n const amount = slice.loadCoins()\n const destination = slice.loadAddress()\n const responseDestination = slice.loadAddress()\n\n // Skip custom_payload bit\n const hasCustomPayload = slice.loadBit()\n if (hasCustomPayload) {\n slice.loadRef() // Skip custom payload\n }\n\n const forwardAmount = slice.loadCoins()\n\n // Forward payload\n const hasForwardPayload = slice.loadBit()\n const forwardPayload = hasForwardPayload ? slice.loadRef() : undefined\n\n return {\n op,\n queryId,\n amount,\n destination,\n responseDestination,\n forwardAmount,\n forwardPayload,\n }\n}\n\n/**\n * Calculate estimated gas for Jetton transfer\n * Based on typical TON network fees\n *\n * @param params - Optional parameters for estimation\n * @returns Estimated gas in nanoTON\n */\nexport function estimateJettonTransferGas(_params?: { hasForwardPayload?: boolean }): bigint {\n // Base cost for Jetton transfer (typical)\n // Includes: external message, wallet internal message, Jetton wallet message\n return 100_000_000n // 0.1 TON (conservative estimate)\n}\n","/**\n * TON Server Scheme Implementation\n *\n * Handles price parsing and payment requirement enhancement for\n * TON Jetton payments using the exact scheme.\n */\n\nimport type {\n AssetAmount,\n Network,\n PaymentRequirements,\n Price,\n SchemeNetworkServer,\n MoneyParser,\n} from '@t402/core/types'\nimport { SCHEME_EXACT } from '../../constants.js'\nimport { getDefaultJetton, getJettonConfig, JETTON_REGISTRY } from '../../tokens.js'\nimport { normalizeNetwork } from '../../utils.js'\n\n/**\n * Configuration options for ExactTonScheme server\n */\nexport interface ExactTonSchemeConfig {\n /** Preferred Jetton symbol (e.g., \"USDT\"). Defaults to network's highest priority token. */\n preferredJetton?: string\n}\n\n/**\n * TON server implementation for the Exact payment scheme.\n * Handles price parsing and converts user-friendly amounts to Jetton amounts.\n */\nexport class ExactTonScheme implements SchemeNetworkServer {\n readonly scheme = SCHEME_EXACT\n private moneyParsers: MoneyParser[] = []\n private config: ExactTonSchemeConfig\n\n constructor(config: ExactTonSchemeConfig = {}) {\n this.config = config\n }\n\n /**\n * Register a custom money parser in the parser chain.\n * Multiple parsers can be registered - they will be tried in registration order.\n * Each parser receives a decimal amount (e.g., 1.50 for $1.50).\n * If a parser returns null, the next parser in the chain will be tried.\n * The default parser is always the final fallback.\n *\n * @param parser - Custom function to convert amount to AssetAmount (or null to skip)\n * @returns The server instance for chaining\n *\n * @example\n * tonServer.registerMoneyParser(async (amount, network) => {\n * // Use custom Jetton for large amounts\n * if (amount > 1000) {\n * return {\n * amount: (amount * 1e9).toString(),\n * asset: \"EQCustomJettonAddress...\",\n * extra: { tier: \"premium\" }\n * };\n * }\n * return null; // Use next parser\n * });\n */\n registerMoneyParser(parser: MoneyParser): ExactTonScheme {\n this.moneyParsers.push(parser)\n return this\n }\n\n /**\n * Parses a price into an asset amount.\n * If price is already an AssetAmount, returns it directly.\n * If price is Money (string | number), parses to decimal and tries custom parsers.\n * Falls back to default conversion if all custom parsers return null.\n *\n * @param price - The price to parse\n * @param network - The network to use\n * @returns Promise that resolves to the parsed asset amount\n */\n async parsePrice(price: Price, network: Network): Promise<AssetAmount> {\n // Normalize network to CAIP-2 format\n const normalizedNetwork = normalizeNetwork(network)\n\n // If already an AssetAmount, return it directly\n if (typeof price === 'object' && price !== null && 'amount' in price) {\n if (!price.asset) {\n throw new Error(`Asset address must be specified for AssetAmount on network ${network}`)\n }\n return {\n amount: price.amount,\n asset: price.asset,\n extra: price.extra || {},\n }\n }\n\n // Parse Money to decimal number\n const amount = this.parseMoneyToDecimal(price)\n\n // Try each custom money parser in order\n for (const parser of this.moneyParsers) {\n const result = await parser(amount, normalizedNetwork)\n if (result !== null) {\n return result\n }\n }\n\n // All custom parsers returned null, use default conversion\n return this.defaultMoneyConversion(amount, normalizedNetwork)\n }\n\n /**\n * Build payment requirements for this scheme/network combination.\n * Adds TON-specific fields like gas sponsor if provided by facilitator.\n *\n * @param paymentRequirements - Base payment requirements with amount/asset already set\n * @param supportedKind - The supported kind from facilitator's /supported endpoint\n * @param extensionKeys - Extensions supported by the facilitator (unused)\n * @returns Enhanced payment requirements ready to be sent to clients\n */\n async enhancePaymentRequirements(\n paymentRequirements: PaymentRequirements,\n supportedKind: {\n t402Version: number\n scheme: string\n network: Network\n extra?: Record<string, unknown>\n },\n extensionKeys: string[],\n ): Promise<PaymentRequirements> {\n // Mark unused parameters to satisfy linter\n void extensionKeys\n\n // Start with existing extra fields\n const extra = { ...paymentRequirements.extra }\n\n // Add gas sponsor from facilitator if provided\n if (supportedKind.extra?.gasSponsor) {\n extra.gasSponsor = supportedKind.extra.gasSponsor\n }\n\n return {\n ...paymentRequirements,\n extra,\n }\n }\n\n /**\n * Parse Money (string | number) to a decimal number.\n * Handles formats like \"$1.50\", \"1.50\", 1.50, etc.\n *\n * @param money - The money value to parse\n * @returns Decimal number\n */\n private parseMoneyToDecimal(money: string | number): number {\n if (typeof money === 'number') {\n return money\n }\n\n // Remove $ sign and whitespace, then parse\n const cleanMoney = money.replace(/^\\$/, '').trim()\n const amount = parseFloat(cleanMoney)\n\n if (isNaN(amount)) {\n throw new Error(`Invalid money format: ${money}`)\n }\n\n return amount\n }\n\n /**\n * Default money conversion implementation.\n * Converts decimal amount to the preferred Jetton on the specified network.\n *\n * @param amount - The decimal amount (e.g., 1.50)\n * @param network - The network to use\n * @returns The parsed asset amount\n */\n private defaultMoneyConversion(amount: number, network: Network): AssetAmount {\n const jetton = this.getDefaultAsset(network)\n\n // Convert decimal amount to token amount\n const tokenAmount = this.convertToTokenAmount(amount.toString(), jetton.decimals)\n\n return {\n amount: tokenAmount,\n asset: jetton.masterAddress,\n extra: {\n symbol: jetton.symbol,\n name: jetton.name,\n decimals: jetton.decimals,\n },\n }\n }\n\n /**\n * Convert decimal amount to token units (e.g., 0.10 -> 100000 for 6-decimal tokens)\n *\n * @param decimalAmount - The decimal amount to convert\n * @param decimals - Number of decimals for the token\n * @returns The token amount as a string\n */\n private convertToTokenAmount(decimalAmount: string, decimals: number): string {\n const amount = parseFloat(decimalAmount)\n if (isNaN(amount)) {\n throw new Error(`Invalid amount: ${decimalAmount}`)\n }\n // Convert to smallest unit (e.g., for USDT with 6 decimals: 0.10 * 10^6 = 100000)\n const tokenAmount = Math.floor(amount * Math.pow(10, decimals))\n return tokenAmount.toString()\n }\n\n /**\n * Get the default asset info for a network.\n * Priority: configured preferredJetton > USDT > first available\n *\n * @param network - The network to get asset info for\n * @returns The Jetton configuration\n */\n private getDefaultAsset(network: Network): {\n masterAddress: string\n symbol: string\n name: string\n decimals: number\n } {\n // If a preferred Jetton is configured, try to use it\n if (this.config.preferredJetton) {\n const preferred = getJettonConfig(network, this.config.preferredJetton)\n if (preferred) return preferred\n }\n\n // Use the network's default token (sorted by priority)\n const defaultJetton = getDefaultJetton(network)\n if (defaultJetton) return defaultJetton\n\n throw new Error(`No Jettons configured for network ${network}`)\n }\n\n /**\n * Get all supported networks\n */\n static getSupportedNetworks(): string[] {\n return Object.keys(JETTON_REGISTRY)\n }\n\n /**\n * Check if a network is supported\n */\n static isNetworkSupported(network: string): boolean {\n return network in JETTON_REGISTRY\n }\n}\n","import { t402ResourceServer } from '@t402/core/server'\nimport { Network } from '@t402/core/types'\nimport { ExactTonScheme, ExactTonSchemeConfig } from './scheme.js'\n\n/**\n * Configuration options for registering TON schemes to an t402ResourceServer\n */\nexport interface TonResourceServerConfig {\n /**\n * Optional specific networks to register\n * If not provided, registers wildcard support (ton:*)\n */\n networks?: Network[]\n\n /**\n * Optional scheme configuration (preferred Jetton, etc.)\n */\n schemeConfig?: ExactTonSchemeConfig\n}\n\n/**\n * Registers TON exact payment schemes to an t402ResourceServer instance.\n *\n * This function registers:\n * - V2: ton:* wildcard scheme with ExactTonScheme (or specific networks if provided)\n *\n * @param server - The t402ResourceServer instance to register schemes to\n * @param config - Configuration for TON resource server registration\n * @returns The server instance for chaining\n *\n * @example\n * ```typescript\n * import { registerExactTonScheme } from \"@t402/ton/exact/server/register\";\n * import { t402ResourceServer } from \"@t402/core/server\";\n *\n * const server = new t402ResourceServer(facilitatorClient);\n * registerExactTonScheme(server, {});\n *\n * // Or with specific Jetton preference\n * registerExactTonScheme(server, {\n * schemeConfig: { preferredJetton: \"USDT\" }\n * });\n * ```\n */\nexport function registerExactTonScheme(\n server: t402ResourceServer,\n config: TonResourceServerConfig = {},\n): t402ResourceServer {\n const scheme = new ExactTonScheme(config.schemeConfig)\n\n // Register V2 scheme\n if (config.networks && config.networks.length > 0) {\n // Register specific networks\n config.networks.forEach((network) => {\n server.register(network, scheme)\n })\n } else {\n // Register wildcard for all TON networks\n server.register('ton:*', scheme)\n }\n\n return server\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../src/exact/server/index.ts","../../../../src/constants.ts","../../../../src/tokens.ts","../../../../src/utils.ts","../../../../src/exact/server/scheme.ts","../../../../src/exact/server/register.ts"],"sourcesContent":["export { ExactTonScheme } from './scheme.js'\nexport type { ExactTonSchemeConfig } from './scheme.js'\nexport { registerExactTonScheme } from './register.js'\nexport type { TonResourceServerConfig } from './register.js'\n","/**\n * TON Network Constants\n *\n * This module provides constants for TON blockchain integration including:\n * - CAIP-2 network identifiers\n * - RPC endpoints\n * - Jetton transfer operation codes\n * - Default gas amounts\n */\n\n/**\n * CAIP-2 Network Identifiers for TON\n * Using simple identifiers for mainnet/testnet\n */\nexport const TON_MAINNET_CAIP2 = 'ton:mainnet'\nexport const TON_TESTNET_CAIP2 = 'ton:testnet'\n\n/**\n * Supported TON networks\n */\nexport const TON_NETWORKS = [TON_MAINNET_CAIP2, TON_TESTNET_CAIP2] as const\n\nexport type TonNetwork = (typeof TON_NETWORKS)[number]\n\n/**\n * Default RPC endpoints (TonCenter API v2)\n */\nexport const TON_MAINNET_ENDPOINT = 'https://toncenter.com/api/v2/jsonRPC'\nexport const TON_TESTNET_ENDPOINT = 'https://testnet.toncenter.com/api/v2/jsonRPC'\n\n/**\n * TON API v4 endpoints (for @ton/ton TonClient4)\n */\nexport const TON_MAINNET_V4_ENDPOINT = 'https://mainnet-v4.tonhubapi.com'\nexport const TON_TESTNET_V4_ENDPOINT = 'https://testnet-v4.tonhubapi.com'\n\n/**\n * Network endpoint mapping\n */\nexport const NETWORK_ENDPOINTS: Record<string, string> = {\n [TON_MAINNET_CAIP2]: TON_MAINNET_ENDPOINT,\n [TON_TESTNET_CAIP2]: TON_TESTNET_ENDPOINT,\n}\n\nexport const NETWORK_V4_ENDPOINTS: Record<string, string> = {\n [TON_MAINNET_CAIP2]: TON_MAINNET_V4_ENDPOINT,\n [TON_TESTNET_CAIP2]: TON_TESTNET_V4_ENDPOINT,\n}\n\n/**\n * Jetton Transfer Operation Codes (TEP-74)\n * @see https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md\n */\nexport const JETTON_TRANSFER_OP = 0x0f8a7ea5 // transfer\nexport const JETTON_INTERNAL_TRANSFER_OP = 0x178d4519 // internal_transfer\nexport const JETTON_TRANSFER_NOTIFICATION_OP = 0x7362d09c // transfer_notification\nexport const JETTON_BURN_OP = 0x595f07bc // burn\n\n/**\n * Default gas amounts for Jetton transfers\n * TON requires attaching TON for gas to internal messages\n */\nexport const DEFAULT_JETTON_TRANSFER_TON = 100_000_000n // 0.1 TON for gas\nexport const DEFAULT_FORWARD_TON = 1n // Minimal forward amount (notification)\nexport const MIN_JETTON_TRANSFER_TON = 50_000_000n // 0.05 TON minimum\n\n/**\n * Maximum gas amounts to prevent excessive fees\n */\nexport const MAX_JETTON_TRANSFER_TON = 500_000_000n // 0.5 TON maximum\n\n/**\n * Scheme identifier for exact payments\n */\nexport const SCHEME_EXACT = 'exact'\n\n/**\n * Default timeout for payment validity (in seconds)\n */\nexport const DEFAULT_VALIDITY_DURATION = 3600 // 1 hour\n\n/**\n * Address format constants\n */\nexport const TON_ADDRESS_LENGTH = 48 // Friendly format length (base64url)\nexport const TON_RAW_ADDRESS_LENGTH = 66 // Raw format: workchain:hash (0:64hex)\n","/**\n * TON Jetton Token Configuration\n *\n * This module provides comprehensive Jetton token definitions including:\n * - USDT (Tether USD on TON)\n * - Network-specific configurations\n * - Helper functions for token lookups\n */\n\nimport { TON_MAINNET_CAIP2, TON_TESTNET_CAIP2 } from './constants.js'\n\n/**\n * Jetton token configuration\n */\nexport interface JettonConfig {\n /** Jetton master contract address (friendly format) */\n masterAddress: string\n /** Token symbol */\n symbol: string\n /** Token name */\n name: string\n /** Number of decimal places */\n decimals: number\n /** Payment priority (lower = higher priority) */\n priority: number\n}\n\n/**\n * Network token registry mapping network -> symbol -> config\n */\nexport type NetworkJettonRegistry = Record<string, Record<string, JettonConfig>>\n\n/**\n * USDT Jetton Master Contract Addresses by Network\n *\n * USDT on TON follows the TEP-74 Jetton standard.\n * @see https://docs.tether.to/tether-on-ton\n */\nexport const USDT_ADDRESSES: Record<string, string> = {\n // TON Mainnet - Official Tether USDT\n [TON_MAINNET_CAIP2]: 'EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs',\n // TON Testnet - Test USDT from TON documentation\n // @see https://docs.ton.org/v3/guidelines/dapps/cookbook\n [TON_TESTNET_CAIP2]: 'kQD0GKBM8ZbryVk2aESmzfU6b9b_8era_IkvBSELujFZPsyy',\n}\n\n/**\n * Complete Jetton registry with all supported tokens per network\n */\nexport const JETTON_REGISTRY: NetworkJettonRegistry = {\n // TON Mainnet\n [TON_MAINNET_CAIP2]: {\n USDT: {\n masterAddress: USDT_ADDRESSES[TON_MAINNET_CAIP2],\n symbol: 'USDT',\n name: 'Tether USD',\n decimals: 6,\n priority: 1,\n },\n },\n\n // TON Testnet\n [TON_TESTNET_CAIP2]: {\n USDT: {\n masterAddress: USDT_ADDRESSES[TON_TESTNET_CAIP2],\n symbol: 'USDT',\n name: 'Tether USD (Testnet)',\n decimals: 6,\n priority: 1,\n },\n },\n}\n\n/**\n * Get Jetton configuration for a specific token on a network\n *\n * @param network - Network identifier (CAIP-2 format)\n * @param symbol - Token symbol (e.g., \"USDT\")\n * @returns Jetton configuration or undefined\n */\nexport function getJettonConfig(network: string, symbol: string): JettonConfig | undefined {\n return JETTON_REGISTRY[network]?.[symbol.toUpperCase()]\n}\n\n/**\n * Get all Jettons available on a network\n *\n * @param network - Network identifier\n * @returns Array of Jetton configurations sorted by priority\n */\nexport function getNetworkJettons(network: string): JettonConfig[] {\n const jettons = JETTON_REGISTRY[network]\n if (!jettons) return []\n return Object.values(jettons).sort((a, b) => a.priority - b.priority)\n}\n\n/**\n * Get the default/preferred Jetton for a network\n * Prefers USDT based on priority\n *\n * @param network - Network identifier\n * @returns Default Jetton configuration or undefined\n */\nexport function getDefaultJetton(network: string): JettonConfig | undefined {\n const jettons = getNetworkJettons(network)\n return jettons[0] // Already sorted by priority\n}\n\n/**\n * Get Jetton by master contract address on a network\n *\n * @param network - Network identifier\n * @param address - Jetton master contract address\n * @returns Jetton configuration or undefined\n */\nexport function getJettonByAddress(network: string, address: string): JettonConfig | undefined {\n const jettons = JETTON_REGISTRY[network]\n if (!jettons) return undefined\n\n // Normalize address comparison (case-insensitive for base64)\n return Object.values(jettons).find((j) => j.masterAddress.toLowerCase() === address.toLowerCase())\n}\n\n/**\n * Get all networks that support a specific Jetton\n *\n * @param symbol - Token symbol\n * @returns Array of network identifiers\n */\nexport function getNetworksForJetton(symbol: string): string[] {\n const networks: string[] = []\n for (const [network, jettons] of Object.entries(JETTON_REGISTRY)) {\n if (jettons[symbol.toUpperCase()]) {\n networks.push(network)\n }\n }\n return networks\n}\n\n/**\n * Get USDT networks on TON\n *\n * @returns Array of networks supporting USDT\n */\nexport function getUsdtNetworks(): string[] {\n return getNetworksForJetton('USDT')\n}\n\n/**\n * Check if a network is supported\n *\n * @param network - Network identifier to check\n * @returns true if network has configured Jettons\n */\nexport function isNetworkSupported(network: string): boolean {\n return network in JETTON_REGISTRY\n}\n\n/**\n * Get all supported networks\n *\n * @returns Array of all supported network identifiers\n */\nexport function getSupportedNetworks(): string[] {\n return Object.keys(JETTON_REGISTRY)\n}\n","/**\n * TON Utility Functions\n *\n * Helper functions for TON address handling, message building,\n * and network operations.\n */\n\nimport { Address, beginCell, Cell } from '@ton/core'\nimport type { Network } from '@t402/core/types'\nimport { cryptoRandomBigInt } from '@t402/core/utils'\nimport {\n TON_MAINNET_CAIP2,\n TON_TESTNET_CAIP2,\n TON_NETWORKS,\n NETWORK_ENDPOINTS,\n JETTON_TRANSFER_OP,\n} from './constants.js'\n\n/**\n * Normalize network identifier to CAIP-2 format\n *\n * @param network - Network identifier (may be legacy format)\n * @returns Normalized CAIP-2 network identifier\n * @throws Error if network is not supported\n */\nexport function normalizeNetwork(network: Network): Network {\n // Already in CAIP-2 format\n if (network.startsWith('ton:')) {\n if (!TON_NETWORKS.includes(network as (typeof TON_NETWORKS)[number])) {\n throw new Error(`Unsupported TON network: ${network}`)\n }\n return network as Network\n }\n\n // Handle legacy format conversions\n const mapping: Record<string, Network> = {\n ton: TON_MAINNET_CAIP2 as Network,\n 'ton-mainnet': TON_MAINNET_CAIP2 as Network,\n mainnet: TON_MAINNET_CAIP2 as Network,\n 'ton-testnet': TON_TESTNET_CAIP2 as Network,\n testnet: TON_TESTNET_CAIP2 as Network,\n }\n\n const caip2 = mapping[network.toLowerCase()]\n if (!caip2) {\n throw new Error(`Unsupported TON network: ${network}`)\n }\n return caip2\n}\n\n/**\n * Get RPC endpoint for a network\n *\n * @param network - Network identifier\n * @returns RPC endpoint URL\n */\nexport function getEndpoint(network: Network): string {\n const caip2 = normalizeNetwork(network)\n const endpoint = NETWORK_ENDPOINTS[caip2]\n if (!endpoint) {\n throw new Error(`No endpoint configured for network: ${network}`)\n }\n return endpoint\n}\n\n/**\n * Check if a network identifier is a supported TON network\n *\n * @param network - Network identifier to check\n * @returns true if supported\n */\nexport function isTonNetwork(network: string): boolean {\n try {\n normalizeNetwork(network as Network)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Validate TON address format\n *\n * @param address - Address to validate\n * @returns true if valid TON address\n */\nexport function validateTonAddress(address: string): boolean {\n try {\n Address.parse(address)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Parse TON address from string\n *\n * @param address - Address string (friendly or raw format)\n * @returns Parsed Address object\n * @throws Error if invalid format\n */\nexport function parseTonAddress(address: string): Address {\n return Address.parse(address)\n}\n\n/**\n * Compare two TON addresses for equality\n * Handles different address formats (friendly, raw, bounceable/non-bounceable)\n *\n * @param addr1 - First address\n * @param addr2 - Second address\n * @returns true if addresses are equal\n */\nexport function addressesEqual(addr1: string, addr2: string): boolean {\n try {\n const a1 = Address.parse(addr1)\n const a2 = Address.parse(addr2)\n return a1.equals(a2)\n } catch {\n return false\n }\n}\n\n/**\n * Format address to friendly format\n *\n * @param address - Address to format\n * @param options - Formatting options\n * @returns Friendly format address string\n */\nexport function formatAddress(\n address: string | Address,\n options?: { bounceable?: boolean; testOnly?: boolean },\n): string {\n const addr = typeof address === 'string' ? Address.parse(address) : address\n return addr.toString({\n bounceable: options?.bounceable ?? true,\n testOnly: options?.testOnly ?? false,\n })\n}\n\n/**\n * Convert decimal amount to smallest units (e.g., nano-Jettons)\n *\n * @param decimalAmount - Amount in decimal format (e.g., \"1.50\")\n * @param decimals - Number of decimal places\n * @returns Amount in smallest units as string\n */\nexport function convertToJettonAmount(decimalAmount: string, decimals: number): string {\n const amount = parseFloat(decimalAmount)\n if (isNaN(amount)) {\n throw new Error(`Invalid amount: ${decimalAmount}`)\n }\n const jettonAmount = Math.floor(amount * Math.pow(10, decimals))\n return jettonAmount.toString()\n}\n\n/**\n * Convert smallest units to decimal amount\n *\n * @param jettonAmount - Amount in smallest units\n * @param decimals - Number of decimal places\n * @returns Amount in decimal format as string\n */\nexport function convertFromJettonAmount(jettonAmount: string | bigint, decimals: number): string {\n const amount = typeof jettonAmount === 'string' ? BigInt(jettonAmount) : jettonAmount\n const divisor = BigInt(Math.pow(10, decimals))\n const wholePart = amount / divisor\n const fractionalPart = amount % divisor\n\n if (fractionalPart === 0n) {\n return wholePart.toString()\n }\n\n const fractionalStr = fractionalPart.toString().padStart(decimals, '0')\n return `${wholePart}.${fractionalStr}`.replace(/\\.?0+$/, '')\n}\n\n/**\n * Generate a unique query ID for Jetton transfer\n * Uses timestamp + cryptographically secure random component for uniqueness\n *\n * @returns BigInt query ID (64-bit)\n */\nexport function generateQueryId(): bigint {\n const timestamp = BigInt(Date.now())\n // Use 20 bits of cryptographic randomness (0-1048575)\n const random = cryptoRandomBigInt(20)\n return timestamp * 1048576n + random\n}\n\n/**\n * Build Jetton transfer message body (TEP-74)\n *\n * @param params - Transfer parameters\n * @returns Cell containing the transfer message\n */\nexport function buildJettonTransferBody(params: {\n queryId: bigint\n amount: bigint\n destination: Address\n responseDestination: Address\n forwardAmount?: bigint\n forwardPayload?: Cell\n}): Cell {\n const builder = beginCell()\n .storeUint(JETTON_TRANSFER_OP, 32) // op: transfer\n .storeUint(params.queryId, 64) // query_id\n .storeCoins(params.amount) // amount\n .storeAddress(params.destination) // destination\n .storeAddress(params.responseDestination) // response_destination\n .storeBit(false) // no custom payload\n\n // Forward amount (for notification)\n builder.storeCoins(params.forwardAmount ?? 1n)\n\n // Forward payload (optional)\n if (params.forwardPayload) {\n builder.storeBit(true).storeRef(params.forwardPayload)\n } else {\n builder.storeBit(false)\n }\n\n return builder.endCell()\n}\n\n/**\n * Parse Jetton transfer message from Cell\n *\n * @param body - Cell containing the message\n * @returns Parsed transfer parameters\n * @throws Error if not a valid Jetton transfer message\n */\nexport function parseJettonTransferBody(body: Cell): {\n op: number\n queryId: bigint\n amount: bigint\n destination: Address\n responseDestination: Address\n forwardAmount: bigint\n forwardPayload?: Cell\n} {\n const slice = body.beginParse()\n\n const op = slice.loadUint(32)\n if (op !== JETTON_TRANSFER_OP) {\n throw new Error(`Not a Jetton transfer message. Expected op ${JETTON_TRANSFER_OP}, got ${op}`)\n }\n\n const queryId = slice.loadUintBig(64)\n const amount = slice.loadCoins()\n const destination = slice.loadAddress()\n const responseDestination = slice.loadAddress()\n\n // Skip custom_payload bit\n const hasCustomPayload = slice.loadBit()\n if (hasCustomPayload) {\n slice.loadRef() // Skip custom payload\n }\n\n const forwardAmount = slice.loadCoins()\n\n // Forward payload\n const hasForwardPayload = slice.loadBit()\n const forwardPayload = hasForwardPayload ? slice.loadRef() : undefined\n\n return {\n op,\n queryId,\n amount,\n destination,\n responseDestination,\n forwardAmount,\n forwardPayload,\n }\n}\n\n/**\n * Calculate estimated gas for Jetton transfer\n * Based on typical TON network fees\n *\n * @param params - Optional parameters for estimation\n * @returns Estimated gas in nanoTON\n */\nexport function estimateJettonTransferGas(_params?: { hasForwardPayload?: boolean }): bigint {\n // Base cost for Jetton transfer (typical)\n // Includes: external message, wallet internal message, Jetton wallet message\n return 100_000_000n // 0.1 TON (conservative estimate)\n}\n","/**\n * TON Server Scheme Implementation\n *\n * Handles price parsing and payment requirement enhancement for\n * TON Jetton payments using the exact scheme.\n */\n\nimport type {\n AssetAmount,\n Network,\n PaymentRequirements,\n Price,\n SchemeNetworkServer,\n MoneyParser,\n} from '@t402/core/types'\nimport { SCHEME_EXACT } from '../../constants.js'\nimport { getDefaultJetton, getJettonConfig, JETTON_REGISTRY } from '../../tokens.js'\nimport { normalizeNetwork } from '../../utils.js'\n\n/**\n * Configuration options for ExactTonScheme server\n */\nexport interface ExactTonSchemeConfig {\n /** Preferred Jetton symbol (e.g., \"USDT\"). Defaults to network's highest priority token. */\n preferredJetton?: string\n}\n\n/**\n * TON server implementation for the Exact payment scheme.\n * Handles price parsing and converts user-friendly amounts to Jetton amounts.\n */\nexport class ExactTonScheme implements SchemeNetworkServer {\n readonly scheme = SCHEME_EXACT\n private moneyParsers: MoneyParser[] = []\n private config: ExactTonSchemeConfig\n\n constructor(config: ExactTonSchemeConfig = {}) {\n this.config = config\n }\n\n /**\n * Register a custom money parser in the parser chain.\n * Multiple parsers can be registered - they will be tried in registration order.\n * Each parser receives a decimal amount (e.g., 1.50 for $1.50).\n * If a parser returns null, the next parser in the chain will be tried.\n * The default parser is always the final fallback.\n *\n * @param parser - Custom function to convert amount to AssetAmount (or null to skip)\n * @returns The server instance for chaining\n *\n * @example\n * tonServer.registerMoneyParser(async (amount, network) => {\n * // Use custom Jetton for large amounts\n * if (amount > 1000) {\n * return {\n * amount: (amount * 1e9).toString(),\n * asset: \"EQCustomJettonAddress...\",\n * extra: { tier: \"premium\" }\n * };\n * }\n * return null; // Use next parser\n * });\n */\n registerMoneyParser(parser: MoneyParser): ExactTonScheme {\n this.moneyParsers.push(parser)\n return this\n }\n\n /**\n * Parses a price into an asset amount.\n * If price is already an AssetAmount, returns it directly.\n * If price is Money (string | number), parses to decimal and tries custom parsers.\n * Falls back to default conversion if all custom parsers return null.\n *\n * @param price - The price to parse\n * @param network - The network to use\n * @returns Promise that resolves to the parsed asset amount\n */\n async parsePrice(price: Price, network: Network): Promise<AssetAmount> {\n // Normalize network to CAIP-2 format\n const normalizedNetwork = normalizeNetwork(network)\n\n // If already an AssetAmount, return it directly\n if (typeof price === 'object' && price !== null && 'amount' in price) {\n if (!price.asset) {\n throw new Error(`Asset address must be specified for AssetAmount on network ${network}`)\n }\n return {\n amount: price.amount,\n asset: price.asset,\n extra: price.extra || {},\n }\n }\n\n // Parse Money to decimal number\n const amount = this.parseMoneyToDecimal(price)\n\n // Try each custom money parser in order\n for (const parser of this.moneyParsers) {\n const result = await parser(amount, normalizedNetwork)\n if (result !== null) {\n return result\n }\n }\n\n // All custom parsers returned null, use default conversion\n return this.defaultMoneyConversion(amount, normalizedNetwork)\n }\n\n /**\n * Build payment requirements for this scheme/network combination.\n * Adds TON-specific fields like gas sponsor if provided by facilitator.\n *\n * @param paymentRequirements - Base payment requirements with amount/asset already set\n * @param supportedKind - The supported kind from facilitator's /supported endpoint\n * @param extensionKeys - Extensions supported by the facilitator (unused)\n * @returns Enhanced payment requirements ready to be sent to clients\n */\n async enhancePaymentRequirements(\n paymentRequirements: PaymentRequirements,\n supportedKind: {\n t402Version: number\n scheme: string\n network: Network\n extra?: Record<string, unknown>\n },\n extensionKeys: string[],\n ): Promise<PaymentRequirements> {\n // Mark unused parameters to satisfy linter\n void extensionKeys\n\n // Start with existing extra fields\n const extra = { ...paymentRequirements.extra }\n\n // Add gas sponsor from facilitator if provided\n if (supportedKind.extra?.gasSponsor) {\n extra.gasSponsor = supportedKind.extra.gasSponsor\n }\n\n return {\n ...paymentRequirements,\n extra,\n }\n }\n\n /**\n * Parse Money (string | number) to a decimal number.\n * Handles formats like \"$1.50\", \"1.50\", 1.50, etc.\n *\n * @param money - The money value to parse\n * @returns Decimal number\n */\n private parseMoneyToDecimal(money: string | number): number {\n if (typeof money === 'number') {\n return money\n }\n\n // Remove $ sign and whitespace, then parse\n const cleanMoney = money.replace(/^\\$/, '').trim()\n const amount = parseFloat(cleanMoney)\n\n if (isNaN(amount)) {\n throw new Error(`Invalid money format: ${money}`)\n }\n\n return amount\n }\n\n /**\n * Default money conversion implementation.\n * Converts decimal amount to the preferred Jetton on the specified network.\n *\n * @param amount - The decimal amount (e.g., 1.50)\n * @param network - The network to use\n * @returns The parsed asset amount\n */\n private defaultMoneyConversion(amount: number, network: Network): AssetAmount {\n const jetton = this.getDefaultAsset(network)\n\n // Convert decimal amount to token amount\n const tokenAmount = this.convertToTokenAmount(amount.toString(), jetton.decimals)\n\n return {\n amount: tokenAmount,\n asset: jetton.masterAddress,\n extra: {\n symbol: jetton.symbol,\n name: jetton.name,\n decimals: jetton.decimals,\n },\n }\n }\n\n /**\n * Convert decimal amount to token units (e.g., 0.10 -> 100000 for 6-decimal tokens)\n *\n * @param decimalAmount - The decimal amount to convert\n * @param decimals - Number of decimals for the token\n * @returns The token amount as a string\n */\n private convertToTokenAmount(decimalAmount: string, decimals: number): string {\n const amount = parseFloat(decimalAmount)\n if (isNaN(amount)) {\n throw new Error(`Invalid amount: ${decimalAmount}`)\n }\n // Convert to smallest unit (e.g., for USDT with 6 decimals: 0.10 * 10^6 = 100000)\n const tokenAmount = Math.floor(amount * Math.pow(10, decimals))\n return tokenAmount.toString()\n }\n\n /**\n * Get the default asset info for a network.\n * Priority: configured preferredJetton > USDT > first available\n *\n * @param network - The network to get asset info for\n * @returns The Jetton configuration\n */\n private getDefaultAsset(network: Network): {\n masterAddress: string\n symbol: string\n name: string\n decimals: number\n } {\n // If a preferred Jetton is configured, try to use it\n if (this.config.preferredJetton) {\n const preferred = getJettonConfig(network, this.config.preferredJetton)\n if (preferred) return preferred\n }\n\n // Use the network's default token (sorted by priority)\n const defaultJetton = getDefaultJetton(network)\n if (defaultJetton) return defaultJetton\n\n throw new Error(`No Jettons configured for network ${network}`)\n }\n\n /**\n * Get all supported networks\n */\n static getSupportedNetworks(): string[] {\n return Object.keys(JETTON_REGISTRY)\n }\n\n /**\n * Check if a network is supported\n */\n static isNetworkSupported(network: string): boolean {\n return network in JETTON_REGISTRY\n }\n}\n","import { t402ResourceServer } from '@t402/core/server'\nimport { Network } from '@t402/core/types'\nimport { ExactTonScheme, ExactTonSchemeConfig } from './scheme.js'\n\n/**\n * Configuration options for registering TON schemes to an t402ResourceServer\n */\nexport interface TonResourceServerConfig {\n /**\n * Optional specific networks to register\n * If not provided, registers wildcard support (ton:*)\n */\n networks?: Network[]\n\n /**\n * Optional scheme configuration (preferred Jetton, etc.)\n */\n schemeConfig?: ExactTonSchemeConfig\n}\n\n/**\n * Registers TON exact payment schemes to an t402ResourceServer instance.\n *\n * This function registers:\n * - V2: ton:* wildcard scheme with ExactTonScheme (or specific networks if provided)\n *\n * @param server - The t402ResourceServer instance to register schemes to\n * @param config - Configuration for TON resource server registration\n * @returns The server instance for chaining\n *\n * @example\n * ```typescript\n * import { registerExactTonScheme } from \"@t402/ton/exact/server/register\";\n * import { t402ResourceServer } from \"@t402/core/server\";\n *\n * const server = new t402ResourceServer(facilitatorClient);\n * registerExactTonScheme(server, {});\n *\n * // Or with specific Jetton preference\n * registerExactTonScheme(server, {\n * schemeConfig: { preferredJetton: \"USDT\" }\n * });\n * ```\n */\nexport function registerExactTonScheme(\n server: t402ResourceServer,\n config: TonResourceServerConfig = {},\n): t402ResourceServer {\n const scheme = new ExactTonScheme(config.schemeConfig)\n\n // Register V2 scheme\n if (config.networks && config.networks.length > 0) {\n // Register specific networks\n config.networks.forEach((network) => {\n server.register(network, scheme)\n })\n } else {\n // Register wildcard for all TON networks\n server.register('ton:*', scheme)\n }\n\n return server\n}\n"],"mappings":"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","names":[]}