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,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}")
|
t402/schemes/evm/upto/types.py
CHANGED
|
@@ -98,7 +98,9 @@ class UptoEIP2612Payload(BaseModel):
|
|
|
98
98
|
class UptoCompactPayload(BaseModel):
|
|
99
99
|
"""Alternative payload with combined signature."""
|
|
100
100
|
|
|
101
|
-
signature: str = Field(
|
|
101
|
+
signature: str = Field(
|
|
102
|
+
description="Combined EIP-2612 permit signature (65 bytes hex)"
|
|
103
|
+
)
|
|
102
104
|
authorization: PermitAuthorization = Field(description="Permit parameters")
|
|
103
105
|
payment_nonce: str = Field(
|
|
104
106
|
alias="paymentNonce",
|
t402/schemes/interfaces.py
CHANGED
|
@@ -25,9 +25,13 @@ from t402.types import (
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
# Type aliases for clarity
|
|
28
|
-
Price = Union[
|
|
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[
|
|
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
|
+
]
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"""NEAR blockchain constants for the T402 protocol.
|
|
2
|
+
|
|
3
|
+
This module contains network configurations, token contract addresses,
|
|
4
|
+
and other constants used by the NEAR exact-direct payment scheme.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Dict, Optional
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Scheme identifier
|
|
13
|
+
SCHEME_EXACT_DIRECT = "exact-direct"
|
|
14
|
+
|
|
15
|
+
# CAIP-2 network identifiers
|
|
16
|
+
NEAR_MAINNET = "near:mainnet"
|
|
17
|
+
NEAR_TESTNET = "near:testnet"
|
|
18
|
+
|
|
19
|
+
# RPC endpoints
|
|
20
|
+
NEAR_MAINNET_RPC = "https://rpc.mainnet.near.org"
|
|
21
|
+
NEAR_TESTNET_RPC = "https://rpc.testnet.near.org"
|
|
22
|
+
|
|
23
|
+
# Default gas for ft_transfer (30 TGas)
|
|
24
|
+
DEFAULT_GAS = "30000000000000"
|
|
25
|
+
DEFAULT_GAS_INT = 30_000_000_000_000
|
|
26
|
+
|
|
27
|
+
# Storage deposit required (1 yoctoNEAR) for ft_transfer
|
|
28
|
+
STORAGE_DEPOSIT = "1"
|
|
29
|
+
|
|
30
|
+
# NEP-141 function names
|
|
31
|
+
FUNCTION_FT_TRANSFER = "ft_transfer"
|
|
32
|
+
FUNCTION_FT_BALANCE_OF = "ft_balance_of"
|
|
33
|
+
FUNCTION_STORAGE_BALANCE = "storage_balance_of"
|
|
34
|
+
|
|
35
|
+
# CAIP family pattern
|
|
36
|
+
CAIP_FAMILY = "near:*"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class TokenInfo:
|
|
40
|
+
"""Contains information about a NEAR fungible token.
|
|
41
|
+
|
|
42
|
+
Attributes:
|
|
43
|
+
contract_id: The NEAR account ID of the token contract.
|
|
44
|
+
symbol: The token symbol (e.g., "USDT").
|
|
45
|
+
decimals: The number of decimal places for the token.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def __init__(self, contract_id: str, symbol: str, decimals: int) -> None:
|
|
49
|
+
self.contract_id = contract_id
|
|
50
|
+
self.symbol = symbol
|
|
51
|
+
self.decimals = decimals
|
|
52
|
+
|
|
53
|
+
def __repr__(self) -> str:
|
|
54
|
+
return f"TokenInfo(contract_id={self.contract_id!r}, symbol={self.symbol!r}, decimals={self.decimals})"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class NetworkConfig:
|
|
58
|
+
"""Network-specific configuration for NEAR.
|
|
59
|
+
|
|
60
|
+
Attributes:
|
|
61
|
+
network_id: The short network identifier (e.g., "mainnet").
|
|
62
|
+
rpc_url: The RPC endpoint URL.
|
|
63
|
+
default_token: The default token for this network.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
def __init__(self, network_id: str, rpc_url: str, default_token: TokenInfo) -> None:
|
|
67
|
+
self.network_id = network_id
|
|
68
|
+
self.rpc_url = rpc_url
|
|
69
|
+
self.default_token = default_token
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# Token definitions
|
|
73
|
+
USDT_MAINNET = TokenInfo(
|
|
74
|
+
contract_id="usdt.tether-token.near",
|
|
75
|
+
symbol="USDT",
|
|
76
|
+
decimals=6,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
USDT_TESTNET = TokenInfo(
|
|
80
|
+
contract_id="usdt.fakes.testnet",
|
|
81
|
+
symbol="USDT",
|
|
82
|
+
decimals=6,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
USDC_MAINNET = TokenInfo(
|
|
86
|
+
contract_id="17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1",
|
|
87
|
+
symbol="USDC",
|
|
88
|
+
decimals=6,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
USDC_TESTNET = TokenInfo(
|
|
92
|
+
contract_id="usdc.fakes.testnet",
|
|
93
|
+
symbol="USDC",
|
|
94
|
+
decimals=6,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# Network configurations
|
|
98
|
+
NETWORK_CONFIGS: Dict[str, NetworkConfig] = {
|
|
99
|
+
NEAR_MAINNET: NetworkConfig(
|
|
100
|
+
network_id="mainnet",
|
|
101
|
+
rpc_url=NEAR_MAINNET_RPC,
|
|
102
|
+
default_token=USDT_MAINNET,
|
|
103
|
+
),
|
|
104
|
+
NEAR_TESTNET: NetworkConfig(
|
|
105
|
+
network_id="testnet",
|
|
106
|
+
rpc_url=NEAR_TESTNET_RPC,
|
|
107
|
+
default_token=USDT_TESTNET,
|
|
108
|
+
),
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
# Token registry: network -> symbol -> TokenInfo
|
|
112
|
+
TOKEN_REGISTRY: Dict[str, Dict[str, TokenInfo]] = {
|
|
113
|
+
NEAR_MAINNET: {
|
|
114
|
+
"USDT": USDT_MAINNET,
|
|
115
|
+
"USDC": USDC_MAINNET,
|
|
116
|
+
},
|
|
117
|
+
NEAR_TESTNET: {
|
|
118
|
+
"USDT": USDT_TESTNET,
|
|
119
|
+
"USDC": USDC_TESTNET,
|
|
120
|
+
},
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def get_network_config(network: str) -> Optional[NetworkConfig]:
|
|
125
|
+
"""Get the configuration for a NEAR network.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
network: The CAIP-2 network identifier (e.g., "near:mainnet").
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
NetworkConfig if the network is supported, None otherwise.
|
|
132
|
+
"""
|
|
133
|
+
return NETWORK_CONFIGS.get(network)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def is_valid_network(network: str) -> bool:
|
|
137
|
+
"""Check if a network identifier is a supported NEAR network.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
network: The CAIP-2 network identifier.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
True if the network is supported.
|
|
144
|
+
"""
|
|
145
|
+
return network in NETWORK_CONFIGS
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def get_token_info(network: str, symbol: str) -> Optional[TokenInfo]:
|
|
149
|
+
"""Get token info for a network and symbol.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
network: The CAIP-2 network identifier.
|
|
153
|
+
symbol: The token symbol (e.g., "USDT").
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
TokenInfo if found, None otherwise.
|
|
157
|
+
"""
|
|
158
|
+
tokens = TOKEN_REGISTRY.get(network)
|
|
159
|
+
if tokens is None:
|
|
160
|
+
return None
|
|
161
|
+
return tokens.get(symbol)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def get_token_by_contract(network: str, contract_id: str) -> Optional[TokenInfo]:
|
|
165
|
+
"""Get token info by contract address.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
network: The CAIP-2 network identifier.
|
|
169
|
+
contract_id: The token contract account ID.
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
TokenInfo if found, None otherwise.
|
|
173
|
+
"""
|
|
174
|
+
tokens = TOKEN_REGISTRY.get(network)
|
|
175
|
+
if tokens is None:
|
|
176
|
+
return None
|
|
177
|
+
for token in tokens.values():
|
|
178
|
+
if token.contract_id == contract_id:
|
|
179
|
+
return token
|
|
180
|
+
return None
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def get_supported_networks() -> list:
|
|
184
|
+
"""Get a list of supported NEAR network identifiers.
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
List of CAIP-2 network identifier strings.
|
|
188
|
+
"""
|
|
189
|
+
return [NEAR_MAINNET, NEAR_TESTNET]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""NEAR Exact-Direct Payment Scheme.
|
|
2
|
+
|
|
3
|
+
This package provides the exact-direct payment scheme implementation for NEAR.
|
|
4
|
+
In this scheme, the client executes the NEP-141 ft_transfer on-chain directly,
|
|
5
|
+
and the transaction hash is used as proof of payment.
|
|
6
|
+
|
|
7
|
+
Components:
|
|
8
|
+
- ExactDirectNearClientScheme: Client-side (executes transfer, returns tx hash)
|
|
9
|
+
- ExactDirectNearServerScheme: Server-side (parses prices, enhances requirements)
|
|
10
|
+
- ExactDirectNearFacilitatorScheme: Facilitator-side (verifies tx, marks settled)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from t402.schemes.near.exact_direct.client import ExactDirectNearClientScheme
|
|
14
|
+
from t402.schemes.near.exact_direct.server import ExactDirectNearServerScheme
|
|
15
|
+
from t402.schemes.near.exact_direct.facilitator import ExactDirectNearFacilitatorScheme
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"ExactDirectNearClientScheme",
|
|
19
|
+
"ExactDirectNearServerScheme",
|
|
20
|
+
"ExactDirectNearFacilitatorScheme",
|
|
21
|
+
]
|