t402 1.9.0__py3-none-any.whl → 1.9.1__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.
Files changed (100) hide show
  1. t402/__init__.py +2 -1
  2. t402/bridge/client.py +13 -5
  3. t402/bridge/constants.py +3 -1
  4. t402/bridge/router.py +1 -1
  5. t402/bridge/scan.py +3 -1
  6. t402/chains.py +268 -1
  7. t402/cli.py +31 -9
  8. t402/common.py +2 -0
  9. t402/cosmos_paywall_template.py +2 -0
  10. t402/encoding.py +9 -3
  11. t402/erc4337/accounts.py +56 -51
  12. t402/erc4337/bundlers.py +105 -99
  13. t402/erc4337/paymasters.py +100 -109
  14. t402/erc4337/types.py +39 -26
  15. t402/evm_paywall_template.py +1 -1
  16. t402/fastapi/middleware.py +1 -3
  17. t402/mcp/server.py +79 -46
  18. t402/near_paywall_template.py +2 -0
  19. t402/networks.py +34 -1
  20. t402/paywall.py +1 -3
  21. t402/schemes/__init__.py +124 -0
  22. t402/schemes/aptos/__init__.py +70 -0
  23. t402/schemes/aptos/constants.py +349 -0
  24. t402/schemes/aptos/exact_direct/__init__.py +44 -0
  25. t402/schemes/aptos/exact_direct/client.py +202 -0
  26. t402/schemes/aptos/exact_direct/facilitator.py +426 -0
  27. t402/schemes/aptos/exact_direct/server.py +272 -0
  28. t402/schemes/aptos/types.py +237 -0
  29. t402/schemes/evm/__init__.py +46 -1
  30. t402/schemes/evm/exact/__init__.py +11 -0
  31. t402/schemes/evm/exact/client.py +3 -1
  32. t402/schemes/evm/exact/facilitator.py +894 -0
  33. t402/schemes/evm/exact/server.py +1 -1
  34. t402/schemes/evm/exact_legacy/__init__.py +38 -0
  35. t402/schemes/evm/exact_legacy/client.py +291 -0
  36. t402/schemes/evm/exact_legacy/facilitator.py +777 -0
  37. t402/schemes/evm/exact_legacy/server.py +231 -0
  38. t402/schemes/evm/upto/__init__.py +12 -0
  39. t402/schemes/evm/upto/client.py +6 -2
  40. t402/schemes/evm/upto/facilitator.py +625 -0
  41. t402/schemes/evm/upto/server.py +243 -0
  42. t402/schemes/evm/upto/types.py +3 -1
  43. t402/schemes/interfaces.py +6 -2
  44. t402/schemes/near/__init__.py +112 -0
  45. t402/schemes/near/constants.py +189 -0
  46. t402/schemes/near/exact_direct/__init__.py +21 -0
  47. t402/schemes/near/exact_direct/client.py +204 -0
  48. t402/schemes/near/exact_direct/facilitator.py +455 -0
  49. t402/schemes/near/exact_direct/server.py +303 -0
  50. t402/schemes/near/types.py +419 -0
  51. t402/schemes/polkadot/__init__.py +72 -0
  52. t402/schemes/polkadot/constants.py +155 -0
  53. t402/schemes/polkadot/exact_direct/__init__.py +43 -0
  54. t402/schemes/polkadot/exact_direct/client.py +235 -0
  55. t402/schemes/polkadot/exact_direct/facilitator.py +428 -0
  56. t402/schemes/polkadot/exact_direct/server.py +292 -0
  57. t402/schemes/polkadot/types.py +385 -0
  58. t402/schemes/registry.py +6 -2
  59. t402/schemes/stacks/__init__.py +68 -0
  60. t402/schemes/stacks/constants.py +122 -0
  61. t402/schemes/stacks/exact_direct/__init__.py +43 -0
  62. t402/schemes/stacks/exact_direct/client.py +222 -0
  63. t402/schemes/stacks/exact_direct/facilitator.py +424 -0
  64. t402/schemes/stacks/exact_direct/server.py +292 -0
  65. t402/schemes/stacks/types.py +380 -0
  66. t402/schemes/svm/__init__.py +29 -0
  67. t402/schemes/svm/exact/__init__.py +35 -0
  68. t402/schemes/svm/exact/client.py +23 -0
  69. t402/schemes/svm/exact/facilitator.py +24 -0
  70. t402/schemes/svm/exact/server.py +20 -0
  71. t402/schemes/tezos/__init__.py +84 -0
  72. t402/schemes/tezos/constants.py +372 -0
  73. t402/schemes/tezos/exact_direct/__init__.py +22 -0
  74. t402/schemes/tezos/exact_direct/client.py +226 -0
  75. t402/schemes/tezos/exact_direct/facilitator.py +491 -0
  76. t402/schemes/tezos/exact_direct/server.py +277 -0
  77. t402/schemes/tezos/types.py +220 -0
  78. t402/schemes/ton/__init__.py +9 -2
  79. t402/schemes/ton/exact/__init__.py +7 -0
  80. t402/schemes/ton/exact/facilitator.py +730 -0
  81. t402/schemes/ton/exact/server.py +1 -1
  82. t402/schemes/tron/__init__.py +11 -2
  83. t402/schemes/tron/exact/__init__.py +9 -0
  84. t402/schemes/tron/exact/facilitator.py +673 -0
  85. t402/schemes/tron/exact/server.py +1 -1
  86. t402/stacks_paywall_template.py +2 -0
  87. t402/svm.py +45 -11
  88. t402/svm_paywall_template.py +1 -1
  89. t402/ton.py +5 -1
  90. t402/ton_paywall_template.py +1 -192
  91. t402/tron.py +2 -0
  92. t402/tron_paywall_template.py +2 -0
  93. t402/types.py +3 -1
  94. t402/wdk/errors.py +15 -5
  95. t402/wdk/signer.py +11 -2
  96. {t402-1.9.0.dist-info → t402-1.9.1.dist-info}/METADATA +42 -1
  97. t402-1.9.1.dist-info/RECORD +125 -0
  98. t402-1.9.0.dist-info/RECORD +0 -72
  99. {t402-1.9.0.dist-info → t402-1.9.1.dist-info}/WHEEL +0 -0
  100. {t402-1.9.0.dist-info → t402-1.9.1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,35 @@
1
+ """Solana SVM Exact Payment Scheme.
2
+
3
+ This package provides the exact payment scheme implementation for Solana
4
+ using SPL Token TransferChecked instructions with a facilitator fee payer.
5
+
6
+ The exact scheme allows users to sign transactions that transfer SPL tokens
7
+ to a recipient. The facilitator pays for gas (transaction fees) and broadcasts
8
+ the transaction.
9
+ """
10
+
11
+ from t402.schemes.svm.exact.client import (
12
+ ExactSvmClientScheme,
13
+ ClientSvmSigner,
14
+ SCHEME_EXACT,
15
+ )
16
+ from t402.schemes.svm.exact.server import (
17
+ ExactSvmServerScheme,
18
+ )
19
+ from t402.schemes.svm.exact.facilitator import (
20
+ ExactSvmFacilitatorScheme,
21
+ FacilitatorSvmSigner,
22
+ )
23
+
24
+ __all__ = [
25
+ # Client
26
+ "ExactSvmClientScheme",
27
+ "ClientSvmSigner",
28
+ # Server
29
+ "ExactSvmServerScheme",
30
+ # Facilitator
31
+ "ExactSvmFacilitatorScheme",
32
+ "FacilitatorSvmSigner",
33
+ # Constants
34
+ "SCHEME_EXACT",
35
+ ]
@@ -0,0 +1,23 @@
1
+ """Solana SVM Exact Scheme - Client Implementation.
2
+
3
+ This module provides the client-side implementation of the exact payment scheme
4
+ for Solana network using SPL Token TransferChecked instructions.
5
+
6
+ The client creates and signs a Solana transaction containing a TransferChecked
7
+ instruction, which the facilitator then co-signs (as fee payer) and broadcasts.
8
+
9
+ This module re-exports the client implementation from the monolithic svm module,
10
+ providing the standard scheme package structure.
11
+ """
12
+
13
+ from t402.svm import (
14
+ SCHEME_EXACT,
15
+ ExactSvmClientScheme,
16
+ ClientSvmSigner,
17
+ )
18
+
19
+ __all__ = [
20
+ "SCHEME_EXACT",
21
+ "ExactSvmClientScheme",
22
+ "ClientSvmSigner",
23
+ ]
@@ -0,0 +1,24 @@
1
+ """Solana SVM Exact Scheme - Facilitator Implementation.
2
+
3
+ This module provides the facilitator-side implementation of the exact payment
4
+ scheme for Solana network using SPL Token TransferChecked instructions.
5
+
6
+ The facilitator:
7
+ 1. Verifies signed transactions by checking transfer instruction parameters,
8
+ ensuring the facilitator's funds are not being stolen, and simulating
9
+ 2. Settles payments by co-signing (as fee payer) and broadcasting the transaction
10
+ 3. Waits for transaction confirmation
11
+
12
+ This module re-exports the facilitator implementation from the monolithic svm module,
13
+ providing the standard scheme package structure.
14
+ """
15
+
16
+ from t402.svm import (
17
+ ExactSvmFacilitatorScheme,
18
+ FacilitatorSvmSigner,
19
+ )
20
+
21
+ __all__ = [
22
+ "ExactSvmFacilitatorScheme",
23
+ "FacilitatorSvmSigner",
24
+ ]
@@ -0,0 +1,20 @@
1
+ """Solana SVM Exact Scheme - Server Implementation.
2
+
3
+ This module provides the server-side implementation of the exact payment scheme
4
+ for Solana network.
5
+
6
+ The server parses user-friendly prices into atomic token amounts and enhances
7
+ payment requirements with the facilitator's fee payer address so clients can
8
+ build transactions correctly.
9
+
10
+ This module re-exports the server implementation from the monolithic svm module,
11
+ providing the standard scheme package structure.
12
+ """
13
+
14
+ from t402.svm import (
15
+ ExactSvmServerScheme,
16
+ )
17
+
18
+ __all__ = [
19
+ "ExactSvmServerScheme",
20
+ ]
@@ -0,0 +1,84 @@
1
+ """Tezos Blockchain Payment Schemes.
2
+
3
+ This package provides payment scheme implementations for the Tezos blockchain.
4
+
5
+ Supported schemes:
6
+ - exact-direct: Client executes FA2 transfer directly, provides opHash as proof
7
+
8
+ Usage:
9
+ ```python
10
+ from t402.schemes.tezos import (
11
+ ExactDirectTezosClient,
12
+ ExactDirectTezosServer,
13
+ ExactDirectTezosFacilitator,
14
+ ClientTezosSigner,
15
+ FacilitatorTezosSigner,
16
+ SCHEME_EXACT_DIRECT,
17
+ )
18
+ ```
19
+ """
20
+
21
+ from t402.schemes.tezos.constants import (
22
+ SCHEME_EXACT_DIRECT,
23
+ TEZOS_MAINNET,
24
+ TEZOS_GHOSTNET,
25
+ USDT_MAINNET_CONTRACT,
26
+ USDT_MAINNET_TOKEN_ID,
27
+ USDT_DECIMALS,
28
+ USDT_MAINNET,
29
+ NETWORK_CONFIGS,
30
+ TOKEN_REGISTRY,
31
+ is_tezos_network,
32
+ is_valid_address,
33
+ is_valid_operation_hash,
34
+ create_asset_identifier,
35
+ parse_asset_identifier,
36
+ get_network_config,
37
+ get_token_info,
38
+ get_token_by_contract,
39
+ decimal_to_atomic,
40
+ parse_decimal_to_atomic,
41
+ )
42
+ from t402.schemes.tezos.types import (
43
+ ClientTezosSigner,
44
+ FacilitatorTezosSigner,
45
+ ExactDirectPayload,
46
+ )
47
+ from t402.schemes.tezos.exact_direct import (
48
+ ExactDirectTezosClient,
49
+ ExactDirectTezosServer,
50
+ ExactDirectTezosFacilitator,
51
+ )
52
+
53
+ __all__ = [
54
+ # Scheme classes
55
+ "ExactDirectTezosClient",
56
+ "ExactDirectTezosServer",
57
+ "ExactDirectTezosFacilitator",
58
+ # Signer protocols
59
+ "ClientTezosSigner",
60
+ "FacilitatorTezosSigner",
61
+ # Payload type
62
+ "ExactDirectPayload",
63
+ # Constants
64
+ "SCHEME_EXACT_DIRECT",
65
+ "TEZOS_MAINNET",
66
+ "TEZOS_GHOSTNET",
67
+ "USDT_MAINNET_CONTRACT",
68
+ "USDT_MAINNET_TOKEN_ID",
69
+ "USDT_DECIMALS",
70
+ "USDT_MAINNET",
71
+ "NETWORK_CONFIGS",
72
+ "TOKEN_REGISTRY",
73
+ # Utility functions
74
+ "is_tezos_network",
75
+ "is_valid_address",
76
+ "is_valid_operation_hash",
77
+ "create_asset_identifier",
78
+ "parse_asset_identifier",
79
+ "get_network_config",
80
+ "get_token_info",
81
+ "get_token_by_contract",
82
+ "decimal_to_atomic",
83
+ "parse_decimal_to_atomic",
84
+ ]
@@ -0,0 +1,372 @@
1
+ """Tezos Blockchain Constants for T402 Protocol.
2
+
3
+ This module defines constants, token information, and network configurations
4
+ for the Tezos blockchain mechanism in the T402 protocol.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import Any, Dict, Optional
10
+
11
+
12
+ # Scheme identifier
13
+ SCHEME_EXACT_DIRECT = "exact-direct"
14
+
15
+ # CAIP-2 network identifiers (derived from genesis block hash prefix)
16
+ TEZOS_MAINNET = "tezos:NetXdQprcVkpaWU"
17
+ TEZOS_GHOSTNET = "tezos:NetXnHfVqm9iesp"
18
+
19
+ # RPC endpoints
20
+ TEZOS_MAINNET_RPC = "https://mainnet.api.tez.ie"
21
+ TEZOS_GHOSTNET_RPC = "https://ghostnet.tezos.marigold.dev"
22
+
23
+ # Indexer API endpoints (TzKT)
24
+ TEZOS_MAINNET_INDEXER = "https://api.tzkt.io"
25
+ TEZOS_GHOSTNET_INDEXER = "https://api.ghostnet.tzkt.io"
26
+
27
+ # FA2 token standard (TZIP-12)
28
+ FA2_TRANSFER_ENTRYPOINT = "transfer"
29
+
30
+ # USDt on Tezos Mainnet
31
+ USDT_MAINNET_CONTRACT = "KT1XnTn74bUtxHfDtBmm2bGZAQfhPbvKWR8o"
32
+ USDT_MAINNET_TOKEN_ID = 0
33
+ USDT_DECIMALS = 6
34
+
35
+ # Address length for Tezos addresses (tz1/tz2/tz3/KT1)
36
+ TEZOS_ADDRESS_LENGTH = 36
37
+
38
+ # Operation hash length (starts with 'o')
39
+ TEZOS_OP_HASH_LENGTH = 51
40
+
41
+ # Valid address prefixes
42
+ VALID_ADDRESS_PREFIXES = ("tz1", "tz2", "tz3", "KT1")
43
+
44
+ # Base58 character set (no 0, O, I, l)
45
+ BASE58_CHARS = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
46
+
47
+
48
+ class TokenInfo:
49
+ """Information about a Tezos FA2 token.
50
+
51
+ Attributes:
52
+ contract_address: The FA2 contract address (KT1...)
53
+ token_id: The token ID within the FA2 contract
54
+ symbol: Token symbol (e.g., "USDt")
55
+ name: Full token name (e.g., "Tether USD")
56
+ decimals: Number of decimal places
57
+ """
58
+
59
+ def __init__(
60
+ self,
61
+ contract_address: str,
62
+ token_id: int,
63
+ symbol: str,
64
+ name: str,
65
+ decimals: int,
66
+ ):
67
+ self.contract_address = contract_address
68
+ self.token_id = token_id
69
+ self.symbol = symbol
70
+ self.name = name
71
+ self.decimals = decimals
72
+
73
+
74
+ class NetworkConfig:
75
+ """Configuration for a Tezos network.
76
+
77
+ Attributes:
78
+ name: Human-readable network name
79
+ rpc_url: Tezos RPC endpoint URL
80
+ indexer_url: TzKT indexer API URL
81
+ default_token: Default token for price conversion (may be None)
82
+ is_testnet: Whether this is a testnet
83
+ """
84
+
85
+ def __init__(
86
+ self,
87
+ name: str,
88
+ rpc_url: str,
89
+ indexer_url: str,
90
+ default_token: Optional[TokenInfo] = None,
91
+ is_testnet: bool = False,
92
+ ):
93
+ self.name = name
94
+ self.rpc_url = rpc_url
95
+ self.indexer_url = indexer_url
96
+ self.default_token = default_token
97
+ self.is_testnet = is_testnet
98
+
99
+
100
+ # Token definitions
101
+ USDT_MAINNET = TokenInfo(
102
+ contract_address=USDT_MAINNET_CONTRACT,
103
+ token_id=USDT_MAINNET_TOKEN_ID,
104
+ symbol="USDt",
105
+ name="Tether USD",
106
+ decimals=USDT_DECIMALS,
107
+ )
108
+
109
+ # Network configurations
110
+ NETWORK_CONFIGS: Dict[str, NetworkConfig] = {
111
+ TEZOS_MAINNET: NetworkConfig(
112
+ name="Tezos Mainnet",
113
+ rpc_url=TEZOS_MAINNET_RPC,
114
+ indexer_url=TEZOS_MAINNET_INDEXER,
115
+ default_token=USDT_MAINNET,
116
+ is_testnet=False,
117
+ ),
118
+ TEZOS_GHOSTNET: NetworkConfig(
119
+ name="Tezos Ghostnet",
120
+ rpc_url=TEZOS_GHOSTNET_RPC,
121
+ indexer_url=TEZOS_GHOSTNET_INDEXER,
122
+ default_token=None, # No USDT on testnet
123
+ is_testnet=True,
124
+ ),
125
+ }
126
+
127
+ # Token registry indexed by network and symbol
128
+ TOKEN_REGISTRY: Dict[str, Dict[str, TokenInfo]] = {
129
+ TEZOS_MAINNET: {
130
+ "USDt": USDT_MAINNET,
131
+ },
132
+ TEZOS_GHOSTNET: {},
133
+ }
134
+
135
+
136
+ def get_network_config(network: str) -> Optional[NetworkConfig]:
137
+ """Get the configuration for a Tezos network.
138
+
139
+ Args:
140
+ network: CAIP-2 network identifier (e.g., "tezos:NetXdQprcVkpaWU")
141
+
142
+ Returns:
143
+ NetworkConfig if found, None otherwise
144
+ """
145
+ return NETWORK_CONFIGS.get(network)
146
+
147
+
148
+ def get_token_info(network: str, symbol: str) -> Optional[TokenInfo]:
149
+ """Get token information by network and symbol.
150
+
151
+ Args:
152
+ network: CAIP-2 network identifier
153
+ symbol: Token symbol (e.g., "USDt")
154
+
155
+ Returns:
156
+ TokenInfo if found, None otherwise
157
+ """
158
+ tokens = TOKEN_REGISTRY.get(network, {})
159
+ return tokens.get(symbol)
160
+
161
+
162
+ def get_token_by_contract(
163
+ network: str, contract_address: str, token_id: int
164
+ ) -> Optional[TokenInfo]:
165
+ """Get token information by contract address and token ID.
166
+
167
+ Args:
168
+ network: CAIP-2 network identifier
169
+ contract_address: FA2 contract address
170
+ token_id: Token ID within the contract
171
+
172
+ Returns:
173
+ TokenInfo if found, None otherwise
174
+ """
175
+ tokens = TOKEN_REGISTRY.get(network, {})
176
+ for token in tokens.values():
177
+ if token.contract_address == contract_address and token.token_id == token_id:
178
+ return token
179
+ return None
180
+
181
+
182
+ def is_tezos_network(network: str) -> bool:
183
+ """Check if a network identifier belongs to the Tezos namespace.
184
+
185
+ Args:
186
+ network: Network identifier string
187
+
188
+ Returns:
189
+ True if the network starts with "tezos:"
190
+ """
191
+ return network.startswith("tezos:")
192
+
193
+
194
+ def is_valid_address(address: str) -> bool:
195
+ """Validate a Tezos address format.
196
+
197
+ Valid addresses:
198
+ - Implicit accounts: tz1, tz2, tz3 (36 characters)
199
+ - Contract accounts: KT1 (36 characters)
200
+
201
+ All characters must be valid Base58 characters.
202
+
203
+ Args:
204
+ address: The address to validate
205
+
206
+ Returns:
207
+ True if the address format is valid
208
+ """
209
+ if not address:
210
+ return False
211
+ if not address.startswith(VALID_ADDRESS_PREFIXES):
212
+ return False
213
+ if len(address) != TEZOS_ADDRESS_LENGTH:
214
+ return False
215
+ # Check all characters are valid base58
216
+ for char in address:
217
+ if char not in BASE58_CHARS:
218
+ return False
219
+ return True
220
+
221
+
222
+ def is_valid_operation_hash(op_hash: str) -> bool:
223
+ """Validate a Tezos operation hash format.
224
+
225
+ Operation hashes start with 'o' and are 51 characters of Base58.
226
+
227
+ Args:
228
+ op_hash: The operation hash to validate
229
+
230
+ Returns:
231
+ True if the operation hash format is valid
232
+ """
233
+ if not op_hash:
234
+ return False
235
+ if not op_hash.startswith("o"):
236
+ return False
237
+ if len(op_hash) != TEZOS_OP_HASH_LENGTH:
238
+ return False
239
+ # Check all characters are valid base58
240
+ for char in op_hash:
241
+ if char not in BASE58_CHARS:
242
+ return False
243
+ return True
244
+
245
+
246
+ def create_asset_identifier(network: str, contract_address: str, token_id: int) -> str:
247
+ """Create a CAIP-19 asset identifier for a Tezos FA2 token.
248
+
249
+ Format: tezos:{chainRef}/fa2:{contractAddress}/{tokenId}
250
+
251
+ Args:
252
+ network: CAIP-2 network identifier (e.g., "tezos:NetXdQprcVkpaWU")
253
+ contract_address: FA2 contract address (e.g., "KT1XnTn74bUtxHfDtBmm2bGZAQfhPbvKWR8o")
254
+ token_id: Token ID within the FA2 contract
255
+
256
+ Returns:
257
+ CAIP-19 asset identifier string
258
+ """
259
+ return f"{network}/fa2:{contract_address}/{token_id}"
260
+
261
+
262
+ def parse_asset_identifier(asset: str) -> Dict[str, Any]:
263
+ """Parse a CAIP-19 asset identifier for Tezos FA2 tokens.
264
+
265
+ Supports two formats:
266
+ - CAIP-19: tezos:{chainRef}/fa2:{contractAddress}/{tokenId}
267
+ - Simple: {contractAddress}/{tokenId} or {contractAddress} (tokenId defaults to 0)
268
+
269
+ Args:
270
+ asset: The asset identifier string
271
+
272
+ Returns:
273
+ Dict with "contract_address" (str) and "token_id" (int)
274
+
275
+ Raises:
276
+ ValueError: If the asset format is unrecognized
277
+ """
278
+ if not asset:
279
+ raise ValueError("Asset identifier is empty")
280
+
281
+ # Try CAIP-19 format: tezos:{chainRef}/fa2:{contract}/{tokenId}
282
+ if asset.startswith("tezos:"):
283
+ parts = asset.split("/")
284
+ if len(parts) == 3 and parts[1].startswith("fa2:"):
285
+ contract_address = parts[1][4:] # Remove "fa2:" prefix
286
+ try:
287
+ token_id = int(parts[2])
288
+ except ValueError:
289
+ raise ValueError(f"Invalid token ID in asset: {asset}")
290
+ if not contract_address.startswith("KT1") or len(contract_address) != 36:
291
+ raise ValueError(f"Invalid contract address in asset: {asset}")
292
+ return {"contract_address": contract_address, "token_id": token_id}
293
+
294
+ # Try simple format: KT1.../tokenId or KT1...
295
+ if asset.startswith("KT1"):
296
+ parts = asset.split("/")
297
+ contract_address = parts[0]
298
+ if len(contract_address) != 36:
299
+ raise ValueError(f"Invalid contract address in asset: {asset}")
300
+ token_id = 0
301
+ if len(parts) == 2:
302
+ try:
303
+ token_id = int(parts[1])
304
+ except ValueError:
305
+ raise ValueError(f"Invalid token ID in asset: {asset}")
306
+ elif len(parts) > 2:
307
+ raise ValueError(f"Unrecognized asset format: {asset}")
308
+ return {"contract_address": contract_address, "token_id": token_id}
309
+
310
+ raise ValueError(
311
+ f"Unrecognized asset format: {asset} "
312
+ f"(expected tezos:{{chainRef}}/fa2:{{contract}}/{{tokenId}} or KT1...)"
313
+ )
314
+
315
+
316
+ def decimal_to_atomic(amount: float, decimals: int) -> str:
317
+ """Convert a decimal amount to atomic units string.
318
+
319
+ Args:
320
+ amount: Decimal amount (e.g., 1.50)
321
+ decimals: Number of decimal places for the token
322
+
323
+ Returns:
324
+ Atomic amount as string (e.g., "1500000" for 6 decimals)
325
+ """
326
+
327
+ multiplier = 10**decimals
328
+ atomic = int(round(amount * multiplier))
329
+ return str(atomic)
330
+
331
+
332
+ def parse_decimal_to_atomic(amount: str, decimals: int) -> str:
333
+ """Convert a decimal string amount to atomic units.
334
+
335
+ Args:
336
+ amount: Decimal string (e.g., "1.50")
337
+ decimals: Number of decimal places for the token
338
+
339
+ Returns:
340
+ Atomic amount string (e.g., "1500000" for 6 decimals)
341
+
342
+ Raises:
343
+ ValueError: If the amount format is invalid
344
+ """
345
+ parts = amount.split(".")
346
+
347
+ integer_part = parts[0]
348
+ fractional_part = ""
349
+
350
+ if len(parts) == 2:
351
+ fractional_part = parts[1]
352
+ elif len(parts) > 2:
353
+ raise ValueError(f"Invalid amount format: {amount}")
354
+
355
+ # Pad or truncate fractional part to match decimals
356
+ if len(fractional_part) > decimals:
357
+ fractional_part = fractional_part[:decimals]
358
+ else:
359
+ fractional_part = fractional_part + "0" * (decimals - len(fractional_part))
360
+
361
+ # Combine and parse as integer
362
+ combined = integer_part + fractional_part
363
+
364
+ # Remove leading zeros but keep at least one digit
365
+ combined = combined.lstrip("0") or "0"
366
+
367
+ try:
368
+ result = int(combined)
369
+ except ValueError:
370
+ raise ValueError(f"Failed to parse amount: {amount}")
371
+
372
+ return str(result)
@@ -0,0 +1,22 @@
1
+ """Tezos Exact-Direct Payment Scheme.
2
+
3
+ This package provides the exact-direct payment scheme implementation for Tezos.
4
+ In this scheme, the client executes the FA2 transfer directly on-chain and
5
+ provides the operation hash as proof of payment. The facilitator then verifies
6
+ the operation on-chain.
7
+
8
+ Components:
9
+ - Client: Executes FA2 transfer and returns opHash
10
+ - Server: Parses prices and enhances requirements with CAIP-19 asset info
11
+ - Facilitator: Verifies operation status and transfer details on-chain
12
+ """
13
+
14
+ from t402.schemes.tezos.exact_direct.client import ExactDirectTezosClient
15
+ from t402.schemes.tezos.exact_direct.server import ExactDirectTezosServer
16
+ from t402.schemes.tezos.exact_direct.facilitator import ExactDirectTezosFacilitator
17
+
18
+ __all__ = [
19
+ "ExactDirectTezosClient",
20
+ "ExactDirectTezosServer",
21
+ "ExactDirectTezosFacilitator",
22
+ ]