t402 1.9.1__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 +1 -1
- t402/a2a/__init__.py +73 -0
- t402/a2a/helpers.py +158 -0
- t402/a2a/types.py +145 -0
- t402/bridge/constants.py +1 -1
- t402/django/__init__.py +42 -0
- t402/django/middleware.py +596 -0
- t402/errors.py +213 -0
- t402/facilitator.py +125 -0
- t402/mcp/constants.py +3 -6
- t402/mcp/server.py +428 -44
- 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/schemes/__init__.py +19 -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 +1 -1
- t402/schemes/evm/exact_legacy/server.py +1 -1
- t402/schemes/near/__init__.py +25 -0
- t402/schemes/near/upto/__init__.py +54 -0
- t402/schemes/near/upto/types.py +272 -0
- t402/schemes/svm/__init__.py +15 -0
- t402/schemes/svm/upto/__init__.py +23 -0
- t402/schemes/svm/upto/types.py +193 -0
- t402/schemes/ton/__init__.py +15 -0
- t402/schemes/ton/upto/__init__.py +31 -0
- t402/schemes/ton/upto/types.py +215 -0
- t402/schemes/tron/__init__.py +21 -4
- t402/schemes/tron/upto/__init__.py +30 -0
- t402/schemes/tron/upto/types.py +213 -0
- t402/starlette/__init__.py +38 -0
- t402/starlette/middleware.py +522 -0
- t402/ton.py +1 -1
- t402/ton_paywall_template.py +1 -1
- t402/types.py +100 -2
- t402/wdk/chains.py +1 -1
- {t402-1.9.1.dist-info → t402-1.10.0.dist-info}/METADATA +3 -3
- {t402-1.9.1.dist-info → t402-1.10.0.dist-info}/RECORD +51 -20
- {t402-1.9.1.dist-info → t402-1.10.0.dist-info}/WHEEL +0 -0
- {t402-1.9.1.dist-info → t402-1.10.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
"""NEAR Up-To Scheme Types.
|
|
2
|
+
|
|
3
|
+
NEAR-specific types for the up-to payment scheme using an escrow pattern.
|
|
4
|
+
Since NEAR NEP-141 tokens don't have native approve/transferFrom, the client
|
|
5
|
+
ft_transfers maxAmount to the facilitator, who then forwards settleAmount
|
|
6
|
+
to the payTo address and refunds the rest.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import Any, Dict, Optional
|
|
12
|
+
|
|
13
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
14
|
+
from pydantic.alias_generators import to_camel
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class UptoNearAuthorization(BaseModel):
|
|
18
|
+
"""Authorization metadata for NEAR upto payments.
|
|
19
|
+
|
|
20
|
+
Contains the transfer details needed for verification.
|
|
21
|
+
|
|
22
|
+
Attributes:
|
|
23
|
+
from_account: Sender's NEAR account ID.
|
|
24
|
+
facilitator: Facilitator's NEAR account ID that receives the transfer.
|
|
25
|
+
token_contract: NEP-141 token contract ID.
|
|
26
|
+
max_amount: Maximum authorized amount in smallest units (as string).
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from_account: str = Field(alias="from", description="Sender's NEAR account ID")
|
|
30
|
+
facilitator: str = Field(description="Facilitator's NEAR account ID")
|
|
31
|
+
token_contract: str = Field(
|
|
32
|
+
alias="tokenContract",
|
|
33
|
+
description="NEP-141 token contract ID",
|
|
34
|
+
)
|
|
35
|
+
max_amount: str = Field(
|
|
36
|
+
alias="maxAmount",
|
|
37
|
+
description="Maximum authorized amount in smallest units",
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
model_config = ConfigDict(
|
|
41
|
+
alias_generator=to_camel,
|
|
42
|
+
populate_by_name=True,
|
|
43
|
+
from_attributes=True,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class UptoNearPayload(BaseModel):
|
|
48
|
+
"""NEAR upto payment payload.
|
|
49
|
+
|
|
50
|
+
Contains the transaction hash of the client's transfer to the facilitator,
|
|
51
|
+
along with authorization metadata for verification.
|
|
52
|
+
|
|
53
|
+
Attributes:
|
|
54
|
+
tx_hash: NEAR transaction hash of the transfer to facilitator.
|
|
55
|
+
authorization: Authorization metadata for verification.
|
|
56
|
+
payment_nonce: Unique nonce for replay protection (hex string).
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
tx_hash: str = Field(
|
|
60
|
+
alias="txHash",
|
|
61
|
+
description="NEAR transaction hash of the transfer to facilitator",
|
|
62
|
+
)
|
|
63
|
+
authorization: UptoNearAuthorization = Field(
|
|
64
|
+
description="Authorization metadata for verification",
|
|
65
|
+
)
|
|
66
|
+
payment_nonce: str = Field(
|
|
67
|
+
alias="paymentNonce",
|
|
68
|
+
description="Unique nonce for replay protection (hex string)",
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
model_config = ConfigDict(
|
|
72
|
+
alias_generator=to_camel,
|
|
73
|
+
populate_by_name=True,
|
|
74
|
+
from_attributes=True,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
78
|
+
"""Convert to dictionary for JSON serialization.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Dict with camelCase keys matching the Go/TypeScript field names.
|
|
82
|
+
"""
|
|
83
|
+
return {
|
|
84
|
+
"txHash": self.tx_hash,
|
|
85
|
+
"authorization": {
|
|
86
|
+
"from": self.authorization.from_account,
|
|
87
|
+
"facilitator": self.authorization.facilitator,
|
|
88
|
+
"tokenContract": self.authorization.token_contract,
|
|
89
|
+
"maxAmount": self.authorization.max_amount,
|
|
90
|
+
},
|
|
91
|
+
"paymentNonce": self.payment_nonce,
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class UptoNearExtra(BaseModel):
|
|
96
|
+
"""NEAR-specific extra fields for the upto scheme.
|
|
97
|
+
|
|
98
|
+
Included in the PaymentRequirements.extra field to provide
|
|
99
|
+
additional parameters needed for the upto payment flow.
|
|
100
|
+
|
|
101
|
+
Attributes:
|
|
102
|
+
facilitator: Facilitator account ID that will receive the initial transfer.
|
|
103
|
+
max_amount: Maximum payment amount authorized.
|
|
104
|
+
min_amount: Minimum acceptable settlement amount.
|
|
105
|
+
unit: Billing unit (e.g., "token", "request", "second").
|
|
106
|
+
unit_price: Price per unit in smallest denomination.
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
facilitator: Optional[str] = Field(
|
|
110
|
+
default=None,
|
|
111
|
+
description="Facilitator account ID that will receive the initial transfer",
|
|
112
|
+
)
|
|
113
|
+
max_amount: Optional[str] = Field(
|
|
114
|
+
default=None,
|
|
115
|
+
alias="maxAmount",
|
|
116
|
+
description="Maximum payment amount authorized",
|
|
117
|
+
)
|
|
118
|
+
min_amount: Optional[str] = Field(
|
|
119
|
+
default=None,
|
|
120
|
+
alias="minAmount",
|
|
121
|
+
description="Minimum acceptable settlement amount",
|
|
122
|
+
)
|
|
123
|
+
unit: Optional[str] = Field(
|
|
124
|
+
default=None,
|
|
125
|
+
description="Billing unit (e.g., 'token', 'request', 'second')",
|
|
126
|
+
)
|
|
127
|
+
unit_price: Optional[str] = Field(
|
|
128
|
+
default=None,
|
|
129
|
+
alias="unitPrice",
|
|
130
|
+
description="Price per unit in smallest denomination",
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
model_config = ConfigDict(
|
|
134
|
+
alias_generator=to_camel,
|
|
135
|
+
populate_by_name=True,
|
|
136
|
+
from_attributes=True,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class UptoNearUsageDetails(BaseModel):
|
|
141
|
+
"""Usage details for NEAR upto settlement.
|
|
142
|
+
|
|
143
|
+
Attributes:
|
|
144
|
+
units_consumed: Number of units consumed.
|
|
145
|
+
unit_price: Price per unit used.
|
|
146
|
+
unit_type: Type of unit.
|
|
147
|
+
start_time: Start timestamp.
|
|
148
|
+
end_time: End timestamp.
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
units_consumed: Optional[int] = Field(
|
|
152
|
+
default=None,
|
|
153
|
+
alias="unitsConsumed",
|
|
154
|
+
description="Number of units consumed",
|
|
155
|
+
)
|
|
156
|
+
unit_price: Optional[str] = Field(
|
|
157
|
+
default=None,
|
|
158
|
+
alias="unitPrice",
|
|
159
|
+
description="Price per unit used",
|
|
160
|
+
)
|
|
161
|
+
unit_type: Optional[str] = Field(
|
|
162
|
+
default=None,
|
|
163
|
+
alias="unitType",
|
|
164
|
+
description="Type of unit",
|
|
165
|
+
)
|
|
166
|
+
start_time: Optional[int] = Field(
|
|
167
|
+
default=None,
|
|
168
|
+
alias="startTime",
|
|
169
|
+
description="Start timestamp",
|
|
170
|
+
)
|
|
171
|
+
end_time: Optional[int] = Field(
|
|
172
|
+
default=None,
|
|
173
|
+
alias="endTime",
|
|
174
|
+
description="End timestamp",
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
model_config = ConfigDict(
|
|
178
|
+
alias_generator=to_camel,
|
|
179
|
+
populate_by_name=True,
|
|
180
|
+
from_attributes=True,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class UptoNearSettlement(BaseModel):
|
|
185
|
+
"""NEAR-specific settlement request for the upto scheme.
|
|
186
|
+
|
|
187
|
+
Attributes:
|
|
188
|
+
settle_amount: Actual amount to settle (must be <= maxAmount).
|
|
189
|
+
usage_details: Optional usage information for auditing.
|
|
190
|
+
"""
|
|
191
|
+
|
|
192
|
+
settle_amount: str = Field(
|
|
193
|
+
alias="settleAmount",
|
|
194
|
+
description="Actual amount to settle",
|
|
195
|
+
)
|
|
196
|
+
usage_details: Optional[UptoNearUsageDetails] = Field(
|
|
197
|
+
default=None,
|
|
198
|
+
alias="usageDetails",
|
|
199
|
+
description="Optional usage information",
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
model_config = ConfigDict(
|
|
203
|
+
alias_generator=to_camel,
|
|
204
|
+
populate_by_name=True,
|
|
205
|
+
from_attributes=True,
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def is_upto_near_payload(data: Dict[str, Any]) -> bool:
|
|
210
|
+
"""Check if the given data represents a NEAR upto payload.
|
|
211
|
+
|
|
212
|
+
Distinguishes upto payloads from exact-direct payloads by checking
|
|
213
|
+
for the authorization structure with facilitator field.
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
data: Dictionary to check.
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
True if data has the correct NEAR upto payload structure.
|
|
220
|
+
"""
|
|
221
|
+
if not isinstance(data, dict):
|
|
222
|
+
return False
|
|
223
|
+
|
|
224
|
+
tx_hash = data.get("txHash")
|
|
225
|
+
payment_nonce = data.get("paymentNonce")
|
|
226
|
+
auth = data.get("authorization")
|
|
227
|
+
|
|
228
|
+
if not tx_hash or not isinstance(tx_hash, str):
|
|
229
|
+
return False
|
|
230
|
+
if not payment_nonce or not isinstance(payment_nonce, str):
|
|
231
|
+
return False
|
|
232
|
+
if not auth or not isinstance(auth, dict):
|
|
233
|
+
return False
|
|
234
|
+
|
|
235
|
+
# Check authorization structure
|
|
236
|
+
required_auth_fields = ["from", "facilitator", "tokenContract", "maxAmount"]
|
|
237
|
+
if not all(k in auth for k in required_auth_fields):
|
|
238
|
+
return False
|
|
239
|
+
|
|
240
|
+
# Ensure from and facilitator are non-empty strings
|
|
241
|
+
if not auth.get("from") or not isinstance(auth["from"], str):
|
|
242
|
+
return False
|
|
243
|
+
if not auth.get("facilitator") or not isinstance(auth["facilitator"], str):
|
|
244
|
+
return False
|
|
245
|
+
|
|
246
|
+
return True
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def upto_payload_from_dict(data: Dict[str, Any]) -> UptoNearPayload:
|
|
250
|
+
"""Create an UptoNearPayload from a dictionary.
|
|
251
|
+
|
|
252
|
+
Args:
|
|
253
|
+
data: Dictionary containing payload data with camelCase keys.
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
UptoNearPayload instance.
|
|
257
|
+
|
|
258
|
+
Raises:
|
|
259
|
+
ValueError: If required fields are missing or invalid.
|
|
260
|
+
"""
|
|
261
|
+
auth_data = data.get("authorization", {})
|
|
262
|
+
|
|
263
|
+
return UptoNearPayload(
|
|
264
|
+
tx_hash=data.get("txHash", ""),
|
|
265
|
+
authorization=UptoNearAuthorization(
|
|
266
|
+
from_account=auth_data.get("from", ""),
|
|
267
|
+
facilitator=auth_data.get("facilitator", ""),
|
|
268
|
+
token_contract=auth_data.get("tokenContract", ""),
|
|
269
|
+
max_amount=auth_data.get("maxAmount", ""),
|
|
270
|
+
),
|
|
271
|
+
payment_nonce=data.get("paymentNonce", ""),
|
|
272
|
+
)
|
t402/schemes/svm/__init__.py
CHANGED
|
@@ -4,6 +4,7 @@ This package provides payment scheme implementations for Solana blockchain.
|
|
|
4
4
|
|
|
5
5
|
Supported schemes:
|
|
6
6
|
- exact: SPL Token TransferChecked with facilitator fee payer
|
|
7
|
+
- upto: SPL Token ApproveChecked with delegated transferFrom
|
|
7
8
|
"""
|
|
8
9
|
|
|
9
10
|
from t402.schemes.svm.exact import (
|
|
@@ -15,6 +16,14 @@ from t402.schemes.svm.exact import (
|
|
|
15
16
|
SCHEME_EXACT,
|
|
16
17
|
)
|
|
17
18
|
|
|
19
|
+
from t402.schemes.svm.upto import (
|
|
20
|
+
UptoSvmAuthorization,
|
|
21
|
+
UptoSvmPayload,
|
|
22
|
+
UptoSvmExtra,
|
|
23
|
+
is_upto_svm_payload,
|
|
24
|
+
upto_payload_from_dict,
|
|
25
|
+
)
|
|
26
|
+
|
|
18
27
|
__all__ = [
|
|
19
28
|
# Client
|
|
20
29
|
"ExactSvmClientScheme",
|
|
@@ -26,4 +35,10 @@ __all__ = [
|
|
|
26
35
|
"FacilitatorSvmSigner",
|
|
27
36
|
# Constants
|
|
28
37
|
"SCHEME_EXACT",
|
|
38
|
+
# Upto types
|
|
39
|
+
"UptoSvmAuthorization",
|
|
40
|
+
"UptoSvmPayload",
|
|
41
|
+
"UptoSvmExtra",
|
|
42
|
+
"is_upto_svm_payload",
|
|
43
|
+
"upto_payload_from_dict",
|
|
29
44
|
]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Solana SVM Up-To Payment Scheme Types.
|
|
2
|
+
|
|
3
|
+
This package provides types for the upto payment scheme on Solana
|
|
4
|
+
using SPL ApproveChecked. The client approves the facilitator (delegate)
|
|
5
|
+
to transfer up to maxAmount of tokens from the client's associated
|
|
6
|
+
token account.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from t402.schemes.svm.upto.types import (
|
|
10
|
+
UptoSvmAuthorization,
|
|
11
|
+
UptoSvmPayload,
|
|
12
|
+
UptoSvmExtra,
|
|
13
|
+
upto_payload_from_dict,
|
|
14
|
+
is_upto_svm_payload,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"UptoSvmAuthorization",
|
|
19
|
+
"UptoSvmPayload",
|
|
20
|
+
"UptoSvmExtra",
|
|
21
|
+
"upto_payload_from_dict",
|
|
22
|
+
"is_upto_svm_payload",
|
|
23
|
+
]
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"""Solana SVM Up-To Scheme Types.
|
|
2
|
+
|
|
3
|
+
SVM-specific types for the up-to payment scheme using SPL ApproveChecked.
|
|
4
|
+
The client signs an approve transaction that authorizes the facilitator
|
|
5
|
+
to transfer up to maxAmount tokens from the client's ATA.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any, Dict, Optional
|
|
11
|
+
|
|
12
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
13
|
+
from pydantic.alias_generators import to_camel
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class UptoSvmAuthorization(BaseModel):
|
|
17
|
+
"""SPL ApproveChecked authorization metadata.
|
|
18
|
+
|
|
19
|
+
Contains the details of the delegate approval including the owner,
|
|
20
|
+
delegate (facilitator), token mint, maximum amount, and source ATA.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
owner: str = Field(description="Token owner address (base58)")
|
|
24
|
+
delegate: str = Field(description="Approved delegate address - facilitator (base58)")
|
|
25
|
+
mint: str = Field(description="SPL token mint address (base58)")
|
|
26
|
+
max_amount: str = Field(
|
|
27
|
+
alias="maxAmount",
|
|
28
|
+
description="Maximum approved amount in smallest units (as string)",
|
|
29
|
+
)
|
|
30
|
+
source_ata: str = Field(
|
|
31
|
+
alias="sourceATA",
|
|
32
|
+
description="Owner's associated token account (base58)",
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
model_config = ConfigDict(
|
|
36
|
+
alias_generator=to_camel,
|
|
37
|
+
populate_by_name=True,
|
|
38
|
+
from_attributes=True,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
42
|
+
"""Convert to dictionary for JSON serialization."""
|
|
43
|
+
return {
|
|
44
|
+
"owner": self.owner,
|
|
45
|
+
"delegate": self.delegate,
|
|
46
|
+
"mint": self.mint,
|
|
47
|
+
"maxAmount": self.max_amount,
|
|
48
|
+
"sourceATA": self.source_ata,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class UptoSvmPayload(BaseModel):
|
|
53
|
+
"""Up-to SVM payment payload containing a signed approve transaction.
|
|
54
|
+
|
|
55
|
+
The facilitator uses the delegated authority to transfer tokens
|
|
56
|
+
up to the approved maxAmount.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
transaction: str = Field(description="Base64 encoded signed approve transaction")
|
|
60
|
+
authorization: UptoSvmAuthorization = Field(
|
|
61
|
+
description="Approval authorization metadata"
|
|
62
|
+
)
|
|
63
|
+
payment_nonce: str = Field(
|
|
64
|
+
alias="paymentNonce",
|
|
65
|
+
description="Unique nonce for replay protection (hex string)",
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
model_config = ConfigDict(
|
|
69
|
+
alias_generator=to_camel,
|
|
70
|
+
populate_by_name=True,
|
|
71
|
+
from_attributes=True,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
75
|
+
"""Convert to dictionary for JSON serialization."""
|
|
76
|
+
return {
|
|
77
|
+
"transaction": self.transaction,
|
|
78
|
+
"authorization": self.authorization.to_dict(),
|
|
79
|
+
"paymentNonce": self.payment_nonce,
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class UptoSvmExtra(BaseModel):
|
|
84
|
+
"""SVM-specific extra fields for the upto scheme.
|
|
85
|
+
|
|
86
|
+
Included in the PaymentRequirements.extra field to provide
|
|
87
|
+
billing configuration for upto payments.
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
fee_payer: Optional[str] = Field(
|
|
91
|
+
default=None,
|
|
92
|
+
alias="feePayer",
|
|
93
|
+
description="Facilitator address that will pay transaction fees (base58)",
|
|
94
|
+
)
|
|
95
|
+
max_amount: Optional[str] = Field(
|
|
96
|
+
default=None,
|
|
97
|
+
alias="maxAmount",
|
|
98
|
+
description="Maximum payment amount authorized",
|
|
99
|
+
)
|
|
100
|
+
min_amount: Optional[str] = Field(
|
|
101
|
+
default=None,
|
|
102
|
+
alias="minAmount",
|
|
103
|
+
description="Minimum acceptable settlement amount",
|
|
104
|
+
)
|
|
105
|
+
unit: Optional[str] = Field(
|
|
106
|
+
default=None,
|
|
107
|
+
description="Billing unit (e.g., 'token', 'request', 'second')",
|
|
108
|
+
)
|
|
109
|
+
unit_price: Optional[str] = Field(
|
|
110
|
+
default=None,
|
|
111
|
+
alias="unitPrice",
|
|
112
|
+
description="Price per unit in smallest denomination",
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
model_config = ConfigDict(
|
|
116
|
+
alias_generator=to_camel,
|
|
117
|
+
populate_by_name=True,
|
|
118
|
+
from_attributes=True,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def upto_payload_from_dict(data: Dict[str, Any]) -> UptoSvmPayload:
|
|
123
|
+
"""Create an UptoSvmPayload from a dictionary.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
data: Dictionary containing payload data with keys:
|
|
127
|
+
- transaction: base64 encoded transaction
|
|
128
|
+
- authorization: dict with owner, delegate, mint, maxAmount, sourceATA
|
|
129
|
+
- paymentNonce: hex string nonce
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
UptoSvmPayload instance
|
|
133
|
+
|
|
134
|
+
Raises:
|
|
135
|
+
ValueError: If required fields are missing
|
|
136
|
+
"""
|
|
137
|
+
auth_data = data.get("authorization", {})
|
|
138
|
+
if not isinstance(auth_data, dict):
|
|
139
|
+
raise ValueError("authorization must be a dictionary")
|
|
140
|
+
|
|
141
|
+
transaction = data.get("transaction")
|
|
142
|
+
if not isinstance(transaction, str) or not transaction:
|
|
143
|
+
raise ValueError("missing or invalid transaction field")
|
|
144
|
+
|
|
145
|
+
payment_nonce = data.get("paymentNonce")
|
|
146
|
+
if not isinstance(payment_nonce, str) or not payment_nonce:
|
|
147
|
+
raise ValueError("missing or invalid paymentNonce field")
|
|
148
|
+
|
|
149
|
+
owner = auth_data.get("owner")
|
|
150
|
+
if not isinstance(owner, str) or not owner:
|
|
151
|
+
raise ValueError("missing or invalid authorization.owner field")
|
|
152
|
+
|
|
153
|
+
delegate = auth_data.get("delegate")
|
|
154
|
+
if not isinstance(delegate, str) or not delegate:
|
|
155
|
+
raise ValueError("missing or invalid authorization.delegate field")
|
|
156
|
+
|
|
157
|
+
return UptoSvmPayload(
|
|
158
|
+
transaction=transaction,
|
|
159
|
+
authorization=UptoSvmAuthorization(
|
|
160
|
+
owner=owner,
|
|
161
|
+
delegate=delegate,
|
|
162
|
+
mint=auth_data.get("mint", ""),
|
|
163
|
+
max_amount=auth_data.get("maxAmount", ""),
|
|
164
|
+
source_ata=auth_data.get("sourceATA", ""),
|
|
165
|
+
),
|
|
166
|
+
payment_nonce=payment_nonce,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def is_upto_svm_payload(data: Any) -> bool:
|
|
171
|
+
"""Check if the given data represents an SVM upto payload.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
data: Data to check
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
True if data has the correct SVM upto payload structure
|
|
178
|
+
"""
|
|
179
|
+
if not isinstance(data, dict):
|
|
180
|
+
return False
|
|
181
|
+
|
|
182
|
+
if not isinstance(data.get("transaction"), str):
|
|
183
|
+
return False
|
|
184
|
+
|
|
185
|
+
if not isinstance(data.get("paymentNonce"), str):
|
|
186
|
+
return False
|
|
187
|
+
|
|
188
|
+
auth = data.get("authorization")
|
|
189
|
+
if not isinstance(auth, dict):
|
|
190
|
+
return False
|
|
191
|
+
|
|
192
|
+
required_fields = ["owner", "delegate", "mint", "maxAmount", "sourceATA"]
|
|
193
|
+
return all(isinstance(auth.get(f), str) for f in required_fields)
|
t402/schemes/ton/__init__.py
CHANGED
|
@@ -4,6 +4,7 @@ This package provides payment scheme implementations for TON blockchain.
|
|
|
4
4
|
|
|
5
5
|
Supported schemes:
|
|
6
6
|
- exact: Jetton TransferWithAuthorization
|
|
7
|
+
- upto: Escrow-based usage billing (maxAmount to facilitator, settle + refund)
|
|
7
8
|
"""
|
|
8
9
|
|
|
9
10
|
from t402.schemes.ton.exact import (
|
|
@@ -15,6 +16,14 @@ from t402.schemes.ton.exact import (
|
|
|
15
16
|
SCHEME_EXACT,
|
|
16
17
|
)
|
|
17
18
|
|
|
19
|
+
from t402.schemes.ton.upto import (
|
|
20
|
+
UptoTonAuthorization,
|
|
21
|
+
UptoTonPayload,
|
|
22
|
+
UptoTonExtra,
|
|
23
|
+
is_upto_ton_payload,
|
|
24
|
+
upto_payload_from_dict,
|
|
25
|
+
)
|
|
26
|
+
|
|
18
27
|
__all__ = [
|
|
19
28
|
# Client
|
|
20
29
|
"ExactTonClientScheme",
|
|
@@ -26,4 +35,10 @@ __all__ = [
|
|
|
26
35
|
"FacilitatorTonSigner",
|
|
27
36
|
# Constants
|
|
28
37
|
"SCHEME_EXACT",
|
|
38
|
+
# Upto types
|
|
39
|
+
"UptoTonAuthorization",
|
|
40
|
+
"UptoTonPayload",
|
|
41
|
+
"UptoTonExtra",
|
|
42
|
+
"is_upto_ton_payload",
|
|
43
|
+
"upto_payload_from_dict",
|
|
29
44
|
]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""TON Up-To Payment Scheme.
|
|
2
|
+
|
|
3
|
+
This package provides types for the upto payment scheme on TON blockchain.
|
|
4
|
+
|
|
5
|
+
The upto scheme uses an escrow pattern:
|
|
6
|
+
1. Client signs a transfer of maxAmount to the facilitator's holding address
|
|
7
|
+
2. Facilitator broadcasts the transfer, then sends settleAmount to payTo
|
|
8
|
+
and refunds (maxAmount - settleAmount) back to the client.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from t402.schemes.ton.upto.types import (
|
|
12
|
+
# Models
|
|
13
|
+
UptoTonAuthorization,
|
|
14
|
+
UptoTonPayload,
|
|
15
|
+
UptoTonExtra,
|
|
16
|
+
# Type guards
|
|
17
|
+
is_upto_ton_payload,
|
|
18
|
+
# Helper functions
|
|
19
|
+
upto_payload_from_dict,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
# Types
|
|
24
|
+
"UptoTonAuthorization",
|
|
25
|
+
"UptoTonPayload",
|
|
26
|
+
"UptoTonExtra",
|
|
27
|
+
# Type guards
|
|
28
|
+
"is_upto_ton_payload",
|
|
29
|
+
# Helper functions
|
|
30
|
+
"upto_payload_from_dict",
|
|
31
|
+
]
|