t402 1.9.0__py3-none-any.whl → 1.10.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 +2 -1
- t402/a2a/__init__.py +73 -0
- t402/a2a/helpers.py +158 -0
- t402/a2a/types.py +145 -0
- t402/bridge/client.py +13 -5
- t402/bridge/constants.py +4 -2
- 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/django/__init__.py +42 -0
- t402/django/middleware.py +596 -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/errors.py +213 -0
- t402/evm_paywall_template.py +1 -1
- t402/facilitator.py +125 -0
- t402/fastapi/middleware.py +1 -3
- t402/mcp/constants.py +3 -6
- t402/mcp/server.py +501 -84
- t402/mcp/web3_utils.py +493 -0
- t402/multisig/__init__.py +120 -0
- t402/multisig/constants.py +54 -0
- t402/multisig/safe.py +441 -0
- t402/multisig/signature.py +228 -0
- t402/multisig/transaction.py +238 -0
- t402/multisig/types.py +108 -0
- t402/multisig/utils.py +77 -0
- t402/near_paywall_template.py +2 -0
- t402/networks.py +34 -1
- t402/paywall.py +1 -3
- t402/schemes/__init__.py +143 -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/cosmos/__init__.py +114 -0
- t402/schemes/cosmos/constants.py +211 -0
- t402/schemes/cosmos/exact_direct/__init__.py +21 -0
- t402/schemes/cosmos/exact_direct/client.py +198 -0
- t402/schemes/cosmos/exact_direct/facilitator.py +493 -0
- t402/schemes/cosmos/exact_direct/server.py +315 -0
- t402/schemes/cosmos/types.py +501 -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 +137 -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/near/upto/__init__.py +54 -0
- t402/schemes/near/upto/types.py +272 -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 +44 -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/svm/upto/__init__.py +23 -0
- t402/schemes/svm/upto/types.py +193 -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 +24 -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/ton/upto/__init__.py +31 -0
- t402/schemes/ton/upto/types.py +215 -0
- t402/schemes/tron/__init__.py +28 -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/schemes/tron/upto/__init__.py +30 -0
- t402/schemes/tron/upto/types.py +213 -0
- t402/stacks_paywall_template.py +2 -0
- t402/starlette/__init__.py +38 -0
- t402/starlette/middleware.py +522 -0
- t402/svm.py +45 -11
- t402/svm_paywall_template.py +1 -1
- t402/ton.py +6 -2
- t402/ton_paywall_template.py +1 -192
- t402/tron.py +2 -0
- t402/tron_paywall_template.py +2 -0
- t402/types.py +103 -3
- t402/wdk/chains.py +1 -1
- t402/wdk/errors.py +15 -5
- t402/wdk/signer.py +11 -2
- {t402-1.9.0.dist-info → t402-1.10.0.dist-info}/METADATA +42 -1
- t402-1.10.0.dist-info/RECORD +156 -0
- t402-1.9.0.dist-info/RECORD +0 -72
- {t402-1.9.0.dist-info → t402-1.10.0.dist-info}/WHEEL +0 -0
- {t402-1.9.0.dist-info → t402-1.10.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""EVM Exact-Legacy Scheme - Server Implementation.
|
|
2
|
+
|
|
3
|
+
This module provides the server-side implementation of the exact-legacy payment scheme
|
|
4
|
+
for EVM networks using the approve + transferFrom pattern.
|
|
5
|
+
|
|
6
|
+
.. deprecated:: 2.3.0
|
|
7
|
+
The exact-legacy scheme is deprecated in favor of using USDT0 with the "exact" scheme.
|
|
8
|
+
USDT0 supports EIP-3009 for gasless transfers and is available on 19+ chains via LayerZero.
|
|
9
|
+
|
|
10
|
+
**Migration Guide:**
|
|
11
|
+
- Replace: `SCHEME_EXACT_LEGACY` with `SCHEME_EXACT`
|
|
12
|
+
- Replace: legacy USDT tokens with USDT0 tokens
|
|
13
|
+
- See https://docs.t402.io/advanced/migration-v1-to-v2 for full migration guide
|
|
14
|
+
|
|
15
|
+
**Why Migrate:**
|
|
16
|
+
1. Gasless transfers: USDT0 supports EIP-3009, eliminating gas costs for users
|
|
17
|
+
2. Cross-chain: USDT0 is available on 19+ chains with LayerZero bridging
|
|
18
|
+
3. Better UX: No separate approve transaction required
|
|
19
|
+
4. Future support: exact-legacy will be removed in v3.0.0
|
|
20
|
+
|
|
21
|
+
**Supported Chains for Migration:**
|
|
22
|
+
- Ethereum (1) - USDT0: 0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee
|
|
23
|
+
- Arbitrum (42161) - USDT0: 0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9
|
|
24
|
+
- Base (8453) - Use USDC: 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
|
|
25
|
+
- And 16+ more chains
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
from decimal import Decimal
|
|
31
|
+
from typing import Any, Dict, List, Union
|
|
32
|
+
|
|
33
|
+
from t402.types import (
|
|
34
|
+
PaymentRequirementsV2,
|
|
35
|
+
Network,
|
|
36
|
+
)
|
|
37
|
+
from t402.schemes.interfaces import AssetAmount, SupportedKindDict
|
|
38
|
+
from t402.chains import (
|
|
39
|
+
get_chain_id,
|
|
40
|
+
get_token_decimals,
|
|
41
|
+
KNOWN_TOKENS,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# Constants
|
|
46
|
+
SCHEME_EXACT_LEGACY = "exact-legacy"
|
|
47
|
+
|
|
48
|
+
# Legacy USDT token addresses by chain ID
|
|
49
|
+
LEGACY_USDT_TOKENS = {
|
|
50
|
+
"56": "0x55d398326f99059fF775485246999027B3197955", # BNB Chain
|
|
51
|
+
"43114": "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7", # Avalanche
|
|
52
|
+
"250": "0x049d68029688eabf473097a2fc38ef61633a3c7a", # Fantom
|
|
53
|
+
"42220": "0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e", # Celo
|
|
54
|
+
"8217": "0xcee8faf64bb97a73bb51e115aa89c17ffa8dd167", # Kaia
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def get_default_legacy_token(chain_id: str) -> str:
|
|
59
|
+
"""Get the default legacy USDT token address for a chain.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
chain_id: Chain ID as string
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
Token address
|
|
66
|
+
|
|
67
|
+
Raises:
|
|
68
|
+
ValueError: If chain doesn't have legacy USDT
|
|
69
|
+
"""
|
|
70
|
+
if chain_id in LEGACY_USDT_TOKENS:
|
|
71
|
+
return LEGACY_USDT_TOKENS[chain_id]
|
|
72
|
+
|
|
73
|
+
# Check KNOWN_TOKENS for usdt entry
|
|
74
|
+
if chain_id in KNOWN_TOKENS:
|
|
75
|
+
for token in KNOWN_TOKENS[chain_id]:
|
|
76
|
+
if token.get("human_name") == "usdt":
|
|
77
|
+
return token["address"]
|
|
78
|
+
|
|
79
|
+
raise ValueError(f"No legacy USDT token found for chain {chain_id}")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class ExactLegacyEvmServerScheme:
|
|
83
|
+
"""Server scheme for EVM exact-legacy payments.
|
|
84
|
+
|
|
85
|
+
Handles parsing user-friendly prices and enhancing payment requirements
|
|
86
|
+
with EIP-712 domain information needed for clients.
|
|
87
|
+
|
|
88
|
+
Example:
|
|
89
|
+
```python
|
|
90
|
+
scheme = ExactLegacyEvmServerScheme()
|
|
91
|
+
|
|
92
|
+
# Parse price
|
|
93
|
+
asset_amount = await scheme.parse_price("$0.10", "eip155:56")
|
|
94
|
+
# Returns: {"amount": "100000000000000000", "asset": "0x55d398...", "extra": {...}}
|
|
95
|
+
|
|
96
|
+
# Enhance requirements
|
|
97
|
+
enhanced = await scheme.enhance_requirements(
|
|
98
|
+
requirements,
|
|
99
|
+
supported_kind,
|
|
100
|
+
facilitator_extensions,
|
|
101
|
+
)
|
|
102
|
+
```
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
scheme = SCHEME_EXACT_LEGACY
|
|
106
|
+
caip_family = "eip155:*"
|
|
107
|
+
|
|
108
|
+
async def parse_price(
|
|
109
|
+
self,
|
|
110
|
+
price: Union[str, int, float, Dict[str, Any]],
|
|
111
|
+
network: Network,
|
|
112
|
+
) -> AssetAmount:
|
|
113
|
+
"""Parse a user-friendly price to atomic amount and asset.
|
|
114
|
+
|
|
115
|
+
Supports:
|
|
116
|
+
- String with $ prefix: "$0.10" -> 100000000000000000 (18 decimals for BSC)
|
|
117
|
+
- String without prefix: "0.10" -> 100000000000000000
|
|
118
|
+
- Integer/float: 0.10 -> 100000000000000000
|
|
119
|
+
- Dict (TokenAmount): {"amount": "100000", "asset": "0x..."}
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
price: User-friendly price
|
|
123
|
+
network: Network identifier (CAIP-2 format)
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
AssetAmount dict with amount, asset, and extra metadata
|
|
127
|
+
"""
|
|
128
|
+
chain_id = self._get_chain_id(network)
|
|
129
|
+
|
|
130
|
+
# Handle dict (already in TokenAmount format)
|
|
131
|
+
if isinstance(price, dict):
|
|
132
|
+
return {
|
|
133
|
+
"amount": str(price.get("amount", "0")),
|
|
134
|
+
"asset": price.get("asset", ""),
|
|
135
|
+
"extra": price.get("extra", {}),
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
# Get legacy USDT token for the network
|
|
139
|
+
chain_id_str = str(chain_id)
|
|
140
|
+
asset_address = get_default_legacy_token(chain_id_str)
|
|
141
|
+
decimals = get_token_decimals(chain_id_str, asset_address)
|
|
142
|
+
|
|
143
|
+
# Parse price string/number
|
|
144
|
+
if isinstance(price, str):
|
|
145
|
+
if price.startswith("$"):
|
|
146
|
+
price = price[1:]
|
|
147
|
+
amount_decimal = Decimal(price)
|
|
148
|
+
else:
|
|
149
|
+
amount_decimal = Decimal(str(price))
|
|
150
|
+
|
|
151
|
+
# Convert to atomic units
|
|
152
|
+
atomic_amount = int(amount_decimal * Decimal(10**decimals))
|
|
153
|
+
|
|
154
|
+
# Get EIP-712 domain info
|
|
155
|
+
extra = {
|
|
156
|
+
"name": "T402LegacyTransfer",
|
|
157
|
+
"version": "1",
|
|
158
|
+
"decimals": decimals,
|
|
159
|
+
"tokenType": "legacy",
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
"amount": str(atomic_amount),
|
|
164
|
+
"asset": asset_address,
|
|
165
|
+
"extra": extra,
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async def enhance_requirements(
|
|
169
|
+
self,
|
|
170
|
+
requirements: Union[PaymentRequirementsV2, Dict[str, Any]],
|
|
171
|
+
supported_kind: SupportedKindDict,
|
|
172
|
+
facilitator_extensions: List[str],
|
|
173
|
+
) -> Union[PaymentRequirementsV2, Dict[str, Any]]:
|
|
174
|
+
"""Enhance payment requirements with EVM-specific metadata.
|
|
175
|
+
|
|
176
|
+
Adds EIP-712 domain information and spender address to the extra field
|
|
177
|
+
so clients can properly sign the authorization.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
requirements: Base payment requirements
|
|
181
|
+
supported_kind: Matched SupportedKind from facilitator
|
|
182
|
+
facilitator_extensions: Extensions supported by facilitator
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
Enhanced requirements with EIP-712 domain in extra
|
|
186
|
+
"""
|
|
187
|
+
# Convert to dict for modification
|
|
188
|
+
if hasattr(requirements, "model_dump"):
|
|
189
|
+
req = requirements.model_dump(by_alias=True)
|
|
190
|
+
else:
|
|
191
|
+
req = dict(requirements)
|
|
192
|
+
|
|
193
|
+
# Ensure extra exists
|
|
194
|
+
if "extra" not in req or req["extra"] is None:
|
|
195
|
+
req["extra"] = {}
|
|
196
|
+
|
|
197
|
+
# Add EIP-712 domain info if not present
|
|
198
|
+
if "name" not in req["extra"]:
|
|
199
|
+
req["extra"]["name"] = "T402LegacyTransfer"
|
|
200
|
+
if "version" not in req["extra"]:
|
|
201
|
+
req["extra"]["version"] = "1"
|
|
202
|
+
|
|
203
|
+
# Mark as legacy token type
|
|
204
|
+
req["extra"]["tokenType"] = "legacy"
|
|
205
|
+
|
|
206
|
+
# Add facilitator extra data if available (includes spender address)
|
|
207
|
+
if supported_kind.get("extra"):
|
|
208
|
+
for key, value in supported_kind["extra"].items():
|
|
209
|
+
if key not in req["extra"]:
|
|
210
|
+
req["extra"][key] = value
|
|
211
|
+
|
|
212
|
+
return req
|
|
213
|
+
|
|
214
|
+
def _get_chain_id(self, network: str) -> int:
|
|
215
|
+
"""Get chain ID from network identifier.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
network: Network identifier (CAIP-2 or legacy format)
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
Chain ID as integer
|
|
222
|
+
"""
|
|
223
|
+
# Handle CAIP-2 format (eip155:8453)
|
|
224
|
+
if network.startswith("eip155:"):
|
|
225
|
+
return int(network.split(":")[1])
|
|
226
|
+
|
|
227
|
+
# Handle legacy format
|
|
228
|
+
try:
|
|
229
|
+
return int(get_chain_id(network))
|
|
230
|
+
except (KeyError, ValueError):
|
|
231
|
+
raise ValueError(f"Unknown network: {network}")
|
|
@@ -33,6 +33,14 @@ from t402.schemes.evm.upto.client import (
|
|
|
33
33
|
SCHEME_UPTO,
|
|
34
34
|
)
|
|
35
35
|
|
|
36
|
+
from t402.schemes.evm.upto.server import (
|
|
37
|
+
UptoEvmServerScheme,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
from t402.schemes.evm.upto.facilitator import (
|
|
41
|
+
UptoEvmFacilitatorScheme,
|
|
42
|
+
)
|
|
43
|
+
|
|
36
44
|
__all__ = [
|
|
37
45
|
# Constants
|
|
38
46
|
"SCHEME_UPTO",
|
|
@@ -41,6 +49,10 @@ __all__ = [
|
|
|
41
49
|
# Client
|
|
42
50
|
"UptoEvmClientScheme",
|
|
43
51
|
"create_payment_nonce",
|
|
52
|
+
# Server
|
|
53
|
+
"UptoEvmServerScheme",
|
|
54
|
+
# Facilitator
|
|
55
|
+
"UptoEvmFacilitatorScheme",
|
|
44
56
|
# Types
|
|
45
57
|
"PermitSignature",
|
|
46
58
|
"PermitAuthorization",
|
t402/schemes/evm/upto/client.py
CHANGED
|
@@ -74,7 +74,9 @@ class UptoEvmClientScheme:
|
|
|
74
74
|
async def create_payment_payload(
|
|
75
75
|
self,
|
|
76
76
|
t402_version: int,
|
|
77
|
-
requirements: Union[
|
|
77
|
+
requirements: Union[
|
|
78
|
+
UptoPaymentRequirements, PaymentRequirementsV2, Dict[str, Any]
|
|
79
|
+
],
|
|
78
80
|
) -> Dict[str, Any]:
|
|
79
81
|
"""Create a payment payload for EVM upto scheme.
|
|
80
82
|
|
|
@@ -114,7 +116,9 @@ class UptoEvmClientScheme:
|
|
|
114
116
|
asset = req.get("asset", "")
|
|
115
117
|
|
|
116
118
|
# Get timeout
|
|
117
|
-
max_timeout = req.get("maxTimeoutSeconds") or req.get(
|
|
119
|
+
max_timeout = req.get("maxTimeoutSeconds") or req.get(
|
|
120
|
+
"max_timeout_seconds", 300
|
|
121
|
+
)
|
|
118
122
|
|
|
119
123
|
# Get EIP-712 domain info
|
|
120
124
|
token_name = extra.get("name", "USD Coin")
|