t402 1.9.0__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.
Files changed (134) hide show
  1. t402/__init__.py +2 -1
  2. t402/a2a/__init__.py +73 -0
  3. t402/a2a/helpers.py +158 -0
  4. t402/a2a/types.py +145 -0
  5. t402/bridge/client.py +13 -5
  6. t402/bridge/constants.py +4 -2
  7. t402/bridge/router.py +1 -1
  8. t402/bridge/scan.py +3 -1
  9. t402/chains.py +268 -1
  10. t402/cli.py +31 -9
  11. t402/common.py +2 -0
  12. t402/cosmos_paywall_template.py +2 -0
  13. t402/django/__init__.py +42 -0
  14. t402/django/middleware.py +596 -0
  15. t402/encoding.py +9 -3
  16. t402/erc4337/accounts.py +56 -51
  17. t402/erc4337/bundlers.py +105 -99
  18. t402/erc4337/paymasters.py +100 -109
  19. t402/erc4337/types.py +39 -26
  20. t402/errors.py +213 -0
  21. t402/evm_paywall_template.py +1 -1
  22. t402/facilitator.py +125 -0
  23. t402/fastapi/middleware.py +1 -3
  24. t402/mcp/constants.py +3 -6
  25. t402/mcp/server.py +501 -84
  26. t402/mcp/web3_utils.py +493 -0
  27. t402/multisig/__init__.py +120 -0
  28. t402/multisig/constants.py +54 -0
  29. t402/multisig/safe.py +441 -0
  30. t402/multisig/signature.py +228 -0
  31. t402/multisig/transaction.py +238 -0
  32. t402/multisig/types.py +108 -0
  33. t402/multisig/utils.py +77 -0
  34. t402/near_paywall_template.py +2 -0
  35. t402/networks.py +34 -1
  36. t402/paywall.py +1 -3
  37. t402/schemes/__init__.py +143 -0
  38. t402/schemes/aptos/__init__.py +70 -0
  39. t402/schemes/aptos/constants.py +349 -0
  40. t402/schemes/aptos/exact_direct/__init__.py +44 -0
  41. t402/schemes/aptos/exact_direct/client.py +202 -0
  42. t402/schemes/aptos/exact_direct/facilitator.py +426 -0
  43. t402/schemes/aptos/exact_direct/server.py +272 -0
  44. t402/schemes/aptos/types.py +237 -0
  45. t402/schemes/cosmos/__init__.py +114 -0
  46. t402/schemes/cosmos/constants.py +211 -0
  47. t402/schemes/cosmos/exact_direct/__init__.py +21 -0
  48. t402/schemes/cosmos/exact_direct/client.py +198 -0
  49. t402/schemes/cosmos/exact_direct/facilitator.py +493 -0
  50. t402/schemes/cosmos/exact_direct/server.py +315 -0
  51. t402/schemes/cosmos/types.py +501 -0
  52. t402/schemes/evm/__init__.py +46 -1
  53. t402/schemes/evm/exact/__init__.py +11 -0
  54. t402/schemes/evm/exact/client.py +3 -1
  55. t402/schemes/evm/exact/facilitator.py +894 -0
  56. t402/schemes/evm/exact/server.py +1 -1
  57. t402/schemes/evm/exact_legacy/__init__.py +38 -0
  58. t402/schemes/evm/exact_legacy/client.py +291 -0
  59. t402/schemes/evm/exact_legacy/facilitator.py +777 -0
  60. t402/schemes/evm/exact_legacy/server.py +231 -0
  61. t402/schemes/evm/upto/__init__.py +12 -0
  62. t402/schemes/evm/upto/client.py +6 -2
  63. t402/schemes/evm/upto/facilitator.py +625 -0
  64. t402/schemes/evm/upto/server.py +243 -0
  65. t402/schemes/evm/upto/types.py +3 -1
  66. t402/schemes/interfaces.py +6 -2
  67. t402/schemes/near/__init__.py +137 -0
  68. t402/schemes/near/constants.py +189 -0
  69. t402/schemes/near/exact_direct/__init__.py +21 -0
  70. t402/schemes/near/exact_direct/client.py +204 -0
  71. t402/schemes/near/exact_direct/facilitator.py +455 -0
  72. t402/schemes/near/exact_direct/server.py +303 -0
  73. t402/schemes/near/types.py +419 -0
  74. t402/schemes/near/upto/__init__.py +54 -0
  75. t402/schemes/near/upto/types.py +272 -0
  76. t402/schemes/polkadot/__init__.py +72 -0
  77. t402/schemes/polkadot/constants.py +155 -0
  78. t402/schemes/polkadot/exact_direct/__init__.py +43 -0
  79. t402/schemes/polkadot/exact_direct/client.py +235 -0
  80. t402/schemes/polkadot/exact_direct/facilitator.py +428 -0
  81. t402/schemes/polkadot/exact_direct/server.py +292 -0
  82. t402/schemes/polkadot/types.py +385 -0
  83. t402/schemes/registry.py +6 -2
  84. t402/schemes/stacks/__init__.py +68 -0
  85. t402/schemes/stacks/constants.py +122 -0
  86. t402/schemes/stacks/exact_direct/__init__.py +43 -0
  87. t402/schemes/stacks/exact_direct/client.py +222 -0
  88. t402/schemes/stacks/exact_direct/facilitator.py +424 -0
  89. t402/schemes/stacks/exact_direct/server.py +292 -0
  90. t402/schemes/stacks/types.py +380 -0
  91. t402/schemes/svm/__init__.py +44 -0
  92. t402/schemes/svm/exact/__init__.py +35 -0
  93. t402/schemes/svm/exact/client.py +23 -0
  94. t402/schemes/svm/exact/facilitator.py +24 -0
  95. t402/schemes/svm/exact/server.py +20 -0
  96. t402/schemes/svm/upto/__init__.py +23 -0
  97. t402/schemes/svm/upto/types.py +193 -0
  98. t402/schemes/tezos/__init__.py +84 -0
  99. t402/schemes/tezos/constants.py +372 -0
  100. t402/schemes/tezos/exact_direct/__init__.py +22 -0
  101. t402/schemes/tezos/exact_direct/client.py +226 -0
  102. t402/schemes/tezos/exact_direct/facilitator.py +491 -0
  103. t402/schemes/tezos/exact_direct/server.py +277 -0
  104. t402/schemes/tezos/types.py +220 -0
  105. t402/schemes/ton/__init__.py +24 -2
  106. t402/schemes/ton/exact/__init__.py +7 -0
  107. t402/schemes/ton/exact/facilitator.py +730 -0
  108. t402/schemes/ton/exact/server.py +1 -1
  109. t402/schemes/ton/upto/__init__.py +31 -0
  110. t402/schemes/ton/upto/types.py +215 -0
  111. t402/schemes/tron/__init__.py +28 -2
  112. t402/schemes/tron/exact/__init__.py +9 -0
  113. t402/schemes/tron/exact/facilitator.py +673 -0
  114. t402/schemes/tron/exact/server.py +1 -1
  115. t402/schemes/tron/upto/__init__.py +30 -0
  116. t402/schemes/tron/upto/types.py +213 -0
  117. t402/stacks_paywall_template.py +2 -0
  118. t402/starlette/__init__.py +38 -0
  119. t402/starlette/middleware.py +522 -0
  120. t402/svm.py +45 -11
  121. t402/svm_paywall_template.py +1 -1
  122. t402/ton.py +6 -2
  123. t402/ton_paywall_template.py +1 -192
  124. t402/tron.py +2 -0
  125. t402/tron_paywall_template.py +2 -0
  126. t402/types.py +103 -3
  127. t402/wdk/chains.py +1 -1
  128. t402/wdk/errors.py +15 -5
  129. t402/wdk/signer.py +11 -2
  130. {t402-1.9.0.dist-info → t402-1.10.0.dist-info}/METADATA +42 -1
  131. t402-1.10.0.dist-info/RECORD +156 -0
  132. t402-1.9.0.dist-info/RECORD +0 -72
  133. {t402-1.9.0.dist-info → t402-1.10.0.dist-info}/WHEEL +0 -0
  134. {t402-1.9.0.dist-info → t402-1.10.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,68 @@
1
+ """Stacks Blockchain Payment Schemes.
2
+
3
+ This package provides payment scheme implementations for Stacks (Bitcoin L2).
4
+
5
+ Supported schemes:
6
+ - exact-direct: SIP-010 token transfers verified on-chain
7
+
8
+ Supported networks:
9
+ - stacks:1 (Stacks Mainnet)
10
+ - stacks:2147483648 (Stacks Testnet)
11
+ """
12
+
13
+ from t402.schemes.stacks.exact_direct import (
14
+ ExactDirectStacksClientScheme,
15
+ ExactDirectStacksServerScheme,
16
+ ExactDirectStacksFacilitatorScheme,
17
+ ClientStacksSigner,
18
+ FacilitatorStacksSigner,
19
+ SCHEME_EXACT_DIRECT,
20
+ )
21
+ from t402.schemes.stacks.constants import (
22
+ STACKS_MAINNET_CAIP2,
23
+ STACKS_TESTNET_CAIP2,
24
+ SUSDC_DECIMALS,
25
+ NETWORKS,
26
+ get_network_config,
27
+ get_supported_networks,
28
+ is_stacks_network,
29
+ )
30
+ from t402.schemes.stacks.types import (
31
+ ExactDirectPayload,
32
+ TransactionResult,
33
+ ParsedTokenTransfer,
34
+ is_valid_stacks_address,
35
+ is_valid_tx_id,
36
+ parse_contract_identifier,
37
+ create_asset_identifier,
38
+ extract_token_transfer,
39
+ )
40
+
41
+ __all__ = [
42
+ # Schemes
43
+ "ExactDirectStacksClientScheme",
44
+ "ExactDirectStacksServerScheme",
45
+ "ExactDirectStacksFacilitatorScheme",
46
+ # Signer protocols
47
+ "ClientStacksSigner",
48
+ "FacilitatorStacksSigner",
49
+ # Constants
50
+ "SCHEME_EXACT_DIRECT",
51
+ "STACKS_MAINNET_CAIP2",
52
+ "STACKS_TESTNET_CAIP2",
53
+ "SUSDC_DECIMALS",
54
+ "NETWORKS",
55
+ # Functions
56
+ "get_network_config",
57
+ "get_supported_networks",
58
+ "is_stacks_network",
59
+ # Types
60
+ "ExactDirectPayload",
61
+ "TransactionResult",
62
+ "ParsedTokenTransfer",
63
+ "is_valid_stacks_address",
64
+ "is_valid_tx_id",
65
+ "parse_contract_identifier",
66
+ "create_asset_identifier",
67
+ "extract_token_transfer",
68
+ ]
@@ -0,0 +1,122 @@
1
+ """Stacks Scheme Constants.
2
+
3
+ This module defines constants for the Stacks exact-direct payment scheme,
4
+ including network identifiers, token configurations, and default endpoints.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from dataclasses import dataclass
10
+ from typing import Dict
11
+
12
+
13
+ # Scheme identifier
14
+ SCHEME_EXACT_DIRECT = "exact-direct"
15
+
16
+ # CAIP-2 network identifiers for Stacks
17
+ STACKS_MAINNET_CAIP2 = "stacks:1"
18
+ STACKS_TESTNET_CAIP2 = "stacks:2147483648"
19
+
20
+ # Default Hiro API endpoints
21
+ STACKS_MAINNET_API = "https://api.hiro.so"
22
+ STACKS_TESTNET_API = "https://api.testnet.hiro.so"
23
+
24
+ # sUSDC token decimals
25
+ SUSDC_DECIMALS = 6
26
+
27
+
28
+ @dataclass(frozen=True)
29
+ class TokenInfo:
30
+ """Token configuration for a Stacks asset."""
31
+
32
+ contract_address: str
33
+ symbol: str
34
+ name: str
35
+ decimals: int
36
+
37
+
38
+ @dataclass(frozen=True)
39
+ class NetworkConfig:
40
+ """Configuration for a Stacks network."""
41
+
42
+ name: str
43
+ caip2: str
44
+ api_url: str
45
+ chain_id: int
46
+ is_testnet: bool
47
+ default_token: TokenInfo
48
+
49
+
50
+ # Default token configurations
51
+ SUSDC_MAINNET = TokenInfo(
52
+ contract_address="SP3Y2ZSH8P7D50B0VBTSX11S7XSG24M1VB9YFQA4K.token-susdc",
53
+ symbol="sUSDC",
54
+ name="Stacks USDC",
55
+ decimals=SUSDC_DECIMALS,
56
+ )
57
+
58
+ SUSDC_TESTNET = TokenInfo(
59
+ contract_address="ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token-susdc",
60
+ symbol="sUSDC",
61
+ name="Test Stacks USDC",
62
+ decimals=SUSDC_DECIMALS,
63
+ )
64
+
65
+ # Network configurations indexed by CAIP-2 identifier
66
+ NETWORKS: Dict[str, NetworkConfig] = {
67
+ STACKS_MAINNET_CAIP2: NetworkConfig(
68
+ name="Stacks Mainnet",
69
+ caip2=STACKS_MAINNET_CAIP2,
70
+ api_url=STACKS_MAINNET_API,
71
+ chain_id=1,
72
+ is_testnet=False,
73
+ default_token=SUSDC_MAINNET,
74
+ ),
75
+ STACKS_TESTNET_CAIP2: NetworkConfig(
76
+ name="Stacks Testnet",
77
+ caip2=STACKS_TESTNET_CAIP2,
78
+ api_url=STACKS_TESTNET_API,
79
+ chain_id=2147483648,
80
+ is_testnet=True,
81
+ default_token=SUSDC_TESTNET,
82
+ ),
83
+ }
84
+
85
+
86
+ def get_network_config(network: str) -> NetworkConfig:
87
+ """Get the network configuration for a CAIP-2 identifier.
88
+
89
+ Args:
90
+ network: CAIP-2 network identifier (e.g., "stacks:1")
91
+
92
+ Returns:
93
+ NetworkConfig for the given network
94
+
95
+ Raises:
96
+ ValueError: If the network is not supported
97
+ """
98
+ config = NETWORKS.get(network)
99
+ if config is None:
100
+ raise ValueError(f"Unsupported Stacks network: {network}")
101
+ return config
102
+
103
+
104
+ def is_stacks_network(network: str) -> bool:
105
+ """Check if a network identifier is a Stacks network.
106
+
107
+ Args:
108
+ network: Network identifier to check
109
+
110
+ Returns:
111
+ True if the network starts with "stacks:"
112
+ """
113
+ return network.startswith("stacks:")
114
+
115
+
116
+ def get_supported_networks() -> list:
117
+ """Get all supported Stacks network identifiers.
118
+
119
+ Returns:
120
+ List of CAIP-2 network identifiers
121
+ """
122
+ return list(NETWORKS.keys())
@@ -0,0 +1,43 @@
1
+ """Stacks Exact-Direct Payment Scheme.
2
+
3
+ This package provides the exact-direct payment scheme implementation for
4
+ Stacks (Bitcoin L2) networks using SIP-010 token transfers.
5
+
6
+ The exact-direct scheme works by:
7
+ 1. Client executes a SIP-010 token transfer on-chain
8
+ 2. Client returns the transaction ID as proof
9
+ 3. Facilitator verifies the transfer on-chain via Hiro API
10
+
11
+ This is a "direct" scheme because the client pays on-chain before
12
+ submitting the payment proof, rather than providing an off-chain signature
13
+ for later settlement.
14
+ """
15
+
16
+ from t402.schemes.stacks.exact_direct.client import (
17
+ ExactDirectStacksClientScheme,
18
+ )
19
+ from t402.schemes.stacks.exact_direct.server import (
20
+ ExactDirectStacksServerScheme,
21
+ )
22
+ from t402.schemes.stacks.exact_direct.facilitator import (
23
+ ExactDirectStacksFacilitatorScheme,
24
+ )
25
+ from t402.schemes.stacks.constants import SCHEME_EXACT_DIRECT
26
+ from t402.schemes.stacks.types import (
27
+ ClientStacksSigner,
28
+ FacilitatorStacksSigner,
29
+ )
30
+
31
+ __all__ = [
32
+ # Client
33
+ "ExactDirectStacksClientScheme",
34
+ # Server
35
+ "ExactDirectStacksServerScheme",
36
+ # Facilitator
37
+ "ExactDirectStacksFacilitatorScheme",
38
+ # Signer protocols
39
+ "ClientStacksSigner",
40
+ "FacilitatorStacksSigner",
41
+ # Constants
42
+ "SCHEME_EXACT_DIRECT",
43
+ ]
@@ -0,0 +1,222 @@
1
+ """Stacks Exact-Direct Scheme - Client Implementation.
2
+
3
+ This module provides the client-side implementation of the exact-direct
4
+ payment scheme for Stacks (Bitcoin L2) networks.
5
+
6
+ The client:
7
+ 1. Builds a SIP-010 token transfer contract call
8
+ 2. Signs and submits it on-chain via the signer
9
+ 3. Returns the transaction ID as payment proof
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import logging
15
+ from typing import Any, Dict, Union
16
+
17
+ from t402.types import (
18
+ PaymentRequirementsV2,
19
+ T402_VERSION_V1,
20
+ T402_VERSION_V2,
21
+ )
22
+ from t402.schemes.stacks.constants import (
23
+ SCHEME_EXACT_DIRECT,
24
+ get_network_config,
25
+ is_stacks_network,
26
+ )
27
+ from t402.schemes.stacks.types import (
28
+ ClientStacksSigner,
29
+ ExactDirectPayload,
30
+ is_valid_stacks_address,
31
+ is_valid_tx_id,
32
+ parse_contract_identifier,
33
+ )
34
+
35
+
36
+ logger = logging.getLogger(__name__)
37
+
38
+
39
+ class ExactDirectStacksClientScheme:
40
+ """Client scheme for Stacks exact-direct payments.
41
+
42
+ Executes on-chain SIP-010 token transfers and returns the transaction
43
+ ID as a payment payload.
44
+
45
+ Example:
46
+ ```python
47
+ scheme = ExactDirectStacksClientScheme(signer=my_stacks_signer)
48
+
49
+ payload = await scheme.create_payment_payload(
50
+ t402_version=2,
51
+ requirements={
52
+ "scheme": "exact-direct",
53
+ "network": "stacks:1",
54
+ "asset": "stacks:1/token:SP3Y2ZSH8P7D50B0VBTSX11S7XSG24M1VB9YFQA4K.token-susdc",
55
+ "amount": "1000000",
56
+ "payTo": "SP3Y2ZSH8P7D50B0VBTSX11S7XSG24M1VB9YFQA4K",
57
+ "maxTimeoutSeconds": 300,
58
+ },
59
+ )
60
+ ```
61
+ """
62
+
63
+ scheme = SCHEME_EXACT_DIRECT
64
+ caip_family = "stacks:*"
65
+
66
+ def __init__(
67
+ self,
68
+ signer: ClientStacksSigner,
69
+ ):
70
+ """Initialize the Stacks client scheme.
71
+
72
+ Args:
73
+ signer: Stacks signer for signing and submitting token transfers
74
+ """
75
+ self._signer = signer
76
+
77
+ @property
78
+ def address(self) -> str:
79
+ """Return the signer's Stacks address."""
80
+ return self._signer.address
81
+
82
+ async def create_payment_payload(
83
+ self,
84
+ t402_version: int,
85
+ requirements: Union[PaymentRequirementsV2, Dict[str, Any]],
86
+ ) -> Dict[str, Any]:
87
+ """Create a payment payload by executing an on-chain token transfer.
88
+
89
+ Validates the requirements, resolves the contract address, calls
90
+ the signer to execute the transfer, then returns the proof.
91
+
92
+ Args:
93
+ t402_version: Protocol version (1 or 2)
94
+ requirements: Payment requirements specifying the transfer details
95
+
96
+ Returns:
97
+ Payment payload dictionary with transaction proof
98
+
99
+ Raises:
100
+ ValueError: If requirements are invalid (bad network, address, amount, etc.)
101
+ Exception: If signing or submission fails
102
+ """
103
+ # Convert to dict for easier access
104
+ if hasattr(requirements, "model_dump"):
105
+ req = requirements.model_dump(by_alias=True)
106
+ else:
107
+ req = dict(requirements)
108
+
109
+ # Extract fields
110
+ network = req.get("network", "")
111
+ asset = req.get("asset", "")
112
+ amount = req.get("amount", "0")
113
+ pay_to = req.get("payTo", "")
114
+ extra = req.get("extra", {})
115
+
116
+ # Validate network
117
+ if not is_stacks_network(network):
118
+ raise ValueError(f"Unsupported network: {network}")
119
+
120
+ network_config = get_network_config(network)
121
+
122
+ # Validate payTo address
123
+ if not pay_to:
124
+ raise ValueError("payTo address is required")
125
+ if not is_valid_stacks_address(pay_to):
126
+ raise ValueError(f"Invalid payTo address: {pay_to}")
127
+
128
+ # Validate amount
129
+ if not amount:
130
+ raise ValueError("Amount is required")
131
+ try:
132
+ amount_int = int(amount)
133
+ except (ValueError, TypeError):
134
+ raise ValueError(f"Invalid amount format: {amount}")
135
+ if amount_int <= 0:
136
+ raise ValueError(f"Amount must be positive: {amount}")
137
+
138
+ # Resolve contract address
139
+ contract_address = self._resolve_contract_address(
140
+ asset, extra, network_config
141
+ )
142
+
143
+ # Get sender address
144
+ from_address = self._signer.address
145
+ if not from_address:
146
+ raise ValueError("Signer address is empty")
147
+
148
+ # Execute the token transfer
149
+ tx_id = await self._signer.transfer_token(
150
+ contract_address=contract_address,
151
+ to=pay_to,
152
+ amount=amount_int,
153
+ )
154
+
155
+ # Validate result
156
+ if not tx_id:
157
+ raise ValueError("Transfer returned empty transaction ID")
158
+
159
+ if not is_valid_tx_id(tx_id):
160
+ raise ValueError(f"Invalid transaction ID format: {tx_id}")
161
+
162
+ # Build the payload
163
+ payload = ExactDirectPayload(
164
+ tx_id=tx_id,
165
+ from_address=from_address,
166
+ to_address=pay_to,
167
+ amount=amount,
168
+ contract_address=contract_address,
169
+ )
170
+
171
+ if t402_version == T402_VERSION_V1:
172
+ return {
173
+ "t402Version": T402_VERSION_V1,
174
+ "scheme": self.scheme,
175
+ "network": network,
176
+ "payload": payload.to_dict(),
177
+ }
178
+
179
+ # V2 format
180
+ return {
181
+ "t402Version": T402_VERSION_V2,
182
+ "payload": payload.to_dict(),
183
+ }
184
+
185
+ def _resolve_contract_address(
186
+ self,
187
+ asset: str,
188
+ extra: Dict[str, Any],
189
+ network_config: Any,
190
+ ) -> str:
191
+ """Resolve the contract address from requirements fields.
192
+
193
+ Tries to determine the contract address from:
194
+ 1. The extra.contractAddress field
195
+ 2. The CAIP-19 asset identifier
196
+ 3. The network's default token
197
+
198
+ Args:
199
+ asset: CAIP-19 asset identifier string
200
+ extra: Extra metadata from requirements
201
+ network_config: Network configuration
202
+
203
+ Returns:
204
+ Resolved contract address
205
+
206
+ Raises:
207
+ ValueError: If contract address cannot be determined
208
+ """
209
+ # Try extra.contractAddress first
210
+ if extra and "contractAddress" in extra:
211
+ contract_val = extra["contractAddress"]
212
+ if isinstance(contract_val, str) and contract_val:
213
+ return contract_val
214
+
215
+ # Try parsing CAIP-19 asset identifier
216
+ if asset:
217
+ parsed = parse_contract_identifier(asset)
218
+ if parsed is not None:
219
+ return parsed
220
+
221
+ # Fall back to network default
222
+ return network_config.default_token.contract_address