t402 1.6.1__py3-none-any.whl → 1.9.0__py3-none-any.whl
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.
- t402/__init__.py +169 -2
- t402/common.py +37 -8
- t402/encoding.py +298 -1
- t402/facilitator.py +1 -1
- t402/fastapi/__init__.py +79 -0
- t402/fastapi/dependencies.py +398 -0
- t402/fastapi/middleware.py +665 -130
- t402/schemes/__init__.py +164 -0
- t402/schemes/evm/__init__.py +46 -0
- t402/schemes/evm/exact/__init__.py +29 -0
- t402/schemes/evm/exact/client.py +265 -0
- t402/schemes/evm/exact/server.py +181 -0
- t402/schemes/evm/upto/__init__.py +58 -0
- t402/schemes/evm/upto/client.py +240 -0
- t402/schemes/evm/upto/types.py +305 -0
- t402/schemes/interfaces.py +401 -0
- t402/schemes/registry.py +477 -0
- t402/schemes/ton/__init__.py +22 -0
- t402/schemes/ton/exact/__init__.py +27 -0
- t402/schemes/ton/exact/client.py +343 -0
- t402/schemes/ton/exact/server.py +201 -0
- t402/schemes/tron/__init__.py +22 -0
- t402/schemes/tron/exact/__init__.py +27 -0
- t402/schemes/tron/exact/client.py +260 -0
- t402/schemes/tron/exact/server.py +192 -0
- t402/schemes/upto/__init__.py +80 -0
- t402/schemes/upto/types.py +376 -0
- t402/types.py +178 -8
- {t402-1.6.1.dist-info → t402-1.9.0.dist-info}/METADATA +1 -1
- {t402-1.6.1.dist-info → t402-1.9.0.dist-info}/RECORD +32 -11
- {t402-1.6.1.dist-info → t402-1.9.0.dist-info}/WHEEL +0 -0
- {t402-1.6.1.dist-info → t402-1.9.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
"""TON Exact Scheme - Client Implementation.
|
|
2
|
+
|
|
3
|
+
This module provides the client-side implementation of the exact payment scheme
|
|
4
|
+
for TON network using Jetton transfers.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import time
|
|
10
|
+
from typing import Any, Callable, Dict, Optional, Protocol, Union, Awaitable
|
|
11
|
+
|
|
12
|
+
from t402.types import (
|
|
13
|
+
PaymentRequirementsV2,
|
|
14
|
+
T402_VERSION_V1,
|
|
15
|
+
T402_VERSION_V2,
|
|
16
|
+
)
|
|
17
|
+
from t402.ton import (
|
|
18
|
+
TonAuthorization,
|
|
19
|
+
TonPaymentPayload,
|
|
20
|
+
DEFAULT_JETTON_TRANSFER_TON,
|
|
21
|
+
DEFAULT_FORWARD_TON,
|
|
22
|
+
validate_ton_address,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# Constants
|
|
27
|
+
SCHEME_EXACT = "exact"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class SignedMessage(Protocol):
|
|
31
|
+
"""Protocol for a signed TON message."""
|
|
32
|
+
|
|
33
|
+
def to_boc_base64(self) -> str:
|
|
34
|
+
"""Convert message to base64-encoded BOC."""
|
|
35
|
+
...
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class TonSigner(Protocol):
|
|
39
|
+
"""Protocol for TON wallet signing operations.
|
|
40
|
+
|
|
41
|
+
Implementations should provide wallet address, seqno retrieval,
|
|
42
|
+
and message signing capabilities.
|
|
43
|
+
|
|
44
|
+
Example implementation with tonsdk:
|
|
45
|
+
```python
|
|
46
|
+
class MyTonSigner:
|
|
47
|
+
def __init__(self, wallet, client):
|
|
48
|
+
self._wallet = wallet
|
|
49
|
+
self._client = client
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def address(self) -> str:
|
|
53
|
+
return self._wallet.address.to_string(True, True, True)
|
|
54
|
+
|
|
55
|
+
async def get_seqno(self) -> int:
|
|
56
|
+
return await self._client.get_seqno(self._wallet.address)
|
|
57
|
+
|
|
58
|
+
async def sign_message(
|
|
59
|
+
self,
|
|
60
|
+
to: str,
|
|
61
|
+
value: int,
|
|
62
|
+
body: bytes,
|
|
63
|
+
timeout: int,
|
|
64
|
+
) -> SignedMessage:
|
|
65
|
+
# Build and sign the message
|
|
66
|
+
return signed_message
|
|
67
|
+
```
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def address(self) -> str:
|
|
72
|
+
"""Return the wallet address as a friendly string."""
|
|
73
|
+
...
|
|
74
|
+
|
|
75
|
+
async def get_seqno(self) -> int:
|
|
76
|
+
"""Get the current sequence number for replay protection."""
|
|
77
|
+
...
|
|
78
|
+
|
|
79
|
+
async def sign_message(
|
|
80
|
+
self,
|
|
81
|
+
to: str,
|
|
82
|
+
value: int,
|
|
83
|
+
body: bytes,
|
|
84
|
+
timeout: int,
|
|
85
|
+
) -> SignedMessage:
|
|
86
|
+
"""Sign a message to be sent to the TON network.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
to: Destination address
|
|
90
|
+
value: Amount of TON in nanoTON
|
|
91
|
+
body: Message body as bytes (BOC)
|
|
92
|
+
timeout: Message validity timeout in seconds
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
SignedMessage that can be converted to BOC
|
|
96
|
+
"""
|
|
97
|
+
...
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
# Type for Jetton wallet address resolver
|
|
101
|
+
JettonWalletResolver = Callable[[str, str], Awaitable[str]]
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def generate_query_id() -> int:
|
|
105
|
+
"""Generate a unique query ID for Jetton transfers.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
Unique query ID based on timestamp
|
|
109
|
+
"""
|
|
110
|
+
return int(time.time() * 1000000)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class ExactTonClientScheme:
|
|
114
|
+
"""Client scheme for TON exact payments using Jetton transfers.
|
|
115
|
+
|
|
116
|
+
Creates signed BOC messages that can be broadcast by a facilitator
|
|
117
|
+
to complete the payment.
|
|
118
|
+
|
|
119
|
+
Example:
|
|
120
|
+
```python
|
|
121
|
+
scheme = ExactTonClientScheme(
|
|
122
|
+
signer=my_ton_signer,
|
|
123
|
+
get_jetton_wallet_address=my_resolver,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
payload = await scheme.create_payment_payload(
|
|
127
|
+
t402_version=2,
|
|
128
|
+
requirements={
|
|
129
|
+
"scheme": "exact",
|
|
130
|
+
"network": "ton:mainnet",
|
|
131
|
+
"asset": "EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs",
|
|
132
|
+
"amount": "1000000",
|
|
133
|
+
"payTo": "EQ...",
|
|
134
|
+
"maxTimeoutSeconds": 300,
|
|
135
|
+
},
|
|
136
|
+
)
|
|
137
|
+
```
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
scheme = SCHEME_EXACT
|
|
141
|
+
caip_family = "ton:*"
|
|
142
|
+
|
|
143
|
+
def __init__(
|
|
144
|
+
self,
|
|
145
|
+
signer: TonSigner,
|
|
146
|
+
get_jetton_wallet_address: JettonWalletResolver,
|
|
147
|
+
gas_amount: Optional[int] = None,
|
|
148
|
+
forward_amount: Optional[int] = None,
|
|
149
|
+
):
|
|
150
|
+
"""Initialize the TON client scheme.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
signer: TON signer for signing messages
|
|
154
|
+
get_jetton_wallet_address: Function to resolve Jetton wallet address
|
|
155
|
+
gas_amount: Override TON amount for gas (in nanoTON)
|
|
156
|
+
forward_amount: Override forward TON amount (in nanoTON)
|
|
157
|
+
"""
|
|
158
|
+
self._signer = signer
|
|
159
|
+
self._get_jetton_wallet_address = get_jetton_wallet_address
|
|
160
|
+
self._gas_amount = gas_amount or DEFAULT_JETTON_TRANSFER_TON
|
|
161
|
+
self._forward_amount = forward_amount or DEFAULT_FORWARD_TON
|
|
162
|
+
|
|
163
|
+
@property
|
|
164
|
+
def address(self) -> str:
|
|
165
|
+
"""Return the wallet address."""
|
|
166
|
+
return self._signer.address
|
|
167
|
+
|
|
168
|
+
async def create_payment_payload(
|
|
169
|
+
self,
|
|
170
|
+
t402_version: int,
|
|
171
|
+
requirements: Union[PaymentRequirementsV2, Dict[str, Any]],
|
|
172
|
+
) -> Dict[str, Any]:
|
|
173
|
+
"""Create a payment payload for TON Jetton transfer.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
t402_version: Protocol version (1 or 2)
|
|
177
|
+
requirements: Payment requirements
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
Payment payload with signed BOC and authorization metadata
|
|
181
|
+
"""
|
|
182
|
+
# Convert to dict for easier access
|
|
183
|
+
if hasattr(requirements, "model_dump"):
|
|
184
|
+
req = requirements.model_dump(by_alias=True)
|
|
185
|
+
else:
|
|
186
|
+
req = dict(requirements)
|
|
187
|
+
|
|
188
|
+
# Extract fields
|
|
189
|
+
network = req.get("network", "")
|
|
190
|
+
asset = req.get("asset", "")
|
|
191
|
+
amount = req.get("amount", "0")
|
|
192
|
+
pay_to = req.get("payTo", "")
|
|
193
|
+
max_timeout = req.get("maxTimeoutSeconds", 300)
|
|
194
|
+
|
|
195
|
+
# Validate required fields
|
|
196
|
+
if not asset:
|
|
197
|
+
raise ValueError("Asset (Jetton master address) is required")
|
|
198
|
+
if not pay_to:
|
|
199
|
+
raise ValueError("PayTo address is required")
|
|
200
|
+
if not amount:
|
|
201
|
+
raise ValueError("Amount is required")
|
|
202
|
+
if not validate_ton_address(pay_to):
|
|
203
|
+
raise ValueError(f"Invalid payTo address: {pay_to}")
|
|
204
|
+
|
|
205
|
+
# Get sender's Jetton wallet address
|
|
206
|
+
sender_jetton_wallet = await self._get_jetton_wallet_address(
|
|
207
|
+
self._signer.address,
|
|
208
|
+
asset,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
# Get current seqno for replay protection
|
|
212
|
+
seqno = await self._signer.get_seqno()
|
|
213
|
+
|
|
214
|
+
# Calculate validity period
|
|
215
|
+
now = int(time.time())
|
|
216
|
+
valid_until = now + max_timeout
|
|
217
|
+
|
|
218
|
+
# Generate unique query ID
|
|
219
|
+
query_id = generate_query_id()
|
|
220
|
+
|
|
221
|
+
# Parse amount
|
|
222
|
+
jetton_amount = int(amount)
|
|
223
|
+
|
|
224
|
+
# Build Jetton transfer body
|
|
225
|
+
jetton_body = self._build_jetton_transfer_body(
|
|
226
|
+
query_id=query_id,
|
|
227
|
+
amount=jetton_amount,
|
|
228
|
+
destination=pay_to,
|
|
229
|
+
response_destination=self._signer.address,
|
|
230
|
+
forward_amount=self._forward_amount,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
# Sign the message
|
|
234
|
+
signed_message = await self._signer.sign_message(
|
|
235
|
+
to=sender_jetton_wallet,
|
|
236
|
+
value=self._gas_amount,
|
|
237
|
+
body=jetton_body,
|
|
238
|
+
timeout=max_timeout,
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
# Encode to base64
|
|
242
|
+
signed_boc = signed_message.to_boc_base64()
|
|
243
|
+
|
|
244
|
+
# Build authorization metadata
|
|
245
|
+
authorization = TonAuthorization(
|
|
246
|
+
from_=self._signer.address,
|
|
247
|
+
to=pay_to,
|
|
248
|
+
jetton_master=asset,
|
|
249
|
+
jetton_amount=str(jetton_amount),
|
|
250
|
+
ton_amount=str(self._gas_amount),
|
|
251
|
+
valid_until=valid_until,
|
|
252
|
+
seqno=seqno,
|
|
253
|
+
query_id=str(query_id),
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
# Build payload
|
|
257
|
+
payload_data = TonPaymentPayload(
|
|
258
|
+
signed_boc=signed_boc,
|
|
259
|
+
authorization=authorization,
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
if t402_version == T402_VERSION_V1:
|
|
263
|
+
return {
|
|
264
|
+
"t402Version": T402_VERSION_V1,
|
|
265
|
+
"scheme": self.scheme,
|
|
266
|
+
"network": network,
|
|
267
|
+
"payload": payload_data.model_dump(by_alias=True),
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
# V2 format
|
|
271
|
+
return {
|
|
272
|
+
"t402Version": T402_VERSION_V2,
|
|
273
|
+
"payload": payload_data.model_dump(by_alias=True),
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
def _build_jetton_transfer_body(
|
|
277
|
+
self,
|
|
278
|
+
query_id: int,
|
|
279
|
+
amount: int,
|
|
280
|
+
destination: str,
|
|
281
|
+
response_destination: str,
|
|
282
|
+
forward_amount: int,
|
|
283
|
+
) -> bytes:
|
|
284
|
+
"""Build Jetton transfer body as bytes.
|
|
285
|
+
|
|
286
|
+
This builds the TEP-74 Jetton transfer internal message body.
|
|
287
|
+
|
|
288
|
+
Args:
|
|
289
|
+
query_id: Unique query ID
|
|
290
|
+
amount: Jetton amount in smallest units
|
|
291
|
+
destination: Destination address
|
|
292
|
+
response_destination: Address for excess response
|
|
293
|
+
forward_amount: Forward TON amount
|
|
294
|
+
|
|
295
|
+
Returns:
|
|
296
|
+
Serialized message body as bytes
|
|
297
|
+
|
|
298
|
+
Note:
|
|
299
|
+
This returns a minimal placeholder. Real implementations should
|
|
300
|
+
use tonsdk or pytoniq to build proper BOC cells.
|
|
301
|
+
"""
|
|
302
|
+
# Import here to avoid hard dependency
|
|
303
|
+
try:
|
|
304
|
+
from t402.ton import JETTON_TRANSFER_OP
|
|
305
|
+
|
|
306
|
+
# Build cell using available library
|
|
307
|
+
# This is a placeholder - real implementation needs tonsdk/pytoniq
|
|
308
|
+
#
|
|
309
|
+
# The actual cell structure should be:
|
|
310
|
+
# transfer#0f8a7ea5 query_id:uint64 amount:(VarUInteger 16)
|
|
311
|
+
# destination:MsgAddress response_destination:MsgAddress
|
|
312
|
+
# custom_payload:(Maybe ^Cell) forward_ton_amount:(VarUInteger 16)
|
|
313
|
+
# forward_payload:(Either Cell ^Cell) = InternalMsgBody;
|
|
314
|
+
|
|
315
|
+
# For now, return a placeholder that indicates the transfer params
|
|
316
|
+
# Real signers should handle cell building internally
|
|
317
|
+
import json
|
|
318
|
+
|
|
319
|
+
# Encode as JSON for testing/mocking purposes
|
|
320
|
+
# Real implementation would use proper BOC encoding
|
|
321
|
+
transfer_params = {
|
|
322
|
+
"op": JETTON_TRANSFER_OP,
|
|
323
|
+
"query_id": query_id,
|
|
324
|
+
"amount": amount,
|
|
325
|
+
"destination": destination,
|
|
326
|
+
"response_destination": response_destination,
|
|
327
|
+
"forward_amount": forward_amount,
|
|
328
|
+
}
|
|
329
|
+
return json.dumps(transfer_params).encode("utf-8")
|
|
330
|
+
|
|
331
|
+
except ImportError:
|
|
332
|
+
# Fallback if ton module not fully available
|
|
333
|
+
import json
|
|
334
|
+
|
|
335
|
+
transfer_params = {
|
|
336
|
+
"op": 0x0F8A7EA5,
|
|
337
|
+
"query_id": query_id,
|
|
338
|
+
"amount": amount,
|
|
339
|
+
"destination": destination,
|
|
340
|
+
"response_destination": response_destination,
|
|
341
|
+
"forward_amount": forward_amount,
|
|
342
|
+
}
|
|
343
|
+
return json.dumps(transfer_params).encode("utf-8")
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"""TON Exact Scheme - Server Implementation.
|
|
2
|
+
|
|
3
|
+
This module provides the server-side implementation of the exact payment scheme
|
|
4
|
+
for TON network.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from decimal import Decimal
|
|
10
|
+
from typing import Any, Dict, List, Union
|
|
11
|
+
|
|
12
|
+
from t402.types import (
|
|
13
|
+
PaymentRequirementsV2,
|
|
14
|
+
Network,
|
|
15
|
+
)
|
|
16
|
+
from t402.schemes.interfaces import AssetAmount, SupportedKindDict
|
|
17
|
+
from t402.ton import (
|
|
18
|
+
SCHEME_EXACT,
|
|
19
|
+
TON_MAINNET,
|
|
20
|
+
TON_TESTNET,
|
|
21
|
+
DEFAULT_DECIMALS,
|
|
22
|
+
get_network_config,
|
|
23
|
+
get_default_asset,
|
|
24
|
+
get_asset_info,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ExactTonServerScheme:
|
|
29
|
+
"""Server scheme for TON exact payments.
|
|
30
|
+
|
|
31
|
+
Handles parsing user-friendly prices and enhancing payment requirements
|
|
32
|
+
with TON-specific metadata for clients.
|
|
33
|
+
|
|
34
|
+
Example:
|
|
35
|
+
```python
|
|
36
|
+
scheme = ExactTonServerScheme()
|
|
37
|
+
|
|
38
|
+
# Parse price
|
|
39
|
+
asset_amount = await scheme.parse_price("$0.10", "ton:mainnet")
|
|
40
|
+
# Returns: {"amount": "100000", "asset": "EQ...", "extra": {...}}
|
|
41
|
+
|
|
42
|
+
# Enhance requirements
|
|
43
|
+
enhanced = await scheme.enhance_requirements(
|
|
44
|
+
requirements,
|
|
45
|
+
supported_kind,
|
|
46
|
+
facilitator_extensions,
|
|
47
|
+
)
|
|
48
|
+
```
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
scheme = SCHEME_EXACT
|
|
52
|
+
caip_family = "ton:*"
|
|
53
|
+
|
|
54
|
+
async def parse_price(
|
|
55
|
+
self,
|
|
56
|
+
price: Union[str, int, float, Dict[str, Any]],
|
|
57
|
+
network: Network,
|
|
58
|
+
) -> AssetAmount:
|
|
59
|
+
"""Parse a user-friendly price to atomic amount and asset.
|
|
60
|
+
|
|
61
|
+
Supports:
|
|
62
|
+
- String with $ prefix: "$0.10" -> 100000 (6 decimals)
|
|
63
|
+
- String without prefix: "0.10" -> 100000
|
|
64
|
+
- Integer/float: 0.10 -> 100000
|
|
65
|
+
- Dict (TokenAmount): {"amount": "100000", "asset": "EQ..."}
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
price: User-friendly price
|
|
69
|
+
network: Network identifier (CAIP-2 format, e.g., "ton:mainnet")
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
AssetAmount dict with amount, asset, and extra metadata
|
|
73
|
+
"""
|
|
74
|
+
# Validate network
|
|
75
|
+
network_str = self._normalize_network(network)
|
|
76
|
+
|
|
77
|
+
# Handle dict (already in TokenAmount format)
|
|
78
|
+
if isinstance(price, dict):
|
|
79
|
+
return {
|
|
80
|
+
"amount": str(price.get("amount", "0")),
|
|
81
|
+
"asset": price.get("asset", ""),
|
|
82
|
+
"extra": price.get("extra", {}),
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# Get default asset (USDT) for the network
|
|
86
|
+
default_asset = get_default_asset(network_str)
|
|
87
|
+
if not default_asset:
|
|
88
|
+
raise ValueError(f"Unsupported TON network: {network}")
|
|
89
|
+
|
|
90
|
+
asset_address = default_asset["master_address"]
|
|
91
|
+
decimals = default_asset.get("decimals", DEFAULT_DECIMALS)
|
|
92
|
+
|
|
93
|
+
# Parse price string/number
|
|
94
|
+
if isinstance(price, str):
|
|
95
|
+
if price.startswith("$"):
|
|
96
|
+
price = price[1:]
|
|
97
|
+
amount_decimal = Decimal(price)
|
|
98
|
+
else:
|
|
99
|
+
amount_decimal = Decimal(str(price))
|
|
100
|
+
|
|
101
|
+
# Convert to atomic units
|
|
102
|
+
atomic_amount = int(amount_decimal * Decimal(10 ** decimals))
|
|
103
|
+
|
|
104
|
+
# Build extra metadata
|
|
105
|
+
extra = {
|
|
106
|
+
"symbol": default_asset.get("symbol", "USDT"),
|
|
107
|
+
"name": default_asset.get("name", "Tether USD"),
|
|
108
|
+
"decimals": decimals,
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
"amount": str(atomic_amount),
|
|
113
|
+
"asset": asset_address,
|
|
114
|
+
"extra": extra,
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async def enhance_requirements(
|
|
118
|
+
self,
|
|
119
|
+
requirements: Union[PaymentRequirementsV2, Dict[str, Any]],
|
|
120
|
+
supported_kind: SupportedKindDict,
|
|
121
|
+
facilitator_extensions: List[str],
|
|
122
|
+
) -> Union[PaymentRequirementsV2, Dict[str, Any]]:
|
|
123
|
+
"""Enhance payment requirements with TON-specific metadata.
|
|
124
|
+
|
|
125
|
+
Adds Jetton metadata to the extra field so clients can
|
|
126
|
+
properly build the transfer message.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
requirements: Base payment requirements
|
|
130
|
+
supported_kind: Matched SupportedKind from facilitator
|
|
131
|
+
facilitator_extensions: Extensions supported by facilitator
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
Enhanced requirements with TON metadata in extra
|
|
135
|
+
"""
|
|
136
|
+
# Convert to dict for modification
|
|
137
|
+
if hasattr(requirements, "model_dump"):
|
|
138
|
+
req = requirements.model_dump(by_alias=True)
|
|
139
|
+
else:
|
|
140
|
+
req = dict(requirements)
|
|
141
|
+
|
|
142
|
+
network = req.get("network", "")
|
|
143
|
+
asset = req.get("asset", "")
|
|
144
|
+
|
|
145
|
+
# Normalize network
|
|
146
|
+
network_str = self._normalize_network(network)
|
|
147
|
+
|
|
148
|
+
# Ensure extra exists
|
|
149
|
+
if "extra" not in req or req["extra"] is None:
|
|
150
|
+
req["extra"] = {}
|
|
151
|
+
|
|
152
|
+
# Add Jetton metadata if not present
|
|
153
|
+
asset_info = get_asset_info(network_str, asset)
|
|
154
|
+
if asset_info:
|
|
155
|
+
if "symbol" not in req["extra"]:
|
|
156
|
+
req["extra"]["symbol"] = asset_info.get("symbol", "UNKNOWN")
|
|
157
|
+
if "name" not in req["extra"]:
|
|
158
|
+
req["extra"]["name"] = asset_info.get("name", "Unknown Jetton")
|
|
159
|
+
if "decimals" not in req["extra"]:
|
|
160
|
+
req["extra"]["decimals"] = asset_info.get("decimals", DEFAULT_DECIMALS)
|
|
161
|
+
|
|
162
|
+
# Add network config info
|
|
163
|
+
network_config = get_network_config(network_str)
|
|
164
|
+
if network_config:
|
|
165
|
+
if "endpoint" not in req["extra"]:
|
|
166
|
+
req["extra"]["endpoint"] = network_config.get("endpoint", "")
|
|
167
|
+
|
|
168
|
+
# Add facilitator extra data if available
|
|
169
|
+
if supported_kind.get("extra"):
|
|
170
|
+
for key, value in supported_kind["extra"].items():
|
|
171
|
+
if key not in req["extra"]:
|
|
172
|
+
req["extra"][key] = value
|
|
173
|
+
|
|
174
|
+
return req
|
|
175
|
+
|
|
176
|
+
def _normalize_network(self, network: str) -> str:
|
|
177
|
+
"""Normalize network identifier to CAIP-2 format.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
network: Network identifier
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
Normalized CAIP-2 network string
|
|
184
|
+
|
|
185
|
+
Raises:
|
|
186
|
+
ValueError: If network is not supported
|
|
187
|
+
"""
|
|
188
|
+
# Already in CAIP-2 format
|
|
189
|
+
if network.startswith("ton:"):
|
|
190
|
+
if network in (TON_MAINNET, TON_TESTNET):
|
|
191
|
+
return network
|
|
192
|
+
raise ValueError(f"Unknown TON network: {network}")
|
|
193
|
+
|
|
194
|
+
# Handle legacy format
|
|
195
|
+
lower = network.lower()
|
|
196
|
+
if lower in ("mainnet", "ton-mainnet"):
|
|
197
|
+
return TON_MAINNET
|
|
198
|
+
elif lower in ("testnet", "ton-testnet"):
|
|
199
|
+
return TON_TESTNET
|
|
200
|
+
|
|
201
|
+
raise ValueError(f"Unknown network: {network}")
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""TRON Blockchain Payment Schemes.
|
|
2
|
+
|
|
3
|
+
This package provides payment scheme implementations for TRON blockchain.
|
|
4
|
+
|
|
5
|
+
Supported schemes:
|
|
6
|
+
- exact: TRC-20 token transfers with signed transactions
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from t402.schemes.tron.exact import (
|
|
10
|
+
ExactTronClientScheme,
|
|
11
|
+
ExactTronServerScheme,
|
|
12
|
+
TronSigner,
|
|
13
|
+
SCHEME_EXACT,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
# Exact scheme
|
|
18
|
+
"ExactTronClientScheme",
|
|
19
|
+
"ExactTronServerScheme",
|
|
20
|
+
"TronSigner",
|
|
21
|
+
"SCHEME_EXACT",
|
|
22
|
+
]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""TRON Exact Payment Scheme.
|
|
2
|
+
|
|
3
|
+
This package provides the exact payment scheme implementation for TRON
|
|
4
|
+
using TRC-20 token transfers.
|
|
5
|
+
|
|
6
|
+
The exact scheme allows users to sign TRC-20 transfer transactions that
|
|
7
|
+
can be verified and broadcast by a facilitator, enabling gasless payments.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from t402.schemes.tron.exact.client import (
|
|
11
|
+
ExactTronClientScheme,
|
|
12
|
+
TronSigner,
|
|
13
|
+
SCHEME_EXACT,
|
|
14
|
+
)
|
|
15
|
+
from t402.schemes.tron.exact.server import (
|
|
16
|
+
ExactTronServerScheme,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
# Client
|
|
21
|
+
"ExactTronClientScheme",
|
|
22
|
+
"TronSigner",
|
|
23
|
+
# Server
|
|
24
|
+
"ExactTronServerScheme",
|
|
25
|
+
# Constants
|
|
26
|
+
"SCHEME_EXACT",
|
|
27
|
+
]
|