t402 1.7.1__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 (102) 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 +164 -1
  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 +67 -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 +70 -0
  39. t402/schemes/evm/upto/client.py +244 -0
  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 +307 -0
  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/schemes/upto/__init__.py +80 -0
  87. t402/schemes/upto/types.py +376 -0
  88. t402/stacks_paywall_template.py +2 -0
  89. t402/svm.py +45 -11
  90. t402/svm_paywall_template.py +1 -1
  91. t402/ton.py +5 -1
  92. t402/ton_paywall_template.py +1 -192
  93. t402/tron.py +2 -0
  94. t402/tron_paywall_template.py +2 -0
  95. t402/types.py +4 -2
  96. t402/wdk/errors.py +15 -5
  97. t402/wdk/signer.py +11 -2
  98. {t402-1.7.1.dist-info → t402-1.9.1.dist-info}/METADATA +42 -1
  99. t402-1.9.1.dist-info/RECORD +125 -0
  100. t402-1.7.1.dist-info/RECORD +0 -67
  101. {t402-1.7.1.dist-info → t402-1.9.1.dist-info}/WHEEL +0 -0
  102. {t402-1.7.1.dist-info → t402-1.9.1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,226 @@
1
+ """Tezos Exact-Direct Scheme - Client Implementation.
2
+
3
+ This module provides the client-side implementation of the exact-direct payment
4
+ scheme for Tezos using FA2 token transfers.
5
+
6
+ In the exact-direct scheme, the client directly executes the FA2 transfer on-chain
7
+ and provides the operation hash as proof of payment. This differs from off-chain
8
+ authorization schemes where the facilitator executes the transfer.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import logging
14
+ from typing import Any, Dict, Union
15
+
16
+ from t402.types import (
17
+ PaymentRequirementsV2,
18
+ T402_VERSION_V1,
19
+ T402_VERSION_V2,
20
+ )
21
+ from t402.schemes.tezos.constants import (
22
+ SCHEME_EXACT_DIRECT,
23
+ is_tezos_network,
24
+ is_valid_address,
25
+ parse_asset_identifier,
26
+ )
27
+ from t402.schemes.tezos.types import (
28
+ ClientTezosSigner,
29
+ ExactDirectPayload,
30
+ )
31
+
32
+
33
+ logger = logging.getLogger(__name__)
34
+
35
+
36
+ class ExactDirectTezosClient:
37
+ """Client scheme for Tezos exact-direct payments using FA2 transfers.
38
+
39
+ This scheme executes FA2 token transfers directly on-chain and provides
40
+ the operation hash as proof of payment. The facilitator verifies the
41
+ operation status and transfer details.
42
+
43
+ Example:
44
+ ```python
45
+ from t402.schemes.tezos import ExactDirectTezosClient
46
+
47
+ class MyTezosSigner:
48
+ def address(self) -> str:
49
+ return "tz1..."
50
+
51
+ async def transfer_fa2(
52
+ self, contract, token_id, to, amount, network
53
+ ) -> str:
54
+ # Execute FA2 transfer
55
+ return "oo7bHf..." # operation hash
56
+
57
+ signer = MyTezosSigner()
58
+ client = ExactDirectTezosClient(signer=signer)
59
+
60
+ payload = await client.create_payment_payload(
61
+ t402_version=2,
62
+ requirements={
63
+ "scheme": "exact-direct",
64
+ "network": "tezos:NetXdQprcVkpaWU",
65
+ "asset": "tezos:NetXdQprcVkpaWU/fa2:KT1XnTn74bUtxHfDtBmm2bGZAQfhPbvKWR8o/0",
66
+ "amount": "1000000",
67
+ "payTo": "tz1...",
68
+ "maxTimeoutSeconds": 300,
69
+ },
70
+ )
71
+ ```
72
+ """
73
+
74
+ scheme = SCHEME_EXACT_DIRECT
75
+ caip_family = "tezos:*"
76
+
77
+ def __init__(self, signer: ClientTezosSigner):
78
+ """Initialize the Tezos exact-direct client.
79
+
80
+ Args:
81
+ signer: A Tezos signer implementing the ClientTezosSigner protocol.
82
+ Must provide address() and transfer_fa2() methods.
83
+ """
84
+ self._signer = signer
85
+
86
+ @property
87
+ def address(self) -> str:
88
+ """Get the signer's Tezos address."""
89
+ return self._signer.address()
90
+
91
+ async def create_payment_payload(
92
+ self,
93
+ t402_version: int,
94
+ requirements: Union[PaymentRequirementsV2, Dict[str, Any]],
95
+ ) -> Dict[str, Any]:
96
+ """Execute FA2 transfer and create payment payload with operation hash.
97
+
98
+ This method:
99
+ 1. Validates the payment requirements
100
+ 2. Parses the CAIP-19 asset identifier
101
+ 3. Executes the FA2 transfer on-chain via the signer
102
+ 4. Returns a payload containing the operation hash as proof
103
+
104
+ Args:
105
+ t402_version: Protocol version (1 or 2)
106
+ requirements: Payment requirements specifying amount, network, asset, payTo
107
+
108
+ Returns:
109
+ Payment payload dict containing the operation hash and transfer metadata
110
+
111
+ Raises:
112
+ ValueError: If requirements are invalid (wrong scheme, network, address, etc.)
113
+ Exception: If the FA2 transfer fails
114
+ """
115
+ # Convert to dict for easier access
116
+ if hasattr(requirements, "model_dump"):
117
+ req = requirements.model_dump(by_alias=True)
118
+ else:
119
+ req = dict(requirements)
120
+
121
+ # Validate requirements
122
+ self._validate_requirements(req)
123
+
124
+ # Extract fields
125
+ network = req.get("network", "")
126
+ asset = req.get("asset", "")
127
+ amount = req.get("amount", "0")
128
+ pay_to = req.get("payTo") or req.get("pay_to", "")
129
+
130
+ # Parse asset to get contract address and token ID
131
+ asset_info = parse_asset_identifier(asset)
132
+ contract_address = asset_info["contract_address"]
133
+ token_id = asset_info["token_id"]
134
+
135
+ # Parse amount as integer
136
+ amount_int = int(amount)
137
+
138
+ # Execute FA2 transfer on-chain
139
+ logger.debug(
140
+ "Executing FA2 transfer: contract=%s, token_id=%d, to=%s, amount=%d",
141
+ contract_address,
142
+ token_id,
143
+ pay_to,
144
+ amount_int,
145
+ )
146
+ op_hash = await self._signer.transfer_fa2(
147
+ contract=contract_address,
148
+ token_id=token_id,
149
+ to=pay_to,
150
+ amount=amount_int,
151
+ network=network,
152
+ )
153
+
154
+ # Build the payload
155
+ payload_data = ExactDirectPayload(
156
+ op_hash=op_hash,
157
+ from_=self._signer.address(),
158
+ to=pay_to,
159
+ amount=amount,
160
+ contract_address=contract_address,
161
+ token_id=token_id,
162
+ )
163
+
164
+ if t402_version == T402_VERSION_V1:
165
+ return {
166
+ "t402Version": T402_VERSION_V1,
167
+ "scheme": self.scheme,
168
+ "network": network,
169
+ "payload": payload_data.to_map(),
170
+ }
171
+
172
+ # V2 format
173
+ return {
174
+ "t402Version": T402_VERSION_V2,
175
+ "payload": payload_data.to_map(),
176
+ }
177
+
178
+ def _validate_requirements(self, req: Dict[str, Any]) -> None:
179
+ """Validate payment requirements for the exact-direct scheme.
180
+
181
+ Args:
182
+ req: Requirements dict
183
+
184
+ Raises:
185
+ ValueError: If any validation check fails
186
+ """
187
+ # Check scheme
188
+ scheme = req.get("scheme", "")
189
+ if scheme and scheme != SCHEME_EXACT_DIRECT:
190
+ raise ValueError(
191
+ f"Invalid scheme: expected {SCHEME_EXACT_DIRECT}, got {scheme}"
192
+ )
193
+
194
+ # Check network is Tezos
195
+ network = req.get("network", "")
196
+ if not is_tezos_network(network):
197
+ raise ValueError(f"Invalid network: {network} (expected tezos:*)")
198
+
199
+ # Check payTo address
200
+ pay_to = req.get("payTo") or req.get("pay_to", "")
201
+ if not pay_to:
202
+ raise ValueError("PayTo address is required")
203
+ if not is_valid_address(pay_to):
204
+ raise ValueError(f"Invalid payTo address: {pay_to}")
205
+
206
+ # Check amount
207
+ amount = req.get("amount", "")
208
+ if not amount:
209
+ raise ValueError("Amount is required")
210
+ try:
211
+ amount_int = int(amount)
212
+ if amount_int <= 0:
213
+ raise ValueError(
214
+ f"Invalid amount: {amount} (must be a positive integer)"
215
+ )
216
+ except ValueError:
217
+ raise ValueError(
218
+ f"Invalid amount: {amount} (must be a positive integer string)"
219
+ )
220
+
221
+ # Check asset
222
+ asset = req.get("asset", "")
223
+ if not asset:
224
+ raise ValueError("Asset is required")
225
+ # This will raise ValueError if invalid
226
+ parse_asset_identifier(asset)