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,80 @@
1
+ """Up-To Payment Scheme.
2
+
3
+ The upto scheme authorizes transfer of up to a maximum amount,
4
+ enabling usage-based billing where the final settlement amount
5
+ is determined by actual usage.
6
+
7
+ This is useful for:
8
+ - AI inference billing (pay per token)
9
+ - Metered API access (pay per request)
10
+ - Streaming services (pay per second/minute)
11
+ - Data transfer (pay per byte)
12
+
13
+ Example:
14
+ ```python
15
+ from t402.schemes.upto import (
16
+ UptoPaymentRequirements,
17
+ UptoSettlement,
18
+ create_payment_requirements,
19
+ create_settlement,
20
+ )
21
+
22
+ # Server specifies requirements
23
+ requirements = create_payment_requirements(
24
+ network="eip155:8453",
25
+ max_amount="1000000", # $1.00 max
26
+ asset="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
27
+ pay_to="0x...",
28
+ )
29
+
30
+ # After service delivery, settle for actual usage
31
+ settlement = create_settlement(
32
+ settle_amount="150000", # $0.15 actual
33
+ units_consumed=1500,
34
+ unit_price="100",
35
+ unit_type="token",
36
+ )
37
+ ```
38
+ """
39
+
40
+ from t402.schemes.upto.types import (
41
+ # Constants
42
+ SCHEME_UPTO,
43
+ DEFAULT_MIN_AMOUNT,
44
+ DEFAULT_MAX_TIMEOUT_SECONDS,
45
+ SUPPORTED_UNITS,
46
+ # Models
47
+ UptoExtra,
48
+ UptoPaymentRequirements,
49
+ UptoUsageDetails,
50
+ UptoSettlement,
51
+ UptoSettlementResponse,
52
+ UptoValidationResult,
53
+ # Type guards
54
+ is_upto_payment_requirements,
55
+ is_valid_unit,
56
+ # Factory functions
57
+ create_payment_requirements,
58
+ create_settlement,
59
+ )
60
+
61
+ __all__ = [
62
+ # Constants
63
+ "SCHEME_UPTO",
64
+ "DEFAULT_MIN_AMOUNT",
65
+ "DEFAULT_MAX_TIMEOUT_SECONDS",
66
+ "SUPPORTED_UNITS",
67
+ # Models
68
+ "UptoExtra",
69
+ "UptoPaymentRequirements",
70
+ "UptoUsageDetails",
71
+ "UptoSettlement",
72
+ "UptoSettlementResponse",
73
+ "UptoValidationResult",
74
+ # Type guards
75
+ "is_upto_payment_requirements",
76
+ "is_valid_unit",
77
+ # Factory functions
78
+ "create_payment_requirements",
79
+ "create_settlement",
80
+ ]
@@ -0,0 +1,376 @@
1
+ """Up-To Scheme Core Types.
2
+
3
+ The upto scheme authorizes transfer of up to a maximum amount,
4
+ enabling usage-based billing where the final settlement amount
5
+ is determined by actual usage.
6
+
7
+ Example:
8
+ ```python
9
+ from t402.schemes.upto import (
10
+ UptoPaymentRequirements,
11
+ UptoSettlement,
12
+ UptoUsageDetails,
13
+ )
14
+
15
+ # Server specifies requirements
16
+ requirements = UptoPaymentRequirements(
17
+ scheme="upto",
18
+ network="eip155:8453",
19
+ max_amount="1000000", # $1.00 in USDC
20
+ min_amount="10000", # $0.01 minimum
21
+ asset="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
22
+ pay_to="0x...",
23
+ max_timeout_seconds=300,
24
+ extra=UptoExtra(unit="token", unit_price="100"),
25
+ )
26
+
27
+ # Server settles for actual usage
28
+ settlement = UptoSettlement(
29
+ settle_amount="150000", # $0.15
30
+ usage_details=UptoUsageDetails(
31
+ units_consumed=1500,
32
+ unit_price="100",
33
+ unit_type="token",
34
+ ),
35
+ )
36
+ ```
37
+ """
38
+
39
+ from __future__ import annotations
40
+
41
+ from typing import Any, Dict, List, Optional, Literal
42
+ from pydantic import BaseModel, ConfigDict, Field, field_validator
43
+ from pydantic.alias_generators import to_camel
44
+
45
+
46
+ # Constants
47
+ SCHEME_UPTO = "upto"
48
+ DEFAULT_MIN_AMOUNT = "1000"
49
+ DEFAULT_MAX_TIMEOUT_SECONDS = 300
50
+
51
+ # Supported billing units
52
+ SUPPORTED_UNITS: List[str] = [
53
+ "token",
54
+ "request",
55
+ "second",
56
+ "minute",
57
+ "byte",
58
+ "kb",
59
+ "mb",
60
+ ]
61
+
62
+
63
+ class UptoExtra(BaseModel):
64
+ """Extra fields specific to the upto scheme."""
65
+
66
+ unit: Optional[str] = Field(
67
+ default=None,
68
+ description="Billing unit (e.g., 'token', 'request', 'second', 'byte')",
69
+ )
70
+ unit_price: Optional[str] = Field(
71
+ default=None,
72
+ alias="unitPrice",
73
+ description="Price per unit in smallest denomination",
74
+ )
75
+ name: Optional[str] = Field(
76
+ default=None,
77
+ description="EIP-712 domain name (for EVM)",
78
+ )
79
+ version: Optional[str] = Field(
80
+ default=None,
81
+ description="EIP-712 domain version (for EVM)",
82
+ )
83
+ router_address: Optional[str] = Field(
84
+ default=None,
85
+ alias="routerAddress",
86
+ description="Router contract address (for EVM)",
87
+ )
88
+
89
+ model_config = ConfigDict(
90
+ alias_generator=to_camel,
91
+ populate_by_name=True,
92
+ from_attributes=True,
93
+ extra="allow", # Allow additional fields
94
+ )
95
+
96
+
97
+ class UptoPaymentRequirements(BaseModel):
98
+ """Extended payment requirements for the upto scheme.
99
+
100
+ Uses maxAmount instead of amount to indicate a maximum authorization.
101
+ """
102
+
103
+ scheme: Literal["upto"] = Field(default="upto")
104
+ network: str = Field(description="Network identifier (CAIP-2 format)")
105
+ max_amount: str = Field(
106
+ alias="maxAmount",
107
+ description="Maximum amount the client authorizes (in smallest denomination)",
108
+ )
109
+ min_amount: Optional[str] = Field(
110
+ default=None,
111
+ alias="minAmount",
112
+ description="Minimum settlement amount (prevents dust payments)",
113
+ )
114
+ asset: str = Field(description="Asset contract address or identifier")
115
+ pay_to: str = Field(alias="payTo", description="Recipient address")
116
+ max_timeout_seconds: int = Field(
117
+ alias="maxTimeoutSeconds",
118
+ description="Maximum time in seconds before payment expires",
119
+ )
120
+ extra: UptoExtra = Field(default_factory=UptoExtra)
121
+
122
+ model_config = ConfigDict(
123
+ alias_generator=to_camel,
124
+ populate_by_name=True,
125
+ from_attributes=True,
126
+ )
127
+
128
+ @field_validator("max_amount", "min_amount")
129
+ @classmethod
130
+ def validate_amount(cls, v: Optional[str]) -> Optional[str]:
131
+ if v is not None:
132
+ try:
133
+ int(v)
134
+ except ValueError:
135
+ raise ValueError("Amount must be an integer encoded as a string")
136
+ return v
137
+
138
+
139
+ class UptoUsageDetails(BaseModel):
140
+ """Usage details for settlement auditing."""
141
+
142
+ units_consumed: Optional[int] = Field(
143
+ default=None,
144
+ alias="unitsConsumed",
145
+ description="Number of units consumed",
146
+ )
147
+ unit_price: Optional[str] = Field(
148
+ default=None,
149
+ alias="unitPrice",
150
+ description="Price per unit used",
151
+ )
152
+ unit_type: Optional[str] = Field(
153
+ default=None,
154
+ alias="unitType",
155
+ description="Type of unit",
156
+ )
157
+ start_time: Optional[int] = Field(
158
+ default=None,
159
+ alias="startTime",
160
+ description="Start timestamp of usage period",
161
+ )
162
+ end_time: Optional[int] = Field(
163
+ default=None,
164
+ alias="endTime",
165
+ description="End timestamp of usage period",
166
+ )
167
+ metadata: Optional[Dict[str, Any]] = Field(
168
+ default=None,
169
+ description="Additional metadata",
170
+ )
171
+
172
+ model_config = ConfigDict(
173
+ alias_generator=to_camel,
174
+ populate_by_name=True,
175
+ from_attributes=True,
176
+ )
177
+
178
+
179
+ class UptoSettlement(BaseModel):
180
+ """Settlement request for the upto scheme."""
181
+
182
+ settle_amount: str = Field(
183
+ alias="settleAmount",
184
+ description="Actual amount to settle (must be <= maxAmount)",
185
+ )
186
+ usage_details: Optional[UptoUsageDetails] = Field(
187
+ default=None,
188
+ alias="usageDetails",
189
+ description="Optional usage details for auditing",
190
+ )
191
+
192
+ model_config = ConfigDict(
193
+ alias_generator=to_camel,
194
+ populate_by_name=True,
195
+ from_attributes=True,
196
+ )
197
+
198
+ @field_validator("settle_amount")
199
+ @classmethod
200
+ def validate_settle_amount(cls, v: str) -> str:
201
+ try:
202
+ int(v)
203
+ except ValueError:
204
+ raise ValueError("settle_amount must be an integer encoded as a string")
205
+ return v
206
+
207
+
208
+ class UptoSettlementResponse(BaseModel):
209
+ """Settlement response for the upto scheme."""
210
+
211
+ success: bool = Field(description="Whether settlement was successful")
212
+ transaction_hash: Optional[str] = Field(
213
+ default=None,
214
+ alias="transactionHash",
215
+ description="Transaction hash (if on-chain)",
216
+ )
217
+ settled_amount: str = Field(
218
+ alias="settledAmount",
219
+ description="Actual amount settled",
220
+ )
221
+ max_amount: str = Field(
222
+ alias="maxAmount",
223
+ description="Maximum amount that was authorized",
224
+ )
225
+ block_number: Optional[int] = Field(
226
+ default=None,
227
+ alias="blockNumber",
228
+ description="Block number (if on-chain)",
229
+ )
230
+ gas_used: Optional[str] = Field(
231
+ default=None,
232
+ alias="gasUsed",
233
+ description="Gas used (if on-chain)",
234
+ )
235
+ error: Optional[str] = Field(
236
+ default=None,
237
+ description="Error message if failed",
238
+ )
239
+
240
+ model_config = ConfigDict(
241
+ alias_generator=to_camel,
242
+ populate_by_name=True,
243
+ from_attributes=True,
244
+ )
245
+
246
+
247
+ class UptoValidationResult(BaseModel):
248
+ """Validation result for upto payment."""
249
+
250
+ is_valid: bool = Field(alias="isValid", description="Whether the payment is valid")
251
+ invalid_reason: Optional[str] = Field(
252
+ default=None,
253
+ alias="invalidReason",
254
+ description="Reason if invalid",
255
+ )
256
+ validated_max_amount: Optional[str] = Field(
257
+ default=None,
258
+ alias="validatedMaxAmount",
259
+ description="Validated maximum amount",
260
+ )
261
+ payer: Optional[str] = Field(
262
+ default=None,
263
+ description="Payer address",
264
+ )
265
+ expires_at: Optional[int] = Field(
266
+ default=None,
267
+ alias="expiresAt",
268
+ description="Expiration timestamp",
269
+ )
270
+
271
+ model_config = ConfigDict(
272
+ alias_generator=to_camel,
273
+ populate_by_name=True,
274
+ from_attributes=True,
275
+ )
276
+
277
+
278
+ def is_upto_payment_requirements(data: Dict[str, Any]) -> bool:
279
+ """Check if the given data represents upto payment requirements.
280
+
281
+ Args:
282
+ data: Dictionary to check
283
+
284
+ Returns:
285
+ True if data has scheme="upto" and maxAmount field
286
+ """
287
+ scheme = data.get("scheme")
288
+ has_max_amount = "maxAmount" in data or "max_amount" in data
289
+ return scheme == SCHEME_UPTO and has_max_amount
290
+
291
+
292
+ def is_valid_unit(unit: str) -> bool:
293
+ """Check if the given unit is a supported billing unit.
294
+
295
+ Args:
296
+ unit: Unit string to check
297
+
298
+ Returns:
299
+ True if unit is in SUPPORTED_UNITS
300
+ """
301
+ return unit in SUPPORTED_UNITS
302
+
303
+
304
+ def create_payment_requirements(
305
+ network: str,
306
+ max_amount: str,
307
+ asset: str,
308
+ pay_to: str,
309
+ min_amount: Optional[str] = None,
310
+ max_timeout_seconds: int = DEFAULT_MAX_TIMEOUT_SECONDS,
311
+ extra: Optional[UptoExtra] = None,
312
+ ) -> UptoPaymentRequirements:
313
+ """Create a new UptoPaymentRequirements with default values.
314
+
315
+ Args:
316
+ network: Network identifier (CAIP-2 format)
317
+ max_amount: Maximum authorization amount
318
+ asset: Asset contract address
319
+ pay_to: Recipient address
320
+ min_amount: Minimum settlement amount (defaults to DEFAULT_MIN_AMOUNT)
321
+ max_timeout_seconds: Timeout in seconds
322
+ extra: Additional scheme-specific data
323
+
324
+ Returns:
325
+ UptoPaymentRequirements instance
326
+ """
327
+ return UptoPaymentRequirements(
328
+ scheme="upto",
329
+ network=network,
330
+ max_amount=max_amount,
331
+ min_amount=min_amount or DEFAULT_MIN_AMOUNT,
332
+ asset=asset,
333
+ pay_to=pay_to,
334
+ max_timeout_seconds=max_timeout_seconds,
335
+ extra=extra or UptoExtra(),
336
+ )
337
+
338
+
339
+ def create_settlement(
340
+ settle_amount: str,
341
+ units_consumed: Optional[int] = None,
342
+ unit_price: Optional[str] = None,
343
+ unit_type: Optional[str] = None,
344
+ start_time: Optional[int] = None,
345
+ end_time: Optional[int] = None,
346
+ metadata: Optional[Dict[str, Any]] = None,
347
+ ) -> UptoSettlement:
348
+ """Create a settlement with optional usage details.
349
+
350
+ Args:
351
+ settle_amount: Amount to settle
352
+ units_consumed: Number of units consumed
353
+ unit_price: Price per unit
354
+ unit_type: Type of unit
355
+ start_time: Start timestamp
356
+ end_time: End timestamp
357
+ metadata: Additional metadata
358
+
359
+ Returns:
360
+ UptoSettlement instance
361
+ """
362
+ usage_details = None
363
+ if any([units_consumed, unit_price, unit_type, start_time, end_time, metadata]):
364
+ usage_details = UptoUsageDetails(
365
+ units_consumed=units_consumed,
366
+ unit_price=unit_price,
367
+ unit_type=unit_type,
368
+ start_time=start_time,
369
+ end_time=end_time,
370
+ metadata=metadata,
371
+ )
372
+
373
+ return UptoSettlement(
374
+ settle_amount=settle_amount,
375
+ usage_details=usage_details,
376
+ )