t402 1.5.3__tar.gz → 1.6.0__tar.gz
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-1.5.3/README.md → t402-1.6.0/PKG-INFO +59 -0
- t402-1.5.3/PKG-INFO → t402-1.6.0/README.md +34 -19
- {t402-1.5.3 → t402-1.6.0}/pyproject.toml +13 -1
- {t402-1.5.3 → t402-1.6.0}/src/t402/__init__.py +37 -0
- t402-1.6.0/src/t402/mcp/__init__.py +109 -0
- t402-1.6.0/src/t402/mcp/constants.py +213 -0
- t402-1.6.0/src/t402/mcp/server.py +527 -0
- t402-1.6.0/src/t402/mcp/tools.py +169 -0
- t402-1.6.0/src/t402/mcp/types.py +241 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/networks.py +47 -3
- t402-1.6.0/src/t402/svm.py +566 -0
- t402-1.6.0/tests/test_bridge.py +706 -0
- t402-1.6.0/tests/test_mcp.py +505 -0
- t402-1.6.0/tests/test_svm.py +493 -0
- {t402-1.5.3 → t402-1.6.0}/.gitignore +0 -0
- {t402-1.5.3 → t402-1.6.0}/.python-version +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/bridge/__init__.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/bridge/client.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/bridge/constants.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/bridge/router.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/bridge/scan.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/bridge/types.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/chains.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/cli.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/clients/__init__.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/clients/base.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/clients/httpx.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/clients/requests.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/common.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/encoding.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/erc4337/__init__.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/erc4337/accounts.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/erc4337/bundlers.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/erc4337/paymasters.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/erc4337/types.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/evm_paywall_template.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/exact.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/facilitator.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/fastapi/__init__.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/fastapi/middleware.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/flask/__init__.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/flask/middleware.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/path.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/paywall.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/py.typed +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/svm_paywall_template.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/ton.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/ton_paywall_template.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/tron.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/types.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/wdk/__init__.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/wdk/chains.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/wdk/errors.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/wdk/signer.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/src/t402/wdk/types.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/tests/clients/__init__.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/tests/clients/test_base.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/tests/clients/test_httpx.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/tests/clients/test_requests.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/tests/fastapi_tests/__init__.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/tests/fastapi_tests/test_middleware.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/tests/flask_tests/__init__.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/tests/flask_tests/test_middleware.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/tests/test_common.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/tests/test_encoding.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/tests/test_exact.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/tests/test_paywall.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/tests/test_ton.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/tests/test_tron.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/tests/test_types.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/tests/test_wdk.py +0 -0
- {t402-1.5.3 → t402-1.6.0}/uv.lock +0 -0
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: t402
|
|
3
|
+
Version: 1.6.0
|
|
4
|
+
Summary: t402: An internet native payments protocol
|
|
5
|
+
Author-email: T402 Team <dev@t402.io>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Keywords: crypto,payments,sdk,t402,tether,usdt,web3
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Requires-Dist: eth-account>=0.13.7
|
|
10
|
+
Requires-Dist: eth-typing>=4.0.0
|
|
11
|
+
Requires-Dist: eth-utils>=3.0.0
|
|
12
|
+
Requires-Dist: fastapi[standard]>=0.115.12
|
|
13
|
+
Requires-Dist: flask>=3.0.0
|
|
14
|
+
Requires-Dist: pydantic-settings>=2.2.1
|
|
15
|
+
Requires-Dist: pydantic>=2.10.3
|
|
16
|
+
Requires-Dist: python-dotenv>=1.0.1
|
|
17
|
+
Requires-Dist: web3>=6.0.0
|
|
18
|
+
Provides-Extra: all
|
|
19
|
+
Requires-Dist: solana>=0.35.0; extra == 'all'
|
|
20
|
+
Requires-Dist: solders>=0.21.0; extra == 'all'
|
|
21
|
+
Provides-Extra: svm
|
|
22
|
+
Requires-Dist: solana>=0.35.0; extra == 'svm'
|
|
23
|
+
Requires-Dist: solders>=0.21.0; extra == 'svm'
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
1
26
|
# t402 Python
|
|
2
27
|
|
|
3
28
|
Python SDK for the T402 HTTP-native stablecoin payments protocol.
|
|
@@ -260,6 +285,39 @@ is_valid = validate_tron_address("T...")
|
|
|
260
285
|
config = get_tron_network_config(TRON_MAINNET)
|
|
261
286
|
```
|
|
262
287
|
|
|
288
|
+
### Solana (SVM) Network
|
|
289
|
+
|
|
290
|
+
```python
|
|
291
|
+
from t402 import (
|
|
292
|
+
SOLANA_MAINNET,
|
|
293
|
+
SOLANA_DEVNET,
|
|
294
|
+
SOLANA_TESTNET,
|
|
295
|
+
validate_svm_address,
|
|
296
|
+
prepare_svm_payment_header,
|
|
297
|
+
get_svm_network_config,
|
|
298
|
+
get_svm_usdc_address,
|
|
299
|
+
is_svm_network,
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
# Validate address
|
|
303
|
+
is_valid = validate_svm_address("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")
|
|
304
|
+
|
|
305
|
+
# Get network config
|
|
306
|
+
config = get_svm_network_config(SOLANA_MAINNET)
|
|
307
|
+
|
|
308
|
+
# Get USDC mint address
|
|
309
|
+
usdc_mint = get_svm_usdc_address(SOLANA_MAINNET)
|
|
310
|
+
|
|
311
|
+
# Check if network is Solana
|
|
312
|
+
is_solana = is_svm_network("solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp")
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
Install with optional Solana dependencies:
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
pip install t402[svm]
|
|
319
|
+
```
|
|
320
|
+
|
|
263
321
|
## ERC-4337 Account Abstraction
|
|
264
322
|
|
|
265
323
|
Gasless payments using smart accounts and paymasters:
|
|
@@ -383,6 +441,7 @@ signature = await signer.sign_payment(
|
|
|
383
441
|
| `is_evm_network(network)` | Check if EVM network |
|
|
384
442
|
| `is_ton_network(network)` | Check if TON network |
|
|
385
443
|
| `is_tron_network(network)` | Check if TRON network |
|
|
444
|
+
| `is_svm_network(network)` | Check if Solana SVM network |
|
|
386
445
|
| `get_network_type(network)` | Get network type string |
|
|
387
446
|
|
|
388
447
|
### Facilitator Client
|
|
@@ -1,22 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: t402
|
|
3
|
-
Version: 1.5.3
|
|
4
|
-
Summary: t402: An internet native payments protocol
|
|
5
|
-
Author-email: T402 Team <dev@t402.io>
|
|
6
|
-
License: Apache-2.0
|
|
7
|
-
Keywords: crypto,payments,sdk,t402,tether,usdt,web3
|
|
8
|
-
Requires-Python: >=3.10
|
|
9
|
-
Requires-Dist: eth-account>=0.13.7
|
|
10
|
-
Requires-Dist: eth-typing>=4.0.0
|
|
11
|
-
Requires-Dist: eth-utils>=3.0.0
|
|
12
|
-
Requires-Dist: fastapi[standard]>=0.115.12
|
|
13
|
-
Requires-Dist: flask>=3.0.0
|
|
14
|
-
Requires-Dist: pydantic-settings>=2.2.1
|
|
15
|
-
Requires-Dist: pydantic>=2.10.3
|
|
16
|
-
Requires-Dist: python-dotenv>=1.0.1
|
|
17
|
-
Requires-Dist: web3>=6.0.0
|
|
18
|
-
Description-Content-Type: text/markdown
|
|
19
|
-
|
|
20
1
|
# t402 Python
|
|
21
2
|
|
|
22
3
|
Python SDK for the T402 HTTP-native stablecoin payments protocol.
|
|
@@ -279,6 +260,39 @@ is_valid = validate_tron_address("T...")
|
|
|
279
260
|
config = get_tron_network_config(TRON_MAINNET)
|
|
280
261
|
```
|
|
281
262
|
|
|
263
|
+
### Solana (SVM) Network
|
|
264
|
+
|
|
265
|
+
```python
|
|
266
|
+
from t402 import (
|
|
267
|
+
SOLANA_MAINNET,
|
|
268
|
+
SOLANA_DEVNET,
|
|
269
|
+
SOLANA_TESTNET,
|
|
270
|
+
validate_svm_address,
|
|
271
|
+
prepare_svm_payment_header,
|
|
272
|
+
get_svm_network_config,
|
|
273
|
+
get_svm_usdc_address,
|
|
274
|
+
is_svm_network,
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
# Validate address
|
|
278
|
+
is_valid = validate_svm_address("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")
|
|
279
|
+
|
|
280
|
+
# Get network config
|
|
281
|
+
config = get_svm_network_config(SOLANA_MAINNET)
|
|
282
|
+
|
|
283
|
+
# Get USDC mint address
|
|
284
|
+
usdc_mint = get_svm_usdc_address(SOLANA_MAINNET)
|
|
285
|
+
|
|
286
|
+
# Check if network is Solana
|
|
287
|
+
is_solana = is_svm_network("solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp")
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Install with optional Solana dependencies:
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
pip install t402[svm]
|
|
294
|
+
```
|
|
295
|
+
|
|
282
296
|
## ERC-4337 Account Abstraction
|
|
283
297
|
|
|
284
298
|
Gasless payments using smart accounts and paymasters:
|
|
@@ -402,6 +416,7 @@ signature = await signer.sign_payment(
|
|
|
402
416
|
| `is_evm_network(network)` | Check if EVM network |
|
|
403
417
|
| `is_ton_network(network)` | Check if TON network |
|
|
404
418
|
| `is_tron_network(network)` | Check if TRON network |
|
|
419
|
+
| `is_svm_network(network)` | Check if Solana SVM network |
|
|
405
420
|
| `get_network_type(network)` | Get network type string |
|
|
406
421
|
|
|
407
422
|
### Facilitator Client
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "t402"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.6.0"
|
|
4
4
|
description = "t402: An internet native payments protocol"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "Apache-2.0" }
|
|
@@ -24,6 +24,16 @@ dependencies = [
|
|
|
24
24
|
[project.scripts]
|
|
25
25
|
t402 = "t402.cli:main"
|
|
26
26
|
|
|
27
|
+
[project.optional-dependencies]
|
|
28
|
+
svm = [
|
|
29
|
+
"solana>=0.35.0",
|
|
30
|
+
"solders>=0.21.0",
|
|
31
|
+
]
|
|
32
|
+
all = [
|
|
33
|
+
"solana>=0.35.0",
|
|
34
|
+
"solders>=0.21.0",
|
|
35
|
+
]
|
|
36
|
+
|
|
27
37
|
[build-system]
|
|
28
38
|
requires = ["hatchling"]
|
|
29
39
|
build-backend = "hatchling.build"
|
|
@@ -33,6 +43,8 @@ dev = [
|
|
|
33
43
|
"pytest>=8.3.5",
|
|
34
44
|
"pytest-asyncio>=1.0.0",
|
|
35
45
|
"ruff>=0.11.9",
|
|
46
|
+
"solana>=0.35.0",
|
|
47
|
+
"solders>=0.21.0",
|
|
36
48
|
]
|
|
37
49
|
|
|
38
50
|
[tool.pytest.ini_options]
|
|
@@ -12,6 +12,7 @@ from t402.networks import (
|
|
|
12
12
|
is_ton_network,
|
|
13
13
|
is_tron_network,
|
|
14
14
|
is_evm_network,
|
|
15
|
+
is_svm_network,
|
|
15
16
|
get_network_type,
|
|
16
17
|
)
|
|
17
18
|
from t402.types import (
|
|
@@ -62,6 +63,24 @@ from t402.tron import (
|
|
|
62
63
|
format_amount as format_tron_amount,
|
|
63
64
|
is_testnet as is_tron_testnet,
|
|
64
65
|
)
|
|
66
|
+
from t402.svm import (
|
|
67
|
+
SOLANA_MAINNET,
|
|
68
|
+
SOLANA_DEVNET,
|
|
69
|
+
SOLANA_TESTNET,
|
|
70
|
+
USDC_MAINNET_ADDRESS as SVM_USDC_MAINNET_ADDRESS,
|
|
71
|
+
USDC_DEVNET_ADDRESS as SVM_USDC_DEVNET_ADDRESS,
|
|
72
|
+
validate_svm_address,
|
|
73
|
+
get_usdc_address as get_svm_usdc_address,
|
|
74
|
+
get_network_config as get_svm_network_config,
|
|
75
|
+
get_default_asset as get_svm_default_asset,
|
|
76
|
+
prepare_svm_payment_header,
|
|
77
|
+
parse_amount as parse_svm_amount,
|
|
78
|
+
format_amount as format_svm_amount,
|
|
79
|
+
is_testnet as is_svm_testnet,
|
|
80
|
+
validate_transaction as validate_svm_transaction,
|
|
81
|
+
normalize_network as normalize_svm_network,
|
|
82
|
+
get_rpc_url as get_svm_rpc_url,
|
|
83
|
+
)
|
|
65
84
|
from t402.paywall import (
|
|
66
85
|
get_paywall_html,
|
|
67
86
|
get_paywall_template,
|
|
@@ -168,6 +187,7 @@ __all__ = [
|
|
|
168
187
|
"is_ton_network",
|
|
169
188
|
"is_tron_network",
|
|
170
189
|
"is_evm_network",
|
|
190
|
+
"is_svm_network",
|
|
171
191
|
"get_network_type",
|
|
172
192
|
# Types
|
|
173
193
|
"PaymentRequirements",
|
|
@@ -215,6 +235,23 @@ __all__ = [
|
|
|
215
235
|
"parse_tron_amount",
|
|
216
236
|
"format_tron_amount",
|
|
217
237
|
"is_tron_testnet",
|
|
238
|
+
# SVM (Solana) utilities
|
|
239
|
+
"SOLANA_MAINNET",
|
|
240
|
+
"SOLANA_DEVNET",
|
|
241
|
+
"SOLANA_TESTNET",
|
|
242
|
+
"SVM_USDC_MAINNET_ADDRESS",
|
|
243
|
+
"SVM_USDC_DEVNET_ADDRESS",
|
|
244
|
+
"validate_svm_address",
|
|
245
|
+
"get_svm_usdc_address",
|
|
246
|
+
"get_svm_network_config",
|
|
247
|
+
"get_svm_default_asset",
|
|
248
|
+
"prepare_svm_payment_header",
|
|
249
|
+
"parse_svm_amount",
|
|
250
|
+
"format_svm_amount",
|
|
251
|
+
"is_svm_testnet",
|
|
252
|
+
"validate_svm_transaction",
|
|
253
|
+
"normalize_svm_network",
|
|
254
|
+
"get_svm_rpc_url",
|
|
218
255
|
# Paywall
|
|
219
256
|
"get_paywall_html",
|
|
220
257
|
"get_paywall_template",
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""T402 MCP Server - Model Context Protocol server for AI agent integration.
|
|
2
|
+
|
|
3
|
+
This module provides an MCP server that enables AI agents to interact with
|
|
4
|
+
blockchain payments using the T402 protocol.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
```python
|
|
8
|
+
from t402.mcp import T402McpServer, ServerConfig
|
|
9
|
+
|
|
10
|
+
config = ServerConfig(demo_mode=True)
|
|
11
|
+
server = T402McpServer(config)
|
|
12
|
+
await server.run()
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Available Tools:
|
|
16
|
+
- t402/getBalance: Get token balances for a wallet on specific network
|
|
17
|
+
- t402/getAllBalances: Get balances across all supported networks
|
|
18
|
+
- t402/pay: Execute stablecoin payments (USDC, USDT, USDT0)
|
|
19
|
+
- t402/payGasless: ERC-4337 gasless payments
|
|
20
|
+
- t402/getBridgeFee: Get LayerZero bridge fee quotes
|
|
21
|
+
- t402/bridge: Bridge USDT0 between chains via LayerZero
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from .server import T402McpServer, run_server
|
|
25
|
+
from .types import (
|
|
26
|
+
ServerConfig,
|
|
27
|
+
SupportedNetwork,
|
|
28
|
+
SupportedToken,
|
|
29
|
+
Tool,
|
|
30
|
+
ToolResult,
|
|
31
|
+
GetBalanceInput,
|
|
32
|
+
GetAllBalancesInput,
|
|
33
|
+
PayInput,
|
|
34
|
+
PayGaslessInput,
|
|
35
|
+
GetBridgeFeeInput,
|
|
36
|
+
BridgeInput,
|
|
37
|
+
BalanceInfo,
|
|
38
|
+
NetworkBalance,
|
|
39
|
+
PaymentResult,
|
|
40
|
+
BridgeFeeResult,
|
|
41
|
+
BridgeResultData,
|
|
42
|
+
)
|
|
43
|
+
from .constants import (
|
|
44
|
+
CHAIN_IDS,
|
|
45
|
+
NATIVE_SYMBOLS,
|
|
46
|
+
EXPLORER_URLS,
|
|
47
|
+
DEFAULT_RPC_URLS,
|
|
48
|
+
USDC_ADDRESSES,
|
|
49
|
+
USDT_ADDRESSES,
|
|
50
|
+
USDT0_ADDRESSES,
|
|
51
|
+
BRIDGEABLE_CHAINS,
|
|
52
|
+
GASLESS_NETWORKS,
|
|
53
|
+
ALL_NETWORKS,
|
|
54
|
+
is_valid_network,
|
|
55
|
+
is_bridgeable_chain,
|
|
56
|
+
is_gasless_network,
|
|
57
|
+
get_token_address,
|
|
58
|
+
get_explorer_tx_url,
|
|
59
|
+
get_rpc_url,
|
|
60
|
+
format_token_amount,
|
|
61
|
+
parse_token_amount,
|
|
62
|
+
)
|
|
63
|
+
from .tools import get_tool_definitions
|
|
64
|
+
|
|
65
|
+
__all__ = [
|
|
66
|
+
# Server
|
|
67
|
+
"T402McpServer",
|
|
68
|
+
"run_server",
|
|
69
|
+
# Config and types
|
|
70
|
+
"ServerConfig",
|
|
71
|
+
"SupportedNetwork",
|
|
72
|
+
"SupportedToken",
|
|
73
|
+
"Tool",
|
|
74
|
+
"ToolResult",
|
|
75
|
+
# Input types
|
|
76
|
+
"GetBalanceInput",
|
|
77
|
+
"GetAllBalancesInput",
|
|
78
|
+
"PayInput",
|
|
79
|
+
"PayGaslessInput",
|
|
80
|
+
"GetBridgeFeeInput",
|
|
81
|
+
"BridgeInput",
|
|
82
|
+
# Result types
|
|
83
|
+
"BalanceInfo",
|
|
84
|
+
"NetworkBalance",
|
|
85
|
+
"PaymentResult",
|
|
86
|
+
"BridgeFeeResult",
|
|
87
|
+
"BridgeResultData",
|
|
88
|
+
# Constants
|
|
89
|
+
"CHAIN_IDS",
|
|
90
|
+
"NATIVE_SYMBOLS",
|
|
91
|
+
"EXPLORER_URLS",
|
|
92
|
+
"DEFAULT_RPC_URLS",
|
|
93
|
+
"USDC_ADDRESSES",
|
|
94
|
+
"USDT_ADDRESSES",
|
|
95
|
+
"USDT0_ADDRESSES",
|
|
96
|
+
"BRIDGEABLE_CHAINS",
|
|
97
|
+
"GASLESS_NETWORKS",
|
|
98
|
+
"ALL_NETWORKS",
|
|
99
|
+
# Functions
|
|
100
|
+
"is_valid_network",
|
|
101
|
+
"is_bridgeable_chain",
|
|
102
|
+
"is_gasless_network",
|
|
103
|
+
"get_token_address",
|
|
104
|
+
"get_explorer_tx_url",
|
|
105
|
+
"get_rpc_url",
|
|
106
|
+
"format_token_amount",
|
|
107
|
+
"parse_token_amount",
|
|
108
|
+
"get_tool_definitions",
|
|
109
|
+
]
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"""Constants for T402 MCP Server."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from .types import ServerConfig, SupportedNetwork, SupportedToken
|
|
6
|
+
|
|
7
|
+
# Chain IDs for supported networks
|
|
8
|
+
CHAIN_IDS: dict[SupportedNetwork, int] = {
|
|
9
|
+
"ethereum": 1,
|
|
10
|
+
"base": 8453,
|
|
11
|
+
"arbitrum": 42161,
|
|
12
|
+
"optimism": 10,
|
|
13
|
+
"polygon": 137,
|
|
14
|
+
"avalanche": 43114,
|
|
15
|
+
"ink": 57073,
|
|
16
|
+
"berachain": 80094,
|
|
17
|
+
"unichain": 130,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
# Native token symbols for each network
|
|
21
|
+
NATIVE_SYMBOLS: dict[SupportedNetwork, str] = {
|
|
22
|
+
"ethereum": "ETH",
|
|
23
|
+
"base": "ETH",
|
|
24
|
+
"arbitrum": "ETH",
|
|
25
|
+
"optimism": "ETH",
|
|
26
|
+
"polygon": "MATIC",
|
|
27
|
+
"avalanche": "AVAX",
|
|
28
|
+
"ink": "ETH",
|
|
29
|
+
"berachain": "BERA",
|
|
30
|
+
"unichain": "ETH",
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
# Block explorer URLs for each network
|
|
34
|
+
EXPLORER_URLS: dict[SupportedNetwork, str] = {
|
|
35
|
+
"ethereum": "https://etherscan.io",
|
|
36
|
+
"base": "https://basescan.org",
|
|
37
|
+
"arbitrum": "https://arbiscan.io",
|
|
38
|
+
"optimism": "https://optimistic.etherscan.io",
|
|
39
|
+
"polygon": "https://polygonscan.com",
|
|
40
|
+
"avalanche": "https://snowtrace.io",
|
|
41
|
+
"ink": "https://explorer.ink.xyz",
|
|
42
|
+
"berachain": "https://berascan.com",
|
|
43
|
+
"unichain": "https://uniscan.xyz",
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# Default RPC URLs for each network
|
|
47
|
+
DEFAULT_RPC_URLS: dict[SupportedNetwork, str] = {
|
|
48
|
+
"ethereum": "https://eth.llamarpc.com",
|
|
49
|
+
"base": "https://mainnet.base.org",
|
|
50
|
+
"arbitrum": "https://arb1.arbitrum.io/rpc",
|
|
51
|
+
"optimism": "https://mainnet.optimism.io",
|
|
52
|
+
"polygon": "https://polygon-rpc.com",
|
|
53
|
+
"avalanche": "https://api.avax.network/ext/bc/C/rpc",
|
|
54
|
+
"ink": "https://rpc-qnd.ink.xyz",
|
|
55
|
+
"berachain": "https://artio.rpc.berachain.com",
|
|
56
|
+
"unichain": "https://mainnet.unichain.org",
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# USDC contract addresses by network
|
|
60
|
+
USDC_ADDRESSES: dict[SupportedNetwork, str] = {
|
|
61
|
+
"ethereum": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
62
|
+
"base": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
63
|
+
"arbitrum": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
|
|
64
|
+
"optimism": "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
|
|
65
|
+
"polygon": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
|
|
66
|
+
"avalanche": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
|
|
67
|
+
"ink": "0x0200C29006150606B650577BBE7B6248F58470c1",
|
|
68
|
+
"berachain": "0x779Ded0c9e1022225f8E0630b35a9b54bE713736",
|
|
69
|
+
"unichain": "0x588ce4F028D8e7B53B687865d6A67b3A54C75518",
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# USDT contract addresses by network
|
|
73
|
+
USDT_ADDRESSES: dict[SupportedNetwork, str] = {
|
|
74
|
+
"ethereum": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
|
|
75
|
+
"arbitrum": "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
|
|
76
|
+
"optimism": "0x94b008aA00579c1307B0EF2c499aD98a8ce58e58",
|
|
77
|
+
"polygon": "0xc2132D05D31c914a87C6611C10748AEb04B58e8F",
|
|
78
|
+
"avalanche": "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7",
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# USDT0 OFT contract addresses (LayerZero bridgeable)
|
|
82
|
+
USDT0_ADDRESSES: dict[SupportedNetwork, str] = {
|
|
83
|
+
"ethereum": "0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee",
|
|
84
|
+
"arbitrum": "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
|
|
85
|
+
"ink": "0x0200C29006150606B650577BBE7B6248F58470c1",
|
|
86
|
+
"berachain": "0x779Ded0c9e1022225f8E0630b35a9b54bE713736",
|
|
87
|
+
"unichain": "0x588ce4F028D8e7B53B687865d6A67b3A54C75518",
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# Networks that support USDT0 bridging via LayerZero
|
|
91
|
+
BRIDGEABLE_CHAINS: list[SupportedNetwork] = [
|
|
92
|
+
"ethereum",
|
|
93
|
+
"arbitrum",
|
|
94
|
+
"ink",
|
|
95
|
+
"berachain",
|
|
96
|
+
"unichain",
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
# Networks that support ERC-4337 gasless payments
|
|
100
|
+
GASLESS_NETWORKS: list[SupportedNetwork] = [
|
|
101
|
+
"ethereum",
|
|
102
|
+
"base",
|
|
103
|
+
"arbitrum",
|
|
104
|
+
"optimism",
|
|
105
|
+
"polygon",
|
|
106
|
+
"avalanche",
|
|
107
|
+
]
|
|
108
|
+
|
|
109
|
+
# LayerZero endpoint IDs for bridging
|
|
110
|
+
LAYERZERO_ENDPOINT_IDS: dict[SupportedNetwork, int] = {
|
|
111
|
+
"ethereum": 30101,
|
|
112
|
+
"arbitrum": 30110,
|
|
113
|
+
"ink": 30291,
|
|
114
|
+
"berachain": 30362,
|
|
115
|
+
"unichain": 30320,
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
# LayerZero Scan URL for tracking bridge messages
|
|
119
|
+
LAYERZERO_SCAN_URL = "https://layerzeroscan.com/tx/"
|
|
120
|
+
|
|
121
|
+
# All supported networks
|
|
122
|
+
ALL_NETWORKS: list[SupportedNetwork] = [
|
|
123
|
+
"ethereum",
|
|
124
|
+
"base",
|
|
125
|
+
"arbitrum",
|
|
126
|
+
"optimism",
|
|
127
|
+
"polygon",
|
|
128
|
+
"avalanche",
|
|
129
|
+
"ink",
|
|
130
|
+
"berachain",
|
|
131
|
+
"unichain",
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
# Token decimals
|
|
135
|
+
TOKEN_DECIMALS = 6
|
|
136
|
+
NATIVE_DECIMALS = 18
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def is_valid_network(network: str) -> bool:
|
|
140
|
+
"""Check if a network string is valid."""
|
|
141
|
+
return network in ALL_NETWORKS
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def is_bridgeable_chain(network: str) -> bool:
|
|
145
|
+
"""Check if a network supports USDT0 bridging."""
|
|
146
|
+
return network in BRIDGEABLE_CHAINS
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def is_gasless_network(network: str) -> bool:
|
|
150
|
+
"""Check if a network supports ERC-4337 gasless payments."""
|
|
151
|
+
return network in GASLESS_NETWORKS
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def get_token_address(
|
|
155
|
+
network: SupportedNetwork, token: SupportedToken
|
|
156
|
+
) -> Optional[str]:
|
|
157
|
+
"""Get the token contract address for a network."""
|
|
158
|
+
if token == "USDC":
|
|
159
|
+
return USDC_ADDRESSES.get(network)
|
|
160
|
+
elif token == "USDT":
|
|
161
|
+
return USDT_ADDRESSES.get(network)
|
|
162
|
+
elif token == "USDT0":
|
|
163
|
+
return USDT0_ADDRESSES.get(network)
|
|
164
|
+
return None
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def get_explorer_tx_url(network: SupportedNetwork, tx_hash: str) -> str:
|
|
168
|
+
"""Get the explorer URL for a transaction."""
|
|
169
|
+
base_url = EXPLORER_URLS.get(network, "")
|
|
170
|
+
if not base_url:
|
|
171
|
+
return ""
|
|
172
|
+
return f"{base_url}/tx/{tx_hash}"
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def get_rpc_url(config: Optional[ServerConfig], network: SupportedNetwork) -> str:
|
|
176
|
+
"""Get the RPC URL for a network, using config override if available."""
|
|
177
|
+
if config and config.rpc_urls and network in config.rpc_urls:
|
|
178
|
+
return config.rpc_urls[network]
|
|
179
|
+
return DEFAULT_RPC_URLS.get(network, "")
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def format_token_amount(amount: int, decimals: int) -> str:
|
|
183
|
+
"""Format a raw token amount with decimals to human-readable string."""
|
|
184
|
+
if amount == 0:
|
|
185
|
+
return "0"
|
|
186
|
+
|
|
187
|
+
divisor = 10**decimals
|
|
188
|
+
whole = amount // divisor
|
|
189
|
+
fraction = amount % divisor
|
|
190
|
+
|
|
191
|
+
if fraction == 0:
|
|
192
|
+
return str(whole)
|
|
193
|
+
|
|
194
|
+
# Format fraction and trim trailing zeros
|
|
195
|
+
fraction_str = str(fraction).zfill(decimals).rstrip("0")
|
|
196
|
+
return f"{whole}.{fraction_str}"
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def parse_token_amount(amount: str, decimals: int) -> int:
|
|
200
|
+
"""Parse a human-readable amount string to raw token units."""
|
|
201
|
+
parts = amount.split(".")
|
|
202
|
+
|
|
203
|
+
whole = int(parts[0])
|
|
204
|
+
result = whole * (10**decimals)
|
|
205
|
+
|
|
206
|
+
if len(parts) == 2:
|
|
207
|
+
frac = parts[1]
|
|
208
|
+
if len(frac) > decimals:
|
|
209
|
+
frac = frac[:decimals]
|
|
210
|
+
frac = frac.ljust(decimals, "0")
|
|
211
|
+
result += int(frac)
|
|
212
|
+
|
|
213
|
+
return result
|