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,243 @@
1
+ """EVM Up-To Scheme - Server Implementation.
2
+
3
+ This module provides the server-side implementation of the upto payment scheme
4
+ for EVM networks using EIP-2612 Permit.
5
+
6
+ The server parses user-friendly prices into atomic token amounts and enhances
7
+ payment requirements with EIP-712 domain information needed by clients to
8
+ sign Permit authorizations.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from decimal import Decimal
14
+ from typing import Any, Dict, List, Optional, Union
15
+
16
+ from t402.types import (
17
+ PaymentRequirementsV2,
18
+ Network,
19
+ )
20
+ from t402.schemes.interfaces import AssetAmount, SupportedKindDict
21
+ from t402.chains import (
22
+ get_chain_id,
23
+ get_token_decimals,
24
+ get_token_name,
25
+ get_token_version,
26
+ get_default_token_address,
27
+ )
28
+
29
+
30
+ # Constants
31
+ SCHEME_UPTO = "upto"
32
+
33
+
34
+ class UptoEvmServerScheme:
35
+ """Server scheme for EVM upto payments using EIP-2612 Permit.
36
+
37
+ Handles parsing user-friendly prices and enhancing payment requirements
38
+ with EIP-712 Permit domain information needed for clients to sign
39
+ gasless token approvals.
40
+
41
+ The upto scheme allows clients to authorize a maximum amount (maxAmount)
42
+ that the facilitator can settle up to, enabling usage-based billing.
43
+
44
+ Example:
45
+ ```python
46
+ scheme = UptoEvmServerScheme()
47
+
48
+ # Parse price to get asset amount info
49
+ asset_amount = await scheme.parse_price("$1.00", "eip155:8453")
50
+ # Returns: {"amount": "1000000", "asset": "0x833589...", "extra": {...}}
51
+
52
+ # Enhance requirements with EIP-712 domain info
53
+ enhanced = await scheme.enhance_requirements(
54
+ requirements,
55
+ supported_kind,
56
+ facilitator_extensions,
57
+ )
58
+ ```
59
+ """
60
+
61
+ scheme = SCHEME_UPTO
62
+ caip_family = "eip155:*"
63
+
64
+ def __init__(
65
+ self,
66
+ router_address: Optional[str] = None,
67
+ ):
68
+ """Initialize the server scheme.
69
+
70
+ Args:
71
+ router_address: Optional default router/spender contract address.
72
+ If provided, it will be included in enhanced requirements.
73
+ """
74
+ self._router_address = router_address
75
+
76
+ async def parse_price(
77
+ self,
78
+ price: Union[str, int, float, Dict[str, Any]],
79
+ network: Network,
80
+ ) -> AssetAmount:
81
+ """Parse a user-friendly price to atomic amount and asset.
82
+
83
+ For the upto scheme, this returns the maxAmount the client should
84
+ authorize. The actual settled amount may be less.
85
+
86
+ Supports:
87
+ - String with $ prefix: "$1.00" -> 1000000 (6 decimals)
88
+ - String without prefix: "1.00" -> 1000000
89
+ - Integer/float: 1.00 -> 1000000
90
+ - Dict (TokenAmount): {"amount": "1000000", "asset": "0x..."}
91
+
92
+ Args:
93
+ price: User-friendly price (represents the max amount)
94
+ network: Network identifier (CAIP-2 format, e.g., "eip155:8453")
95
+
96
+ Returns:
97
+ AssetAmount dict with amount, asset, and extra metadata
98
+ containing EIP-712 domain info.
99
+
100
+ Raises:
101
+ ValueError: If price format is invalid or network is unsupported
102
+ """
103
+ chain_id = self._get_chain_id(network)
104
+
105
+ # Handle dict (already in TokenAmount format)
106
+ if isinstance(price, dict):
107
+ return {
108
+ "amount": str(price.get("amount", "0")),
109
+ "asset": price.get("asset", ""),
110
+ "extra": price.get("extra", {}),
111
+ }
112
+
113
+ # Get chain ID as string for token lookups
114
+ chain_id_str = str(chain_id)
115
+
116
+ # Get default token for the network
117
+ # Try USDT0 first, fall back to USDT, then USDC
118
+ try:
119
+ asset_address = get_default_token_address(chain_id_str, "usdt0")
120
+ except (ValueError, KeyError):
121
+ try:
122
+ asset_address = get_default_token_address(chain_id_str, "usdt")
123
+ except (ValueError, KeyError):
124
+ try:
125
+ asset_address = get_default_token_address(chain_id_str, "usdc")
126
+ except (ValueError, KeyError):
127
+ raise ValueError(
128
+ f"Unknown network: no known token for chain {chain_id_str}"
129
+ )
130
+
131
+ decimals = get_token_decimals(chain_id_str, asset_address)
132
+
133
+ # Parse price string/number
134
+ if isinstance(price, str):
135
+ if price.startswith("$"):
136
+ price = price[1:]
137
+ amount_decimal = Decimal(price)
138
+ else:
139
+ amount_decimal = Decimal(str(price))
140
+
141
+ # Convert to atomic units
142
+ atomic_amount = int(amount_decimal * Decimal(10**decimals))
143
+
144
+ # Get EIP-712 domain info for Permit signing
145
+ extra: Dict[str, Any] = {
146
+ "name": get_token_name(chain_id_str, asset_address),
147
+ "version": get_token_version(chain_id_str, asset_address),
148
+ "decimals": decimals,
149
+ }
150
+
151
+ # Include router address if configured
152
+ if self._router_address:
153
+ extra["routerAddress"] = self._router_address
154
+
155
+ return {
156
+ "amount": str(atomic_amount),
157
+ "asset": asset_address,
158
+ "extra": extra,
159
+ }
160
+
161
+ async def enhance_requirements(
162
+ self,
163
+ requirements: Union[PaymentRequirementsV2, Dict[str, Any]],
164
+ supported_kind: SupportedKindDict,
165
+ facilitator_extensions: List[str],
166
+ ) -> Union[PaymentRequirementsV2, Dict[str, Any]]:
167
+ """Enhance payment requirements with EVM Permit-specific metadata.
168
+
169
+ Adds EIP-712 domain information (token name, version) and optionally
170
+ the router/spender address to the extra field so clients can properly
171
+ sign the EIP-2612 Permit authorization.
172
+
173
+ Args:
174
+ requirements: Base payment requirements (with maxAmount/amount set)
175
+ supported_kind: Matched SupportedKind from facilitator's /supported
176
+ facilitator_extensions: Extensions supported by facilitator
177
+
178
+ Returns:
179
+ Enhanced requirements with EIP-712 Permit domain info in extra
180
+ """
181
+ # Convert to dict for modification
182
+ if hasattr(requirements, "model_dump"):
183
+ req = requirements.model_dump(by_alias=True)
184
+ else:
185
+ req = dict(requirements)
186
+
187
+ network = req.get("network", "")
188
+ asset = req.get("asset", "")
189
+
190
+ # Get chain ID as string
191
+ chain_id = str(self._get_chain_id(network))
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
+ try:
200
+ req["extra"]["name"] = get_token_name(chain_id, asset)
201
+ except (ValueError, KeyError):
202
+ # If token not found in known tokens, use a sensible default
203
+ req["extra"]["name"] = "TetherToken"
204
+
205
+ if "version" not in req["extra"]:
206
+ try:
207
+ req["extra"]["version"] = get_token_version(chain_id, asset)
208
+ except (ValueError, KeyError):
209
+ req["extra"]["version"] = "1"
210
+
211
+ # Add router address if configured and not already present
212
+ if self._router_address and "routerAddress" not in req["extra"]:
213
+ req["extra"]["routerAddress"] = self._router_address
214
+
215
+ # Add facilitator extra data if available
216
+ if supported_kind.get("extra"):
217
+ for key, value in supported_kind["extra"].items():
218
+ if key not in req["extra"]:
219
+ req["extra"][key] = value
220
+
221
+ return req
222
+
223
+ def _get_chain_id(self, network: str) -> int:
224
+ """Get chain ID from network identifier.
225
+
226
+ Args:
227
+ network: Network identifier (CAIP-2 or legacy format)
228
+
229
+ Returns:
230
+ Chain ID as integer
231
+
232
+ Raises:
233
+ ValueError: If the network format is unrecognized
234
+ """
235
+ # Handle CAIP-2 format (eip155:8453)
236
+ if network.startswith("eip155:"):
237
+ return int(network.split(":")[1])
238
+
239
+ # Handle legacy format
240
+ try:
241
+ return int(get_chain_id(network))
242
+ except (KeyError, ValueError):
243
+ raise ValueError(f"Unknown network: {network}")
@@ -0,0 +1,307 @@
1
+ """EVM Up-To Scheme Types.
2
+
3
+ EVM-specific types for the up-to payment scheme using EIP-2612 Permit.
4
+ The Permit standard allows gasless token approvals via signature.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import Any, Dict, List, Optional
10
+ from pydantic import BaseModel, ConfigDict, Field
11
+ from pydantic.alias_generators import to_camel
12
+
13
+
14
+ # EIP-712 Permit type definitions
15
+ PERMIT_TYPES: Dict[str, List[Dict[str, str]]] = {
16
+ "Permit": [
17
+ {"name": "owner", "type": "address"},
18
+ {"name": "spender", "type": "address"},
19
+ {"name": "value", "type": "uint256"},
20
+ {"name": "nonce", "type": "uint256"},
21
+ {"name": "deadline", "type": "uint256"},
22
+ ]
23
+ }
24
+
25
+ PERMIT_DOMAIN_TYPES: List[Dict[str, str]] = [
26
+ {"name": "name", "type": "string"},
27
+ {"name": "version", "type": "string"},
28
+ {"name": "chainId", "type": "uint256"},
29
+ {"name": "verifyingContract", "type": "address"},
30
+ ]
31
+
32
+
33
+ class PermitSignature(BaseModel):
34
+ """EIP-2612 permit signature components."""
35
+
36
+ v: int = Field(description="Recovery id")
37
+ r: str = Field(description="First 32 bytes of signature")
38
+ s: str = Field(description="Second 32 bytes of signature")
39
+
40
+ model_config = ConfigDict(
41
+ alias_generator=to_camel,
42
+ populate_by_name=True,
43
+ from_attributes=True,
44
+ )
45
+
46
+
47
+ class PermitAuthorization(BaseModel):
48
+ """EIP-2612 permit authorization parameters."""
49
+
50
+ owner: str = Field(description="Token owner address")
51
+ spender: str = Field(description="Address authorized to spend (router contract)")
52
+ value: str = Field(description="Maximum authorized value")
53
+ deadline: str = Field(description="Permit deadline (unix timestamp)")
54
+ nonce: int = Field(description="Permit nonce from token contract")
55
+
56
+ model_config = ConfigDict(
57
+ alias_generator=to_camel,
58
+ populate_by_name=True,
59
+ from_attributes=True,
60
+ )
61
+
62
+
63
+ class UptoEIP2612Payload(BaseModel):
64
+ """Up-to payment payload using EIP-2612 Permit."""
65
+
66
+ signature: PermitSignature = Field(description="Permit signature components")
67
+ authorization: PermitAuthorization = Field(description="Permit parameters")
68
+ payment_nonce: str = Field(
69
+ alias="paymentNonce",
70
+ description="Unique nonce to prevent replay attacks",
71
+ )
72
+
73
+ model_config = ConfigDict(
74
+ alias_generator=to_camel,
75
+ populate_by_name=True,
76
+ from_attributes=True,
77
+ )
78
+
79
+ def to_dict(self) -> Dict[str, Any]:
80
+ """Convert to dictionary for JSON serialization."""
81
+ return {
82
+ "signature": {
83
+ "v": self.signature.v,
84
+ "r": self.signature.r,
85
+ "s": self.signature.s,
86
+ },
87
+ "authorization": {
88
+ "owner": self.authorization.owner,
89
+ "spender": self.authorization.spender,
90
+ "value": self.authorization.value,
91
+ "deadline": self.authorization.deadline,
92
+ "nonce": self.authorization.nonce,
93
+ },
94
+ "paymentNonce": self.payment_nonce,
95
+ }
96
+
97
+
98
+ class UptoCompactPayload(BaseModel):
99
+ """Alternative payload with combined signature."""
100
+
101
+ signature: str = Field(
102
+ description="Combined EIP-2612 permit signature (65 bytes hex)"
103
+ )
104
+ authorization: PermitAuthorization = Field(description="Permit parameters")
105
+ payment_nonce: str = Field(
106
+ alias="paymentNonce",
107
+ description="Unique nonce to prevent replay attacks",
108
+ )
109
+
110
+ model_config = ConfigDict(
111
+ alias_generator=to_camel,
112
+ populate_by_name=True,
113
+ from_attributes=True,
114
+ )
115
+
116
+
117
+ class UptoEvmExtra(BaseModel):
118
+ """EVM-specific extra fields for the upto scheme."""
119
+
120
+ name: str = Field(description="EIP-712 domain name (token name)")
121
+ version: str = Field(description="EIP-712 domain version")
122
+ router_address: Optional[str] = Field(
123
+ default=None,
124
+ alias="routerAddress",
125
+ description="Upto router contract address",
126
+ )
127
+ unit: Optional[str] = Field(
128
+ default=None,
129
+ description="Billing unit (e.g., 'token', 'request')",
130
+ )
131
+ unit_price: Optional[str] = Field(
132
+ default=None,
133
+ alias="unitPrice",
134
+ description="Price per unit in smallest denomination",
135
+ )
136
+
137
+ model_config = ConfigDict(
138
+ alias_generator=to_camel,
139
+ populate_by_name=True,
140
+ from_attributes=True,
141
+ )
142
+
143
+
144
+ class UptoEvmSettlement(BaseModel):
145
+ """EVM-specific settlement request."""
146
+
147
+ settle_amount: str = Field(
148
+ alias="settleAmount",
149
+ description="Actual amount to settle",
150
+ )
151
+ usage_details: Optional["UptoEvmUsageDetails"] = Field(
152
+ default=None,
153
+ alias="usageDetails",
154
+ description="Optional usage information",
155
+ )
156
+
157
+ model_config = ConfigDict(
158
+ alias_generator=to_camel,
159
+ populate_by_name=True,
160
+ from_attributes=True,
161
+ )
162
+
163
+
164
+ class UptoEvmUsageDetails(BaseModel):
165
+ """Usage details for EVM settlement."""
166
+
167
+ units_consumed: Optional[int] = Field(
168
+ default=None,
169
+ alias="unitsConsumed",
170
+ description="Number of units consumed",
171
+ )
172
+ unit_price: Optional[str] = Field(
173
+ default=None,
174
+ alias="unitPrice",
175
+ description="Price per unit used",
176
+ )
177
+ unit_type: Optional[str] = Field(
178
+ default=None,
179
+ alias="unitType",
180
+ description="Type of unit",
181
+ )
182
+ start_time: Optional[int] = Field(
183
+ default=None,
184
+ alias="startTime",
185
+ description="Start timestamp",
186
+ )
187
+ end_time: Optional[int] = Field(
188
+ default=None,
189
+ alias="endTime",
190
+ description="End timestamp",
191
+ )
192
+
193
+ model_config = ConfigDict(
194
+ alias_generator=to_camel,
195
+ populate_by_name=True,
196
+ from_attributes=True,
197
+ )
198
+
199
+
200
+ # Update forward reference
201
+ UptoEvmSettlement.model_rebuild()
202
+
203
+
204
+ def is_eip2612_payload(data: Dict[str, Any]) -> bool:
205
+ """Check if the given data represents an EIP-2612 permit payload.
206
+
207
+ Args:
208
+ data: Dictionary to check
209
+
210
+ Returns:
211
+ True if data has the correct EIP-2612 structure
212
+ """
213
+ if not isinstance(data, dict):
214
+ return False
215
+
216
+ sig = data.get("signature")
217
+ auth = data.get("authorization")
218
+
219
+ if not sig or not auth:
220
+ return False
221
+
222
+ # Check signature structure (should be object with v, r, s)
223
+ if not isinstance(sig, dict):
224
+ return False
225
+ if not all(k in sig for k in ["v", "r", "s"]):
226
+ return False
227
+
228
+ # Check authorization structure
229
+ if not isinstance(auth, dict):
230
+ return False
231
+ required_auth_fields = ["owner", "spender", "value", "deadline"]
232
+ if not all(k in auth for k in required_auth_fields):
233
+ return False
234
+
235
+ return True
236
+
237
+
238
+ def create_permit_domain(
239
+ name: str,
240
+ version: str,
241
+ chain_id: int,
242
+ token_address: str,
243
+ ) -> Dict[str, Any]:
244
+ """Create an EIP-712 domain for permit signing.
245
+
246
+ Args:
247
+ name: Token name
248
+ version: Token version
249
+ chain_id: Chain ID
250
+ token_address: Token contract address
251
+
252
+ Returns:
253
+ EIP-712 domain dictionary
254
+ """
255
+ return {
256
+ "name": name,
257
+ "version": version,
258
+ "chainId": chain_id,
259
+ "verifyingContract": token_address,
260
+ }
261
+
262
+
263
+ def create_permit_message(authorization: PermitAuthorization) -> Dict[str, Any]:
264
+ """Create an EIP-712 message for permit signing.
265
+
266
+ Args:
267
+ authorization: Permit authorization parameters
268
+
269
+ Returns:
270
+ EIP-712 message dictionary
271
+ """
272
+ return {
273
+ "owner": authorization.owner,
274
+ "spender": authorization.spender,
275
+ "value": int(authorization.value),
276
+ "nonce": authorization.nonce,
277
+ "deadline": int(authorization.deadline),
278
+ }
279
+
280
+
281
+ def payload_from_dict(data: Dict[str, Any]) -> UptoEIP2612Payload:
282
+ """Create an UptoEIP2612Payload from a dictionary.
283
+
284
+ Args:
285
+ data: Dictionary containing payload data
286
+
287
+ Returns:
288
+ UptoEIP2612Payload instance
289
+ """
290
+ sig_data = data.get("signature", {})
291
+ auth_data = data.get("authorization", {})
292
+
293
+ return UptoEIP2612Payload(
294
+ signature=PermitSignature(
295
+ v=sig_data.get("v", 0),
296
+ r=sig_data.get("r", ""),
297
+ s=sig_data.get("s", ""),
298
+ ),
299
+ authorization=PermitAuthorization(
300
+ owner=auth_data.get("owner", ""),
301
+ spender=auth_data.get("spender", ""),
302
+ value=auth_data.get("value", ""),
303
+ deadline=auth_data.get("deadline", ""),
304
+ nonce=auth_data.get("nonce", 0),
305
+ ),
306
+ payment_nonce=data.get("paymentNonce", ""),
307
+ )
@@ -25,9 +25,13 @@ from t402.types import (
25
25
 
26
26
 
27
27
  # Type aliases for clarity
28
- Price = Union[str, int, float, Dict[str, Any]] # e.g., "$0.10", 0.10, {"amount": "100000", "asset": "..."}
28
+ Price = Union[
29
+ str, int, float, Dict[str, Any]
30
+ ] # e.g., "$0.10", 0.10, {"amount": "100000", "asset": "..."}
29
31
  AssetAmount = Dict[str, Any] # {"amount": str, "asset": str, "extra"?: dict}
30
- SupportedKindDict = Dict[str, Any] # {"t402Version": int, "scheme": str, "network": str, "extra"?: dict}
32
+ SupportedKindDict = Dict[
33
+ str, Any
34
+ ] # {"t402Version": int, "scheme": str, "network": str, "extra"?: dict}
31
35
 
32
36
 
33
37
  @runtime_checkable
@@ -0,0 +1,112 @@
1
+ """NEAR Blockchain Payment Schemes.
2
+
3
+ This package provides payment scheme implementations for the NEAR blockchain.
4
+
5
+ Supported schemes:
6
+ - exact-direct: Client executes NEP-141 ft_transfer, tx hash used as proof.
7
+
8
+ Usage:
9
+ ```python
10
+ from t402.schemes.near import (
11
+ # Client
12
+ ExactDirectNearClientScheme,
13
+ ExactDirectNearClientConfig,
14
+ # Server
15
+ ExactDirectNearServerScheme,
16
+ ExactDirectNearServerConfig,
17
+ # Facilitator
18
+ ExactDirectNearFacilitatorScheme,
19
+ ExactDirectNearFacilitatorConfig,
20
+ # Signer protocols
21
+ ClientNearSigner,
22
+ FacilitatorNearSigner,
23
+ # Constants
24
+ SCHEME_EXACT_DIRECT,
25
+ NEAR_MAINNET,
26
+ NEAR_TESTNET,
27
+ )
28
+ ```
29
+ """
30
+
31
+ from t402.schemes.near.exact_direct import (
32
+ ExactDirectNearClientScheme,
33
+ ExactDirectNearServerScheme,
34
+ ExactDirectNearFacilitatorScheme,
35
+ )
36
+ from t402.schemes.near.exact_direct.client import ExactDirectNearClientConfig
37
+ from t402.schemes.near.exact_direct.server import ExactDirectNearServerConfig
38
+ from t402.schemes.near.exact_direct.facilitator import ExactDirectNearFacilitatorConfig
39
+ from t402.schemes.near.types import (
40
+ ClientNearSigner,
41
+ FacilitatorNearSigner,
42
+ ExactDirectPayload,
43
+ FtTransferArgs,
44
+ is_valid_account_id,
45
+ )
46
+ from t402.schemes.near.constants import (
47
+ SCHEME_EXACT_DIRECT,
48
+ NEAR_MAINNET,
49
+ NEAR_TESTNET,
50
+ NEAR_MAINNET_RPC,
51
+ NEAR_TESTNET_RPC,
52
+ CAIP_FAMILY,
53
+ DEFAULT_GAS,
54
+ DEFAULT_GAS_INT,
55
+ STORAGE_DEPOSIT,
56
+ FUNCTION_FT_TRANSFER,
57
+ USDT_MAINNET,
58
+ USDT_TESTNET,
59
+ USDC_MAINNET,
60
+ USDC_TESTNET,
61
+ TokenInfo,
62
+ NetworkConfig,
63
+ get_network_config,
64
+ get_token_info,
65
+ get_token_by_contract,
66
+ is_valid_network,
67
+ get_supported_networks,
68
+ )
69
+
70
+ __all__ = [
71
+ # Scheme implementations
72
+ "ExactDirectNearClientScheme",
73
+ "ExactDirectNearServerScheme",
74
+ "ExactDirectNearFacilitatorScheme",
75
+ # Configurations
76
+ "ExactDirectNearClientConfig",
77
+ "ExactDirectNearServerConfig",
78
+ "ExactDirectNearFacilitatorConfig",
79
+ # Signer protocols
80
+ "ClientNearSigner",
81
+ "FacilitatorNearSigner",
82
+ # Payload types
83
+ "ExactDirectPayload",
84
+ "FtTransferArgs",
85
+ # Validation
86
+ "is_valid_account_id",
87
+ "is_valid_network",
88
+ # Constants
89
+ "SCHEME_EXACT_DIRECT",
90
+ "NEAR_MAINNET",
91
+ "NEAR_TESTNET",
92
+ "NEAR_MAINNET_RPC",
93
+ "NEAR_TESTNET_RPC",
94
+ "CAIP_FAMILY",
95
+ "DEFAULT_GAS",
96
+ "DEFAULT_GAS_INT",
97
+ "STORAGE_DEPOSIT",
98
+ "FUNCTION_FT_TRANSFER",
99
+ # Token definitions
100
+ "USDT_MAINNET",
101
+ "USDT_TESTNET",
102
+ "USDC_MAINNET",
103
+ "USDC_TESTNET",
104
+ # Data classes
105
+ "TokenInfo",
106
+ "NetworkConfig",
107
+ # Lookup functions
108
+ "get_network_config",
109
+ "get_token_info",
110
+ "get_token_by_contract",
111
+ "get_supported_networks",
112
+ ]