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.
- t402/__init__.py +2 -1
- t402/bridge/client.py +13 -5
- t402/bridge/constants.py +3 -1
- t402/bridge/router.py +1 -1
- t402/bridge/scan.py +3 -1
- t402/chains.py +268 -1
- t402/cli.py +31 -9
- t402/common.py +2 -0
- t402/cosmos_paywall_template.py +2 -0
- t402/encoding.py +9 -3
- t402/erc4337/accounts.py +56 -51
- t402/erc4337/bundlers.py +105 -99
- t402/erc4337/paymasters.py +100 -109
- t402/erc4337/types.py +39 -26
- t402/evm_paywall_template.py +1 -1
- t402/fastapi/middleware.py +1 -3
- t402/mcp/server.py +79 -46
- t402/near_paywall_template.py +2 -0
- t402/networks.py +34 -1
- t402/paywall.py +1 -3
- t402/schemes/__init__.py +124 -0
- t402/schemes/aptos/__init__.py +70 -0
- t402/schemes/aptos/constants.py +349 -0
- t402/schemes/aptos/exact_direct/__init__.py +44 -0
- t402/schemes/aptos/exact_direct/client.py +202 -0
- t402/schemes/aptos/exact_direct/facilitator.py +426 -0
- t402/schemes/aptos/exact_direct/server.py +272 -0
- t402/schemes/aptos/types.py +237 -0
- t402/schemes/evm/__init__.py +46 -1
- t402/schemes/evm/exact/__init__.py +11 -0
- t402/schemes/evm/exact/client.py +3 -1
- t402/schemes/evm/exact/facilitator.py +894 -0
- t402/schemes/evm/exact/server.py +1 -1
- t402/schemes/evm/exact_legacy/__init__.py +38 -0
- t402/schemes/evm/exact_legacy/client.py +291 -0
- t402/schemes/evm/exact_legacy/facilitator.py +777 -0
- t402/schemes/evm/exact_legacy/server.py +231 -0
- t402/schemes/evm/upto/__init__.py +12 -0
- t402/schemes/evm/upto/client.py +6 -2
- t402/schemes/evm/upto/facilitator.py +625 -0
- t402/schemes/evm/upto/server.py +243 -0
- t402/schemes/evm/upto/types.py +3 -1
- t402/schemes/interfaces.py +6 -2
- t402/schemes/near/__init__.py +112 -0
- t402/schemes/near/constants.py +189 -0
- t402/schemes/near/exact_direct/__init__.py +21 -0
- t402/schemes/near/exact_direct/client.py +204 -0
- t402/schemes/near/exact_direct/facilitator.py +455 -0
- t402/schemes/near/exact_direct/server.py +303 -0
- t402/schemes/near/types.py +419 -0
- t402/schemes/polkadot/__init__.py +72 -0
- t402/schemes/polkadot/constants.py +155 -0
- t402/schemes/polkadot/exact_direct/__init__.py +43 -0
- t402/schemes/polkadot/exact_direct/client.py +235 -0
- t402/schemes/polkadot/exact_direct/facilitator.py +428 -0
- t402/schemes/polkadot/exact_direct/server.py +292 -0
- t402/schemes/polkadot/types.py +385 -0
- t402/schemes/registry.py +6 -2
- t402/schemes/stacks/__init__.py +68 -0
- t402/schemes/stacks/constants.py +122 -0
- t402/schemes/stacks/exact_direct/__init__.py +43 -0
- t402/schemes/stacks/exact_direct/client.py +222 -0
- t402/schemes/stacks/exact_direct/facilitator.py +424 -0
- t402/schemes/stacks/exact_direct/server.py +292 -0
- t402/schemes/stacks/types.py +380 -0
- t402/schemes/svm/__init__.py +29 -0
- t402/schemes/svm/exact/__init__.py +35 -0
- t402/schemes/svm/exact/client.py +23 -0
- t402/schemes/svm/exact/facilitator.py +24 -0
- t402/schemes/svm/exact/server.py +20 -0
- t402/schemes/tezos/__init__.py +84 -0
- t402/schemes/tezos/constants.py +372 -0
- t402/schemes/tezos/exact_direct/__init__.py +22 -0
- t402/schemes/tezos/exact_direct/client.py +226 -0
- t402/schemes/tezos/exact_direct/facilitator.py +491 -0
- t402/schemes/tezos/exact_direct/server.py +277 -0
- t402/schemes/tezos/types.py +220 -0
- t402/schemes/ton/__init__.py +9 -2
- t402/schemes/ton/exact/__init__.py +7 -0
- t402/schemes/ton/exact/facilitator.py +730 -0
- t402/schemes/ton/exact/server.py +1 -1
- t402/schemes/tron/__init__.py +11 -2
- t402/schemes/tron/exact/__init__.py +9 -0
- t402/schemes/tron/exact/facilitator.py +673 -0
- t402/schemes/tron/exact/server.py +1 -1
- t402/stacks_paywall_template.py +2 -0
- t402/svm.py +45 -11
- t402/svm_paywall_template.py +1 -1
- t402/ton.py +5 -1
- t402/ton_paywall_template.py +1 -192
- t402/tron.py +2 -0
- t402/tron_paywall_template.py +2 -0
- t402/types.py +3 -1
- t402/wdk/errors.py +15 -5
- t402/wdk/signer.py +11 -2
- {t402-1.9.0.dist-info → t402-1.9.1.dist-info}/METADATA +42 -1
- t402-1.9.1.dist-info/RECORD +125 -0
- t402-1.9.0.dist-info/RECORD +0 -72
- {t402-1.9.0.dist-info → t402-1.9.1.dist-info}/WHEEL +0 -0
- {t402-1.9.0.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)
|