t402 1.9.0__tar.gz → 1.9.1__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.
Files changed (169) hide show
  1. {t402-1.9.0 → t402-1.9.1}/.gitignore +3 -0
  2. {t402-1.9.0 → t402-1.9.1}/PKG-INFO +42 -1
  3. {t402-1.9.0 → t402-1.9.1}/README.md +41 -0
  4. {t402-1.9.0 → t402-1.9.1}/pyproject.toml +2 -1
  5. {t402-1.9.0 → t402-1.9.1}/src/t402/__init__.py +2 -1
  6. {t402-1.9.0 → t402-1.9.1}/src/t402/bridge/client.py +13 -5
  7. {t402-1.9.0 → t402-1.9.1}/src/t402/bridge/constants.py +3 -1
  8. {t402-1.9.0 → t402-1.9.1}/src/t402/bridge/router.py +1 -1
  9. {t402-1.9.0 → t402-1.9.1}/src/t402/bridge/scan.py +3 -1
  10. t402-1.9.1/src/t402/chains.py +359 -0
  11. {t402-1.9.0 → t402-1.9.1}/src/t402/cli.py +31 -9
  12. {t402-1.9.0 → t402-1.9.1}/src/t402/common.py +2 -0
  13. t402-1.9.1/src/t402/cosmos_paywall_template.py +2 -0
  14. {t402-1.9.0 → t402-1.9.1}/src/t402/encoding.py +9 -3
  15. {t402-1.9.0 → t402-1.9.1}/src/t402/erc4337/accounts.py +56 -51
  16. {t402-1.9.0 → t402-1.9.1}/src/t402/erc4337/bundlers.py +105 -99
  17. {t402-1.9.0 → t402-1.9.1}/src/t402/erc4337/paymasters.py +100 -109
  18. {t402-1.9.0 → t402-1.9.1}/src/t402/erc4337/types.py +39 -26
  19. t402-1.9.1/src/t402/evm_paywall_template.py +2 -0
  20. {t402-1.9.0 → t402-1.9.1}/src/t402/fastapi/middleware.py +1 -3
  21. {t402-1.9.0 → t402-1.9.1}/src/t402/mcp/server.py +79 -46
  22. t402-1.9.1/src/t402/near_paywall_template.py +2 -0
  23. {t402-1.9.0 → t402-1.9.1}/src/t402/networks.py +34 -1
  24. {t402-1.9.0 → t402-1.9.1}/src/t402/paywall.py +1 -3
  25. {t402-1.9.0 → t402-1.9.1}/src/t402/schemes/__init__.py +124 -0
  26. t402-1.9.1/src/t402/schemes/aptos/__init__.py +70 -0
  27. t402-1.9.1/src/t402/schemes/aptos/constants.py +349 -0
  28. t402-1.9.1/src/t402/schemes/aptos/exact_direct/__init__.py +44 -0
  29. t402-1.9.1/src/t402/schemes/aptos/exact_direct/client.py +202 -0
  30. t402-1.9.1/src/t402/schemes/aptos/exact_direct/facilitator.py +426 -0
  31. t402-1.9.1/src/t402/schemes/aptos/exact_direct/server.py +272 -0
  32. t402-1.9.1/src/t402/schemes/aptos/types.py +237 -0
  33. t402-1.9.1/src/t402/schemes/evm/__init__.py +91 -0
  34. {t402-1.9.0 → t402-1.9.1}/src/t402/schemes/evm/exact/__init__.py +11 -0
  35. {t402-1.9.0 → t402-1.9.1}/src/t402/schemes/evm/exact/client.py +3 -1
  36. t402-1.9.1/src/t402/schemes/evm/exact/facilitator.py +894 -0
  37. {t402-1.9.0 → t402-1.9.1}/src/t402/schemes/evm/exact/server.py +1 -1
  38. t402-1.9.1/src/t402/schemes/evm/exact_legacy/__init__.py +38 -0
  39. t402-1.9.1/src/t402/schemes/evm/exact_legacy/client.py +291 -0
  40. t402-1.9.1/src/t402/schemes/evm/exact_legacy/facilitator.py +777 -0
  41. t402-1.9.1/src/t402/schemes/evm/exact_legacy/server.py +231 -0
  42. {t402-1.9.0 → t402-1.9.1}/src/t402/schemes/evm/upto/__init__.py +12 -0
  43. {t402-1.9.0 → t402-1.9.1}/src/t402/schemes/evm/upto/client.py +6 -2
  44. t402-1.9.1/src/t402/schemes/evm/upto/facilitator.py +625 -0
  45. t402-1.9.1/src/t402/schemes/evm/upto/server.py +243 -0
  46. {t402-1.9.0 → t402-1.9.1}/src/t402/schemes/evm/upto/types.py +3 -1
  47. {t402-1.9.0 → t402-1.9.1}/src/t402/schemes/interfaces.py +6 -2
  48. t402-1.9.1/src/t402/schemes/near/__init__.py +112 -0
  49. t402-1.9.1/src/t402/schemes/near/constants.py +189 -0
  50. t402-1.9.1/src/t402/schemes/near/exact_direct/__init__.py +21 -0
  51. t402-1.9.1/src/t402/schemes/near/exact_direct/client.py +204 -0
  52. t402-1.9.1/src/t402/schemes/near/exact_direct/facilitator.py +455 -0
  53. t402-1.9.1/src/t402/schemes/near/exact_direct/server.py +303 -0
  54. t402-1.9.1/src/t402/schemes/near/types.py +419 -0
  55. t402-1.9.1/src/t402/schemes/polkadot/__init__.py +72 -0
  56. t402-1.9.1/src/t402/schemes/polkadot/constants.py +155 -0
  57. t402-1.9.1/src/t402/schemes/polkadot/exact_direct/__init__.py +43 -0
  58. t402-1.9.1/src/t402/schemes/polkadot/exact_direct/client.py +235 -0
  59. t402-1.9.1/src/t402/schemes/polkadot/exact_direct/facilitator.py +428 -0
  60. t402-1.9.1/src/t402/schemes/polkadot/exact_direct/server.py +292 -0
  61. t402-1.9.1/src/t402/schemes/polkadot/types.py +385 -0
  62. {t402-1.9.0 → t402-1.9.1}/src/t402/schemes/registry.py +6 -2
  63. t402-1.9.1/src/t402/schemes/stacks/__init__.py +68 -0
  64. t402-1.9.1/src/t402/schemes/stacks/constants.py +122 -0
  65. t402-1.9.1/src/t402/schemes/stacks/exact_direct/__init__.py +43 -0
  66. t402-1.9.1/src/t402/schemes/stacks/exact_direct/client.py +222 -0
  67. t402-1.9.1/src/t402/schemes/stacks/exact_direct/facilitator.py +424 -0
  68. t402-1.9.1/src/t402/schemes/stacks/exact_direct/server.py +292 -0
  69. t402-1.9.1/src/t402/schemes/stacks/types.py +380 -0
  70. t402-1.9.1/src/t402/schemes/svm/__init__.py +29 -0
  71. t402-1.9.1/src/t402/schemes/svm/exact/__init__.py +35 -0
  72. t402-1.9.1/src/t402/schemes/svm/exact/client.py +23 -0
  73. t402-1.9.1/src/t402/schemes/svm/exact/facilitator.py +24 -0
  74. t402-1.9.1/src/t402/schemes/svm/exact/server.py +20 -0
  75. t402-1.9.1/src/t402/schemes/tezos/__init__.py +84 -0
  76. t402-1.9.1/src/t402/schemes/tezos/constants.py +372 -0
  77. t402-1.9.1/src/t402/schemes/tezos/exact_direct/__init__.py +22 -0
  78. t402-1.9.1/src/t402/schemes/tezos/exact_direct/client.py +226 -0
  79. t402-1.9.1/src/t402/schemes/tezos/exact_direct/facilitator.py +491 -0
  80. t402-1.9.1/src/t402/schemes/tezos/exact_direct/server.py +277 -0
  81. t402-1.9.1/src/t402/schemes/tezos/types.py +220 -0
  82. {t402-1.9.0 → t402-1.9.1}/src/t402/schemes/ton/__init__.py +9 -2
  83. {t402-1.9.0 → t402-1.9.1}/src/t402/schemes/ton/exact/__init__.py +7 -0
  84. t402-1.9.1/src/t402/schemes/ton/exact/facilitator.py +730 -0
  85. {t402-1.9.0 → t402-1.9.1}/src/t402/schemes/ton/exact/server.py +1 -1
  86. {t402-1.9.0 → t402-1.9.1}/src/t402/schemes/tron/__init__.py +11 -2
  87. {t402-1.9.0 → t402-1.9.1}/src/t402/schemes/tron/exact/__init__.py +9 -0
  88. t402-1.9.1/src/t402/schemes/tron/exact/facilitator.py +673 -0
  89. {t402-1.9.0 → t402-1.9.1}/src/t402/schemes/tron/exact/server.py +1 -1
  90. t402-1.9.1/src/t402/stacks_paywall_template.py +2 -0
  91. {t402-1.9.0 → t402-1.9.1}/src/t402/svm.py +45 -11
  92. t402-1.9.1/src/t402/svm_paywall_template.py +2 -0
  93. {t402-1.9.0 → t402-1.9.1}/src/t402/ton.py +5 -1
  94. t402-1.9.1/src/t402/ton_paywall_template.py +2 -0
  95. {t402-1.9.0 → t402-1.9.1}/src/t402/tron.py +2 -0
  96. t402-1.9.1/src/t402/tron_paywall_template.py +2 -0
  97. {t402-1.9.0 → t402-1.9.1}/src/t402/types.py +3 -1
  98. {t402-1.9.0 → t402-1.9.1}/src/t402/wdk/errors.py +15 -5
  99. {t402-1.9.0 → t402-1.9.1}/src/t402/wdk/signer.py +11 -2
  100. t402-1.9.1/tests/test_aptos_scheme.py +1631 -0
  101. {t402-1.9.0 → t402-1.9.1}/tests/test_bridge.py +550 -87
  102. t402-1.9.1/tests/test_cli.py +675 -0
  103. t402-1.9.1/tests/test_erc4337.py +1087 -0
  104. t402-1.9.1/tests/test_evm_facilitator.py +1595 -0
  105. {t402-1.9.0 → t402-1.9.1}/tests/test_fastapi.py +2 -6
  106. {t402-1.9.0 → t402-1.9.1}/tests/test_mcp.py +0 -10
  107. t402-1.9.1/tests/test_near_scheme.py +1464 -0
  108. t402-1.9.1/tests/test_polkadot_scheme.py +1676 -0
  109. {t402-1.9.0 → t402-1.9.1}/tests/test_schemes.py +12 -16
  110. t402-1.9.1/tests/test_stacks_scheme.py +1006 -0
  111. {t402-1.9.0 → t402-1.9.1}/tests/test_svm.py +48 -36
  112. t402-1.9.1/tests/test_svm_scheme.py +405 -0
  113. t402-1.9.1/tests/test_tezos_scheme.py +1329 -0
  114. {t402-1.9.0 → t402-1.9.1}/tests/test_ton.py +3 -1
  115. t402-1.9.1/tests/test_ton_facilitator.py +1278 -0
  116. {t402-1.9.0 → t402-1.9.1}/tests/test_tron.py +0 -4
  117. t402-1.9.1/tests/test_tron_facilitator.py +1257 -0
  118. {t402-1.9.0 → t402-1.9.1}/tests/test_upto.py +0 -1
  119. {t402-1.9.0 → t402-1.9.1}/tests/test_upto_evm.py +3 -5
  120. t402-1.9.1/tests/test_upto_evm_facilitator.py +882 -0
  121. t402-1.9.1/tests/test_upto_evm_server.py +582 -0
  122. {t402-1.9.0 → t402-1.9.1}/tests/test_v2_types.py +0 -3
  123. {t402-1.9.0 → t402-1.9.1}/tests/test_wdk.py +0 -5
  124. {t402-1.9.0 → t402-1.9.1}/uv.lock +1 -1
  125. t402-1.9.0/src/t402/chains.py +0 -92
  126. t402-1.9.0/src/t402/evm_paywall_template.py +0 -2
  127. t402-1.9.0/src/t402/schemes/evm/__init__.py +0 -46
  128. t402-1.9.0/src/t402/svm_paywall_template.py +0 -2
  129. t402-1.9.0/src/t402/ton_paywall_template.py +0 -193
  130. {t402-1.9.0 → t402-1.9.1}/.python-version +0 -0
  131. {t402-1.9.0 → t402-1.9.1}/src/t402/bridge/__init__.py +0 -0
  132. {t402-1.9.0 → t402-1.9.1}/src/t402/bridge/types.py +0 -0
  133. {t402-1.9.0 → t402-1.9.1}/src/t402/clients/__init__.py +0 -0
  134. {t402-1.9.0 → t402-1.9.1}/src/t402/clients/base.py +0 -0
  135. {t402-1.9.0 → t402-1.9.1}/src/t402/clients/httpx.py +0 -0
  136. {t402-1.9.0 → t402-1.9.1}/src/t402/clients/requests.py +0 -0
  137. {t402-1.9.0 → t402-1.9.1}/src/t402/erc4337/__init__.py +0 -0
  138. {t402-1.9.0 → t402-1.9.1}/src/t402/exact.py +0 -0
  139. {t402-1.9.0 → t402-1.9.1}/src/t402/facilitator.py +0 -0
  140. {t402-1.9.0 → t402-1.9.1}/src/t402/fastapi/__init__.py +0 -0
  141. {t402-1.9.0 → t402-1.9.1}/src/t402/fastapi/dependencies.py +0 -0
  142. {t402-1.9.0 → t402-1.9.1}/src/t402/flask/__init__.py +0 -0
  143. {t402-1.9.0 → t402-1.9.1}/src/t402/flask/middleware.py +0 -0
  144. {t402-1.9.0 → t402-1.9.1}/src/t402/mcp/__init__.py +0 -0
  145. {t402-1.9.0 → t402-1.9.1}/src/t402/mcp/constants.py +0 -0
  146. {t402-1.9.0 → t402-1.9.1}/src/t402/mcp/tools.py +0 -0
  147. {t402-1.9.0 → t402-1.9.1}/src/t402/mcp/types.py +0 -0
  148. {t402-1.9.0 → t402-1.9.1}/src/t402/path.py +0 -0
  149. {t402-1.9.0 → t402-1.9.1}/src/t402/py.typed +0 -0
  150. {t402-1.9.0 → t402-1.9.1}/src/t402/schemes/ton/exact/client.py +0 -0
  151. {t402-1.9.0 → t402-1.9.1}/src/t402/schemes/tron/exact/client.py +0 -0
  152. {t402-1.9.0 → t402-1.9.1}/src/t402/schemes/upto/__init__.py +0 -0
  153. {t402-1.9.0 → t402-1.9.1}/src/t402/schemes/upto/types.py +0 -0
  154. {t402-1.9.0 → t402-1.9.1}/src/t402/wdk/__init__.py +0 -0
  155. {t402-1.9.0 → t402-1.9.1}/src/t402/wdk/chains.py +0 -0
  156. {t402-1.9.0 → t402-1.9.1}/src/t402/wdk/types.py +0 -0
  157. {t402-1.9.0 → t402-1.9.1}/tests/clients/__init__.py +0 -0
  158. {t402-1.9.0 → t402-1.9.1}/tests/clients/test_base.py +0 -0
  159. {t402-1.9.0 → t402-1.9.1}/tests/clients/test_httpx.py +0 -0
  160. {t402-1.9.0 → t402-1.9.1}/tests/clients/test_requests.py +0 -0
  161. {t402-1.9.0 → t402-1.9.1}/tests/fastapi_tests/__init__.py +0 -0
  162. {t402-1.9.0 → t402-1.9.1}/tests/fastapi_tests/test_middleware.py +0 -0
  163. {t402-1.9.0 → t402-1.9.1}/tests/flask_tests/__init__.py +0 -0
  164. {t402-1.9.0 → t402-1.9.1}/tests/flask_tests/test_middleware.py +0 -0
  165. {t402-1.9.0 → t402-1.9.1}/tests/test_common.py +0 -0
  166. {t402-1.9.0 → t402-1.9.1}/tests/test_encoding.py +0 -0
  167. {t402-1.9.0 → t402-1.9.1}/tests/test_exact.py +0 -0
  168. {t402-1.9.0 → t402-1.9.1}/tests/test_paywall.py +0 -0
  169. {t402-1.9.0 → t402-1.9.1}/tests/test_types.py +0 -0
@@ -7,9 +7,12 @@ coverage/
7
7
  proxy
8
8
  .env
9
9
  __pycache__/
10
+ .coverage
10
11
  **/.DS_Store
11
12
  e2e/facilitators/external-proxies/*
12
13
  !e2e/facilitators/external-proxies/README.md
13
14
  .vercel
14
15
  .env*.local
16
+ .wrangler/
17
+ .claude/settings.local.json
15
18
  api-docs/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: t402
3
- Version: 1.9.0
3
+ Version: 1.9.1
4
4
  Summary: t402: An internet native payments protocol
5
5
  Author-email: T402 Team <dev@t402.io>
6
6
  License: Apache-2.0
@@ -389,6 +389,47 @@ result = await bridge.bridge(
389
389
  )
390
390
  ```
391
391
 
392
+ ## Deprecation Notice: exact-legacy Scheme
393
+
394
+ > **⚠️ Deprecated in v2.3.0**: The `exact-legacy` scheme is deprecated and will be removed in v3.0.0.
395
+
396
+ The `exact-legacy` scheme uses the traditional `approve + transferFrom` pattern for legacy USDT tokens. This has been superseded by the `exact` scheme with USDT0.
397
+
398
+ ### Why Migrate?
399
+
400
+ | Feature | exact-legacy | exact (USDT0) |
401
+ |---------|--------------|---------------|
402
+ | Transactions | 2 (approve + transfer) | 1 (single signature) |
403
+ | Gas Cost | User pays gas | Gasless (EIP-3009) |
404
+ | Chains | ~5 chains | 19+ chains |
405
+ | Cross-chain | ❌ | ✅ LayerZero bridge |
406
+
407
+ ### Migration Guide
408
+
409
+ ```python
410
+ # Before (deprecated)
411
+ from t402.schemes.evm import ExactLegacyEvmClientScheme, ExactLegacyEvmServerScheme
412
+
413
+ client_scheme = ExactLegacyEvmClientScheme(signer)
414
+ server_scheme = ExactLegacyEvmServerScheme()
415
+
416
+ # After (recommended)
417
+ from t402.schemes.evm import ExactEvmClientScheme, ExactEvmServerScheme
418
+
419
+ client_scheme = ExactEvmClientScheme(signer)
420
+ server_scheme = ExactEvmServerScheme()
421
+ ```
422
+
423
+ ### USDT0 Token Addresses
424
+
425
+ | Chain | USDT0 Address |
426
+ |-------|---------------|
427
+ | Ethereum | `0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee` |
428
+ | Arbitrum | `0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9` |
429
+ | Ink | `0x0200C29006150606B650577BBE7B6248F58470c1` |
430
+ | Berachain | `0x779Ded0c9e1022225f8E0630b35a9b54bE713736` |
431
+ | And 15+ more... | See [USDT0 documentation](https://docs.t402.io/networks/usdt0) |
432
+
392
433
  ## WDK Integration
393
434
 
394
435
  Tether Wallet Development Kit support:
@@ -364,6 +364,47 @@ result = await bridge.bridge(
364
364
  )
365
365
  ```
366
366
 
367
+ ## Deprecation Notice: exact-legacy Scheme
368
+
369
+ > **⚠️ Deprecated in v2.3.0**: The `exact-legacy` scheme is deprecated and will be removed in v3.0.0.
370
+
371
+ The `exact-legacy` scheme uses the traditional `approve + transferFrom` pattern for legacy USDT tokens. This has been superseded by the `exact` scheme with USDT0.
372
+
373
+ ### Why Migrate?
374
+
375
+ | Feature | exact-legacy | exact (USDT0) |
376
+ |---------|--------------|---------------|
377
+ | Transactions | 2 (approve + transfer) | 1 (single signature) |
378
+ | Gas Cost | User pays gas | Gasless (EIP-3009) |
379
+ | Chains | ~5 chains | 19+ chains |
380
+ | Cross-chain | ❌ | ✅ LayerZero bridge |
381
+
382
+ ### Migration Guide
383
+
384
+ ```python
385
+ # Before (deprecated)
386
+ from t402.schemes.evm import ExactLegacyEvmClientScheme, ExactLegacyEvmServerScheme
387
+
388
+ client_scheme = ExactLegacyEvmClientScheme(signer)
389
+ server_scheme = ExactLegacyEvmServerScheme()
390
+
391
+ # After (recommended)
392
+ from t402.schemes.evm import ExactEvmClientScheme, ExactEvmServerScheme
393
+
394
+ client_scheme = ExactEvmClientScheme(signer)
395
+ server_scheme = ExactEvmServerScheme()
396
+ ```
397
+
398
+ ### USDT0 Token Addresses
399
+
400
+ | Chain | USDT0 Address |
401
+ |-------|---------------|
402
+ | Ethereum | `0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee` |
403
+ | Arbitrum | `0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9` |
404
+ | Ink | `0x0200C29006150606B650577BBE7B6248F58470c1` |
405
+ | Berachain | `0x779Ded0c9e1022225f8E0630b35a9b54bE713736` |
406
+ | And 15+ more... | See [USDT0 documentation](https://docs.t402.io/networks/usdt0) |
407
+
367
408
  ## WDK Integration
368
409
 
369
410
  Tether Wallet Development Kit support:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "t402"
3
- version = "1.9.0"
3
+ version = "1.9.1"
4
4
  description = "t402: An internet native payments protocol"
5
5
  readme = "README.md"
6
6
  license = { text = "Apache-2.0" }
@@ -50,6 +50,7 @@ dev = [
50
50
 
51
51
  [tool.pytest.ini_options]
52
52
  asyncio_mode = "auto"
53
+ pythonpath = ["src"]
53
54
 
54
55
  [tool.hatch.build.targets.wheel]
55
56
  packages = ["src/t402"]
@@ -1,5 +1,5 @@
1
1
  # Package version
2
- __version__ = "1.7.1"
2
+ __version__ = "1.9.0"
3
3
 
4
4
  # Re-export commonly used items for convenience
5
5
  from t402.common import (
@@ -290,6 +290,7 @@ from t402.fastapi import (
290
290
  settle_payment,
291
291
  )
292
292
 
293
+
293
294
  def hello() -> str:
294
295
  return "Hello from t402!"
295
296
 
@@ -1,6 +1,5 @@
1
1
  """USDT0 Bridge Client for LayerZero OFT transfers."""
2
2
 
3
-
4
3
  from .constants import (
5
4
  DEFAULT_EXTRA_OPTIONS,
6
5
  DEFAULT_SLIPPAGE,
@@ -73,7 +72,7 @@ class Usdt0Bridge:
73
72
  if not supports_bridging(chain):
74
73
  raise ValueError(
75
74
  f'Chain "{chain}" does not support USDT0 bridging. '
76
- f'Supported chains: {", ".join(get_bridgeable_chains())}'
75
+ f"Supported chains: {', '.join(get_bridgeable_chains())}"
77
76
  )
78
77
 
79
78
  self._signer = signer
@@ -107,7 +106,9 @@ class Usdt0Bridge:
107
106
  False,
108
107
  )
109
108
 
110
- native_fee = fee[0] if isinstance(fee, (list, tuple)) else fee.get("nativeFee", 0)
109
+ native_fee = (
110
+ fee[0] if isinstance(fee, (list, tuple)) else fee.get("nativeFee", 0)
111
+ )
111
112
 
112
113
  return BridgeQuote(
113
114
  native_fee=int(native_fee),
@@ -139,7 +140,11 @@ class Usdt0Bridge:
139
140
  )
140
141
  )
141
142
 
142
- slippage = params.slippage_tolerance if params.slippage_tolerance > 0 else DEFAULT_SLIPPAGE
143
+ slippage = (
144
+ params.slippage_tolerance
145
+ if params.slippage_tolerance > 0
146
+ else DEFAULT_SLIPPAGE
147
+ )
143
148
  oft_address = get_usdt0_oft_address(params.from_chain)
144
149
  send_param = self._build_send_param(
145
150
  params.to_chain, params.amount, params.recipient, slippage
@@ -304,7 +309,10 @@ class Usdt0Bridge:
304
309
  def _extract_message_guid(self, receipt: BridgeTransactionReceipt) -> str:
305
310
  """Extract LayerZero message GUID from OFTSent event logs."""
306
311
  for log in receipt.logs:
307
- if len(log.topics) >= 2 and log.topics[0].lower() == OFT_SENT_EVENT_TOPIC.lower():
312
+ if (
313
+ len(log.topics) >= 2
314
+ and log.topics[0].lower() == OFT_SENT_EVENT_TOPIC.lower()
315
+ ):
308
316
  # GUID is the first indexed parameter (topics[1])
309
317
  return log.topics[1]
310
318
 
@@ -251,7 +251,9 @@ def address_to_bytes32(address: str) -> bytes:
251
251
  addr = address.lower().removeprefix("0x")
252
252
 
253
253
  if len(addr) != 40:
254
- raise ValueError(f"Invalid address length: expected 40 hex chars, got {len(addr)}")
254
+ raise ValueError(
255
+ f"Invalid address length: expected 40 hex chars, got {len(addr)}"
256
+ )
255
257
 
256
258
  try:
257
259
  addr_bytes = bytes.fromhex(addr)
@@ -199,7 +199,7 @@ class CrossChainPaymentRouter:
199
199
  raise ValueError(
200
200
  f'Cannot route payment from "{params.source_chain}" to '
201
201
  f'"{params.destination_chain}". '
202
- f'Supported chains: {", ".join(get_bridgeable_chains())}'
202
+ f"Supported chains: {', '.join(get_bridgeable_chains())}"
203
203
  )
204
204
 
205
205
  if params.amount <= 0:
@@ -206,7 +206,9 @@ class LayerZeroScanClient:
206
206
  dst_tx_hash=data.get("dstTxHash"),
207
207
  status=LayerZeroMessageStatus(data.get("status") or "INFLIGHT"),
208
208
  src_block_number=int(data.get("srcBlockNumber") or 0),
209
- dst_block_number=int(data.get("dstBlockNumber")) if data.get("dstBlockNumber") else None,
209
+ dst_block_number=int(data.get("dstBlockNumber"))
210
+ if data.get("dstBlockNumber")
211
+ else None,
210
212
  created=data.get("created") or data.get("createdAt") or "",
211
213
  updated=data.get("updated") or data.get("updatedAt") or "",
212
214
  )
@@ -0,0 +1,359 @@
1
+ NETWORK_TO_ID = {
2
+ # Standard networks
3
+ "base-sepolia": "84532",
4
+ "base": "8453",
5
+ "avalanche-fuji": "43113",
6
+ "avalanche": "43114",
7
+ # Core USDT0 Networks
8
+ "ethereum": "1",
9
+ "arbitrum": "42161",
10
+ "optimism": "10",
11
+ "polygon": "137",
12
+ "ink": "57073",
13
+ "berachain": "80094",
14
+ "unichain": "130",
15
+ # Phase 1: High Priority USDT0 Networks
16
+ "mantle": "5000",
17
+ "plasma": "9745",
18
+ "sei": "1329",
19
+ "conflux": "1030",
20
+ "monad": "143",
21
+ # Phase 2: Medium Priority USDT0 Networks
22
+ "flare": "14",
23
+ "rootstock": "30",
24
+ "xlayer": "196",
25
+ "stable": "988",
26
+ "hyperevm": "999",
27
+ "megaeth": "4326",
28
+ "corn": "21000000",
29
+ # Legacy USDT Networks (no EIP-3009 support)
30
+ "bnb": "56",
31
+ "bsc": "56",
32
+ "fantom": "250",
33
+ "celo": "42220",
34
+ "kaia": "8217",
35
+ "klaytn": "8217",
36
+ }
37
+
38
+
39
+ def get_chain_id(network: str) -> str:
40
+ """Get the chain ID for a given network
41
+ Supports string encoded chain IDs and human readable networks
42
+ """
43
+ try:
44
+ int(network)
45
+ return network
46
+ except ValueError:
47
+ pass
48
+ if network not in NETWORK_TO_ID:
49
+ raise ValueError(f"Unsupported network: {network}")
50
+ return NETWORK_TO_ID[network]
51
+
52
+
53
+ KNOWN_TOKENS = {
54
+ # Base Sepolia (testnet)
55
+ "84532": [
56
+ {
57
+ "human_name": "usdc",
58
+ "address": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
59
+ "name": "USDC",
60
+ "decimals": 6,
61
+ "version": "2",
62
+ }
63
+ ],
64
+ # Base Mainnet
65
+ "8453": [
66
+ {
67
+ "human_name": "usdc",
68
+ "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
69
+ "name": "USD Coin",
70
+ "decimals": 6,
71
+ "version": "2",
72
+ }
73
+ ],
74
+ # Avalanche Fuji (testnet)
75
+ "43113": [
76
+ {
77
+ "human_name": "usdc",
78
+ "address": "0x5425890298aed601595a70AB815c96711a31Bc65",
79
+ "name": "USD Coin",
80
+ "decimals": 6,
81
+ "version": "2",
82
+ }
83
+ ],
84
+ # Avalanche Mainnet
85
+ "43114": [
86
+ {
87
+ "human_name": "usdc",
88
+ "address": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
89
+ "name": "USDC",
90
+ "decimals": 6,
91
+ "version": "2",
92
+ }
93
+ ],
94
+ # === USDT0 Networks ===
95
+ # Ethereum Mainnet
96
+ "1": [
97
+ {
98
+ "human_name": "usdt0",
99
+ "address": "0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee",
100
+ "name": "TetherToken",
101
+ "decimals": 6,
102
+ "version": "1",
103
+ }
104
+ ],
105
+ # Arbitrum One
106
+ "42161": [
107
+ {
108
+ "human_name": "usdt0",
109
+ "address": "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
110
+ "name": "TetherToken",
111
+ "decimals": 6,
112
+ "version": "1",
113
+ }
114
+ ],
115
+ # Optimism
116
+ "10": [
117
+ {
118
+ "human_name": "usdt0",
119
+ "address": "0x01bFF41798a0BcF287b996046Ca68b395DbC1071",
120
+ "name": "TetherToken",
121
+ "decimals": 6,
122
+ "version": "1",
123
+ }
124
+ ],
125
+ # Polygon
126
+ "137": [
127
+ {
128
+ "human_name": "usdt0",
129
+ "address": "0xc2132D05D31c914a87C6611C10748AEb04B58e8F",
130
+ "name": "TetherToken",
131
+ "decimals": 6,
132
+ "version": "1",
133
+ }
134
+ ],
135
+ # Ink
136
+ "57073": [
137
+ {
138
+ "human_name": "usdt0",
139
+ "address": "0x0200C29006150606B650577BBE7B6248F58470c1",
140
+ "name": "TetherToken",
141
+ "decimals": 6,
142
+ "version": "1",
143
+ }
144
+ ],
145
+ # Berachain
146
+ "80094": [
147
+ {
148
+ "human_name": "usdt0",
149
+ "address": "0x779Ded0c9e1022225f8E0630b35a9b54bE713736",
150
+ "name": "TetherToken",
151
+ "decimals": 6,
152
+ "version": "1",
153
+ }
154
+ ],
155
+ # Unichain
156
+ "130": [
157
+ {
158
+ "human_name": "usdt0",
159
+ "address": "0x9151434b16b9763660705744891fA906F660EcC5",
160
+ "name": "TetherToken",
161
+ "decimals": 6,
162
+ "version": "1",
163
+ }
164
+ ],
165
+ # Mantle
166
+ "5000": [
167
+ {
168
+ "human_name": "usdt0",
169
+ "address": "0x779Ded0c9e1022225f8E0630b35a9b54bE713736",
170
+ "name": "TetherToken",
171
+ "decimals": 6,
172
+ "version": "1",
173
+ }
174
+ ],
175
+ # Plasma
176
+ "9745": [
177
+ {
178
+ "human_name": "usdt0",
179
+ "address": "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb",
180
+ "name": "TetherToken",
181
+ "decimals": 6,
182
+ "version": "1",
183
+ }
184
+ ],
185
+ # Sei
186
+ "1329": [
187
+ {
188
+ "human_name": "usdt0",
189
+ "address": "0x9151434b16b9763660705744891fA906F660EcC5",
190
+ "name": "TetherToken",
191
+ "decimals": 6,
192
+ "version": "1",
193
+ }
194
+ ],
195
+ # Conflux eSpace
196
+ "1030": [
197
+ {
198
+ "human_name": "usdt0",
199
+ "address": "0xaf37E8B6C9ED7f6318979f56Fc287d76c30847ff",
200
+ "name": "TetherToken",
201
+ "decimals": 6,
202
+ "version": "1",
203
+ }
204
+ ],
205
+ # Monad
206
+ "143": [
207
+ {
208
+ "human_name": "usdt0",
209
+ "address": "0xe7cd86e13AC4309349F30B3435a9d337750fC82D",
210
+ "name": "TetherToken",
211
+ "decimals": 6,
212
+ "version": "1",
213
+ }
214
+ ],
215
+ # Flare
216
+ "14": [
217
+ {
218
+ "human_name": "usdt0",
219
+ "address": "0xe7cd86e13AC4309349F30B3435a9d337750fC82D",
220
+ "name": "TetherToken",
221
+ "decimals": 6,
222
+ "version": "1",
223
+ }
224
+ ],
225
+ # Rootstock
226
+ "30": [
227
+ {
228
+ "human_name": "usdt0",
229
+ "address": "0x779dED0C9e1022225F8e0630b35A9B54Be713736",
230
+ "name": "TetherToken",
231
+ "decimals": 6,
232
+ "version": "1",
233
+ }
234
+ ],
235
+ # XLayer
236
+ "196": [
237
+ {
238
+ "human_name": "usdt0",
239
+ "address": "0x779Ded0c9e1022225f8E0630b35a9b54bE713736",
240
+ "name": "TetherToken",
241
+ "decimals": 6,
242
+ "version": "1",
243
+ }
244
+ ],
245
+ # Stable
246
+ "988": [
247
+ {
248
+ "human_name": "usdt0",
249
+ "address": "0x779Ded0c9e1022225f8E0630b35a9b54bE713736",
250
+ "name": "TetherToken",
251
+ "decimals": 6,
252
+ "version": "1",
253
+ }
254
+ ],
255
+ # HyperEVM
256
+ "999": [
257
+ {
258
+ "human_name": "usdt0",
259
+ "address": "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb",
260
+ "name": "TetherToken",
261
+ "decimals": 6,
262
+ "version": "1",
263
+ }
264
+ ],
265
+ # MegaETH
266
+ "4326": [
267
+ {
268
+ "human_name": "usdt0",
269
+ "address": "0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb",
270
+ "name": "TetherToken",
271
+ "decimals": 6,
272
+ "version": "1",
273
+ }
274
+ ],
275
+ # Corn
276
+ "21000000": [
277
+ {
278
+ "human_name": "usdt0",
279
+ "address": "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb",
280
+ "name": "TetherToken",
281
+ "decimals": 6,
282
+ "version": "1",
283
+ }
284
+ ],
285
+ # === Legacy USDT Networks (no EIP-3009 support) ===
286
+ # BNB Chain (BSC)
287
+ "56": [
288
+ {
289
+ "human_name": "usdt",
290
+ "address": "0x55d398326f99059fF775485246999027B3197955",
291
+ "name": "Tether USD",
292
+ "decimals": 18,
293
+ "version": "1",
294
+ }
295
+ ],
296
+ # Avalanche C-Chain (already in KNOWN_TOKENS via "43114", add USDT)
297
+ # Fantom
298
+ "250": [
299
+ {
300
+ "human_name": "usdt",
301
+ "address": "0x049d68029688eabf473097a2fc38ef61633a3c7a",
302
+ "name": "Frapped USDT",
303
+ "decimals": 6,
304
+ "version": "1",
305
+ }
306
+ ],
307
+ # Celo
308
+ "42220": [
309
+ {
310
+ "human_name": "usdt",
311
+ "address": "0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e",
312
+ "name": "Tether USD",
313
+ "decimals": 18,
314
+ "version": "1",
315
+ }
316
+ ],
317
+ # Kaia (formerly Klaytn)
318
+ "8217": [
319
+ {
320
+ "human_name": "usdt",
321
+ "address": "0xcee8faf64bb97a73bb51e115aa89c17ffa8dd167",
322
+ "name": "Tether USD",
323
+ "decimals": 6,
324
+ "version": "1",
325
+ }
326
+ ],
327
+ }
328
+
329
+
330
+ def get_token_name(chain_id: str, address: str) -> str:
331
+ """Get the token name for a given chain and address"""
332
+ for token in KNOWN_TOKENS[chain_id]:
333
+ if token["address"] == address:
334
+ return token["name"]
335
+ raise ValueError(f"Token not found for chain {chain_id} and address {address}")
336
+
337
+
338
+ def get_token_version(chain_id: str, address: str) -> str:
339
+ """Get the token version for a given chain and address"""
340
+ for token in KNOWN_TOKENS[chain_id]:
341
+ if token["address"] == address:
342
+ return token["version"]
343
+ raise ValueError(f"Token not found for chain {chain_id} and address {address}")
344
+
345
+
346
+ def get_token_decimals(chain_id: str, address: str) -> int:
347
+ """Get the token decimals for a given chain and address"""
348
+ for token in KNOWN_TOKENS[chain_id]:
349
+ if token["address"] == address:
350
+ return token["decimals"]
351
+ raise ValueError(f"Token not found for chain {chain_id} and address {address}")
352
+
353
+
354
+ def get_default_token_address(chain_id: str, token_type: str = "usdc") -> str:
355
+ """Get the default token address for a given chain and token type"""
356
+ for token in KNOWN_TOKENS[chain_id]:
357
+ if token["human_name"] == token_type:
358
+ return token["address"]
359
+ raise ValueError(f"Token type '{token_type}' not found for chain {chain_id}")
@@ -10,6 +10,7 @@ Usage:
10
10
  t402 decode <base64-string>
11
11
  t402 version
12
12
  """
13
+
13
14
  from __future__ import annotations
14
15
 
15
16
  import argparse
@@ -20,7 +21,7 @@ from pathlib import Path
20
21
  from typing import Any
21
22
 
22
23
  from . import __version__
23
- from .encoding import decode_payment, encode_payment
24
+ from .exact import decode_payment, encode_payment
24
25
  from .facilitator import FacilitatorClient, FacilitatorConfig
25
26
  from .types import PaymentPayload
26
27
 
@@ -263,7 +264,16 @@ def cmd_decode(args: argparse.Namespace) -> int:
263
264
 
264
265
  def cmd_info(args: argparse.Namespace) -> int:
265
266
  """Show information about a network."""
266
- from .networks import is_evm_network, is_ton_network, is_tron_network
267
+ from .networks import (
268
+ is_evm_network,
269
+ is_ton_network,
270
+ is_tron_network,
271
+ is_svm_network,
272
+ EVM_NETWORK_TO_CHAIN_ID,
273
+ TON_NETWORKS,
274
+ TRON_NETWORKS,
275
+ SVM_NETWORKS,
276
+ )
267
277
 
268
278
  network = args.network
269
279
 
@@ -272,17 +282,29 @@ def cmd_info(args: argparse.Namespace) -> int:
272
282
  "is_evm": is_evm_network(network),
273
283
  "is_ton": is_ton_network(network),
274
284
  "is_tron": is_tron_network(network),
285
+ "is_svm": is_svm_network(network),
275
286
  }
276
287
 
277
288
  # Add chain-specific info
278
289
  if is_evm_network(network):
279
- from .chains import EVM_CHAINS
280
-
281
- chain_id = network.split(":")[1] if ":" in network else network
282
- if chain_id in EVM_CHAINS:
283
- chain = EVM_CHAINS[chain_id]
284
- info["chain_name"] = chain.get("name", "Unknown")
285
- info["currency"] = chain.get("currency", "Unknown")
290
+ chain_id = EVM_NETWORK_TO_CHAIN_ID.get(network)
291
+ if chain_id:
292
+ info["chain_id"] = chain_id
293
+
294
+ if is_ton_network(network) and network in TON_NETWORKS:
295
+ net_info = TON_NETWORKS[network]
296
+ info["name"] = net_info.get("name", "Unknown")
297
+ info["is_testnet"] = net_info.get("is_testnet", False)
298
+
299
+ if is_tron_network(network) and network in TRON_NETWORKS:
300
+ net_info = TRON_NETWORKS[network]
301
+ info["name"] = net_info.get("name", "Unknown")
302
+ info["is_testnet"] = net_info.get("is_testnet", False)
303
+
304
+ if is_svm_network(network) and network in SVM_NETWORKS:
305
+ net_info = SVM_NETWORKS[network]
306
+ info["name"] = net_info.get("name", "Unknown")
307
+ info["is_testnet"] = net_info.get("is_testnet", False)
286
308
 
287
309
  if args.output == "json":
288
310
  print(json.dumps(info, indent=2))
@@ -34,10 +34,12 @@ def parse_money(amount: str | int, address: str, network: str) -> int:
34
34
  # Handle TON networks differently
35
35
  if is_ton_network(network):
36
36
  from t402.ton import DEFAULT_DECIMALS
37
+
37
38
  decimals = DEFAULT_DECIMALS # USDT on TON uses 6 decimals
38
39
  # Handle TRON networks
39
40
  elif is_tron_network(network):
40
41
  from t402.tron import DEFAULT_DECIMALS
42
+
41
43
  decimals = DEFAULT_DECIMALS # USDT on TRON uses 6 decimals
42
44
  else:
43
45
  chain_id = get_chain_id(network)