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.
Files changed (100) hide show
  1. t402/__init__.py +2 -1
  2. t402/bridge/client.py +13 -5
  3. t402/bridge/constants.py +3 -1
  4. t402/bridge/router.py +1 -1
  5. t402/bridge/scan.py +3 -1
  6. t402/chains.py +268 -1
  7. t402/cli.py +31 -9
  8. t402/common.py +2 -0
  9. t402/cosmos_paywall_template.py +2 -0
  10. t402/encoding.py +9 -3
  11. t402/erc4337/accounts.py +56 -51
  12. t402/erc4337/bundlers.py +105 -99
  13. t402/erc4337/paymasters.py +100 -109
  14. t402/erc4337/types.py +39 -26
  15. t402/evm_paywall_template.py +1 -1
  16. t402/fastapi/middleware.py +1 -3
  17. t402/mcp/server.py +79 -46
  18. t402/near_paywall_template.py +2 -0
  19. t402/networks.py +34 -1
  20. t402/paywall.py +1 -3
  21. t402/schemes/__init__.py +124 -0
  22. t402/schemes/aptos/__init__.py +70 -0
  23. t402/schemes/aptos/constants.py +349 -0
  24. t402/schemes/aptos/exact_direct/__init__.py +44 -0
  25. t402/schemes/aptos/exact_direct/client.py +202 -0
  26. t402/schemes/aptos/exact_direct/facilitator.py +426 -0
  27. t402/schemes/aptos/exact_direct/server.py +272 -0
  28. t402/schemes/aptos/types.py +237 -0
  29. t402/schemes/evm/__init__.py +46 -1
  30. t402/schemes/evm/exact/__init__.py +11 -0
  31. t402/schemes/evm/exact/client.py +3 -1
  32. t402/schemes/evm/exact/facilitator.py +894 -0
  33. t402/schemes/evm/exact/server.py +1 -1
  34. t402/schemes/evm/exact_legacy/__init__.py +38 -0
  35. t402/schemes/evm/exact_legacy/client.py +291 -0
  36. t402/schemes/evm/exact_legacy/facilitator.py +777 -0
  37. t402/schemes/evm/exact_legacy/server.py +231 -0
  38. t402/schemes/evm/upto/__init__.py +12 -0
  39. t402/schemes/evm/upto/client.py +6 -2
  40. t402/schemes/evm/upto/facilitator.py +625 -0
  41. t402/schemes/evm/upto/server.py +243 -0
  42. t402/schemes/evm/upto/types.py +3 -1
  43. t402/schemes/interfaces.py +6 -2
  44. t402/schemes/near/__init__.py +112 -0
  45. t402/schemes/near/constants.py +189 -0
  46. t402/schemes/near/exact_direct/__init__.py +21 -0
  47. t402/schemes/near/exact_direct/client.py +204 -0
  48. t402/schemes/near/exact_direct/facilitator.py +455 -0
  49. t402/schemes/near/exact_direct/server.py +303 -0
  50. t402/schemes/near/types.py +419 -0
  51. t402/schemes/polkadot/__init__.py +72 -0
  52. t402/schemes/polkadot/constants.py +155 -0
  53. t402/schemes/polkadot/exact_direct/__init__.py +43 -0
  54. t402/schemes/polkadot/exact_direct/client.py +235 -0
  55. t402/schemes/polkadot/exact_direct/facilitator.py +428 -0
  56. t402/schemes/polkadot/exact_direct/server.py +292 -0
  57. t402/schemes/polkadot/types.py +385 -0
  58. t402/schemes/registry.py +6 -2
  59. t402/schemes/stacks/__init__.py +68 -0
  60. t402/schemes/stacks/constants.py +122 -0
  61. t402/schemes/stacks/exact_direct/__init__.py +43 -0
  62. t402/schemes/stacks/exact_direct/client.py +222 -0
  63. t402/schemes/stacks/exact_direct/facilitator.py +424 -0
  64. t402/schemes/stacks/exact_direct/server.py +292 -0
  65. t402/schemes/stacks/types.py +380 -0
  66. t402/schemes/svm/__init__.py +29 -0
  67. t402/schemes/svm/exact/__init__.py +35 -0
  68. t402/schemes/svm/exact/client.py +23 -0
  69. t402/schemes/svm/exact/facilitator.py +24 -0
  70. t402/schemes/svm/exact/server.py +20 -0
  71. t402/schemes/tezos/__init__.py +84 -0
  72. t402/schemes/tezos/constants.py +372 -0
  73. t402/schemes/tezos/exact_direct/__init__.py +22 -0
  74. t402/schemes/tezos/exact_direct/client.py +226 -0
  75. t402/schemes/tezos/exact_direct/facilitator.py +491 -0
  76. t402/schemes/tezos/exact_direct/server.py +277 -0
  77. t402/schemes/tezos/types.py +220 -0
  78. t402/schemes/ton/__init__.py +9 -2
  79. t402/schemes/ton/exact/__init__.py +7 -0
  80. t402/schemes/ton/exact/facilitator.py +730 -0
  81. t402/schemes/ton/exact/server.py +1 -1
  82. t402/schemes/tron/__init__.py +11 -2
  83. t402/schemes/tron/exact/__init__.py +9 -0
  84. t402/schemes/tron/exact/facilitator.py +673 -0
  85. t402/schemes/tron/exact/server.py +1 -1
  86. t402/stacks_paywall_template.py +2 -0
  87. t402/svm.py +45 -11
  88. t402/svm_paywall_template.py +1 -1
  89. t402/ton.py +5 -1
  90. t402/ton_paywall_template.py +1 -192
  91. t402/tron.py +2 -0
  92. t402/tron_paywall_template.py +2 -0
  93. t402/types.py +3 -1
  94. t402/wdk/errors.py +15 -5
  95. t402/wdk/signer.py +11 -2
  96. {t402-1.9.0.dist-info → t402-1.9.1.dist-info}/METADATA +42 -1
  97. t402-1.9.1.dist-info/RECORD +125 -0
  98. t402-1.9.0.dist-info/RECORD +0 -72
  99. {t402-1.9.0.dist-info → t402-1.9.1.dist-info}/WHEEL +0 -0
  100. {t402-1.9.0.dist-info → t402-1.9.1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,277 @@
1
+ """Tezos Exact-Direct Scheme - Server Implementation.
2
+
3
+ This module provides the server-side implementation of the exact-direct payment
4
+ scheme for Tezos. It handles price parsing (converting user-friendly prices to
5
+ atomic units) and enhancement of payment requirements with Tezos-specific
6
+ FA2 asset information (CAIP-19 identifiers, token metadata).
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import logging
12
+ from decimal import Decimal
13
+ from typing import Any, Dict, List, Optional, Union
14
+
15
+ from t402.types import (
16
+ PaymentRequirementsV2,
17
+ Network,
18
+ )
19
+ from t402.schemes.interfaces import AssetAmount, SupportedKindDict
20
+ from t402.schemes.tezos.constants import (
21
+ SCHEME_EXACT_DIRECT,
22
+ is_tezos_network,
23
+ get_network_config,
24
+ get_token_info,
25
+ create_asset_identifier,
26
+ parse_decimal_to_atomic,
27
+ TokenInfo,
28
+ )
29
+
30
+
31
+ logger = logging.getLogger(__name__)
32
+
33
+
34
+ class ExactDirectTezosServer:
35
+ """Server scheme for Tezos exact-direct payments.
36
+
37
+ Handles parsing user-friendly prices (e.g., "$1.50") into atomic amounts
38
+ and enhancing payment requirements with Tezos-specific metadata including
39
+ CAIP-19 asset identifiers and token information.
40
+
41
+ Example:
42
+ ```python
43
+ from t402.schemes.tezos import ExactDirectTezosServer
44
+
45
+ server = ExactDirectTezosServer()
46
+
47
+ # Parse price to atomic units
48
+ asset_amount = await server.parse_price("$0.10", "tezos:NetXdQprcVkpaWU")
49
+ # Returns: {
50
+ # "amount": "100000",
51
+ # "asset": "tezos:NetXdQprcVkpaWU/fa2:KT1XnTn74.../0",
52
+ # "extra": {"symbol": "USDt", "name": "Tether USD", "decimals": 6}
53
+ # }
54
+
55
+ # Enhance requirements with token metadata
56
+ enhanced = await server.enhance_requirements(
57
+ requirements, supported_kind, []
58
+ )
59
+ ```
60
+ """
61
+
62
+ scheme = SCHEME_EXACT_DIRECT
63
+ caip_family = "tezos:*"
64
+
65
+ def __init__(self, preferred_token: Optional[str] = None):
66
+ """Initialize the Tezos exact-direct server.
67
+
68
+ Args:
69
+ preferred_token: Preferred token symbol (e.g., "USDt").
70
+ If set, this token will be used for price conversions
71
+ when available on the network. Defaults to the network's
72
+ default token.
73
+ """
74
+ self._preferred_token = preferred_token
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
+ Supports:
84
+ - String with $ prefix: "$0.10" -> 100000 (6 decimals)
85
+ - String without prefix: "0.10" -> 100000
86
+ - Integer/float: 0.10 -> 100000
87
+ - Dict (AssetAmount): {"amount": "100000", "asset": "tezos:.../fa2:KT1.../0"}
88
+
89
+ Args:
90
+ price: User-friendly price
91
+ network: Network identifier (CAIP-2 format, e.g., "tezos:NetXdQprcVkpaWU")
92
+
93
+ Returns:
94
+ AssetAmount dict with amount (atomic), asset (CAIP-19), and extra metadata
95
+
96
+ Raises:
97
+ ValueError: If network is unsupported or price format is invalid
98
+ """
99
+ # Validate network
100
+ if not is_tezos_network(network):
101
+ raise ValueError(f"Invalid Tezos network: {network}")
102
+
103
+ # Handle dict (already in AssetAmount format)
104
+ if isinstance(price, dict):
105
+ amount_str = str(price.get("amount", "0"))
106
+ asset = price.get("asset", "")
107
+ if not asset:
108
+ raise ValueError(
109
+ f"Asset must be specified for AssetAmount on network {network}"
110
+ )
111
+ return {
112
+ "amount": amount_str,
113
+ "asset": asset,
114
+ "extra": price.get("extra", {}),
115
+ }
116
+
117
+ # Get default token for the network
118
+ token = self._get_default_token(network)
119
+ if token is None:
120
+ raise ValueError(f"No token configured for network {network}")
121
+
122
+ # Parse price string/number to decimal
123
+ amount_decimal = self._parse_money_to_decimal(price)
124
+
125
+ # Convert to atomic units
126
+ atomic_amount = int(amount_decimal * Decimal(10**token.decimals))
127
+
128
+ # Build CAIP-19 asset identifier
129
+ asset_id = create_asset_identifier(network, token.contract_address, token.token_id)
130
+
131
+ # Build extra metadata
132
+ extra: Dict[str, Any] = {
133
+ "symbol": token.symbol,
134
+ "name": token.name,
135
+ "decimals": token.decimals,
136
+ "tokenId": token.token_id,
137
+ }
138
+
139
+ return {
140
+ "amount": str(atomic_amount),
141
+ "asset": asset_id,
142
+ "extra": extra,
143
+ }
144
+
145
+ async def enhance_requirements(
146
+ self,
147
+ requirements: Union[PaymentRequirementsV2, Dict[str, Any]],
148
+ supported_kind: SupportedKindDict,
149
+ facilitator_extensions: List[str],
150
+ ) -> Union[PaymentRequirementsV2, Dict[str, Any]]:
151
+ """Enhance payment requirements with Tezos-specific metadata.
152
+
153
+ Adds FA2 token information to the extra field, resolves CAIP-19 asset
154
+ identifiers, and converts decimal amounts to atomic units if needed.
155
+
156
+ Args:
157
+ requirements: Base payment requirements with amount/asset set
158
+ supported_kind: Matched SupportedKind from the facilitator
159
+ facilitator_extensions: Extension keys supported by facilitator
160
+
161
+ Returns:
162
+ Enhanced requirements dict with Tezos metadata in extra field
163
+ """
164
+ # Convert to dict for modification
165
+ if hasattr(requirements, "model_dump"):
166
+ req = requirements.model_dump(by_alias=True)
167
+ else:
168
+ req = dict(requirements)
169
+
170
+ network = req.get("network", "")
171
+
172
+ # Validate network
173
+ if not is_tezos_network(network):
174
+ raise ValueError(f"Invalid Tezos network: {network}")
175
+
176
+ # Get network config
177
+ network_config = get_network_config(network)
178
+ if network_config is None:
179
+ raise ValueError(f"Unsupported Tezos network: {network}")
180
+
181
+ # Resolve token info
182
+ token = self._get_default_token(network)
183
+
184
+ # Ensure asset is in CAIP-19 format
185
+ if not req.get("asset") and token is not None:
186
+ req["asset"] = create_asset_identifier(
187
+ network, token.contract_address, token.token_id
188
+ )
189
+
190
+ # Convert decimal amount to atomic units if needed
191
+ amount = req.get("amount", "")
192
+ if amount and "." in amount and token is not None:
193
+ req["amount"] = parse_decimal_to_atomic(amount, token.decimals)
194
+
195
+ # Ensure extra exists
196
+ if "extra" not in req or req["extra"] is None:
197
+ req["extra"] = {}
198
+
199
+ # Add token metadata to extra
200
+ if token is not None:
201
+ if "assetSymbol" not in req["extra"]:
202
+ req["extra"]["assetSymbol"] = token.symbol
203
+ if "assetDecimals" not in req["extra"]:
204
+ req["extra"]["assetDecimals"] = token.decimals
205
+ if "assetName" not in req["extra"]:
206
+ req["extra"]["assetName"] = token.name
207
+
208
+ # Add network name for convenience
209
+ if "networkName" not in req["extra"]:
210
+ req["extra"]["networkName"] = network_config.name
211
+
212
+ # Copy extensions from supportedKind if provided
213
+ if supported_kind.get("extra"):
214
+ for key, value in supported_kind["extra"].items():
215
+ if key not in req["extra"]:
216
+ req["extra"][key] = value
217
+
218
+ return req
219
+
220
+ def _get_default_token(self, network: str) -> Optional[TokenInfo]:
221
+ """Get the default token for a given network.
222
+
223
+ If a preferred token is configured, tries to use it first.
224
+ Falls back to the network's default token.
225
+
226
+ Args:
227
+ network: CAIP-2 network identifier
228
+
229
+ Returns:
230
+ TokenInfo if available, None otherwise
231
+ """
232
+ # If a preferred token is configured, try to use it
233
+ if self._preferred_token:
234
+ token = get_token_info(network, self._preferred_token)
235
+ if token is not None:
236
+ return token
237
+
238
+ # Use the network's default token
239
+ config = get_network_config(network)
240
+ if config is None:
241
+ return None
242
+ return config.default_token
243
+
244
+ def _parse_money_to_decimal(self, price: Union[str, int, float]) -> Decimal:
245
+ """Parse a money value to a Decimal amount.
246
+
247
+ Handles currency symbols, whitespace, and common suffixes.
248
+
249
+ Args:
250
+ price: Price as string, int, or float
251
+
252
+ Returns:
253
+ Decimal amount
254
+
255
+ Raises:
256
+ ValueError: If the price format cannot be parsed
257
+ """
258
+ if isinstance(price, (int, float)):
259
+ return Decimal(str(price))
260
+
261
+ if isinstance(price, str):
262
+ clean_price = price.strip()
263
+ # Remove currency prefix
264
+ if clean_price.startswith("$"):
265
+ clean_price = clean_price[1:]
266
+ # Remove common suffixes
267
+ for suffix in (" USD", " USDT", " USDt"):
268
+ if clean_price.endswith(suffix):
269
+ clean_price = clean_price[: -len(suffix)]
270
+ clean_price = clean_price.strip()
271
+
272
+ try:
273
+ return Decimal(clean_price)
274
+ except Exception:
275
+ raise ValueError(f"Failed to parse price string: {price}")
276
+
277
+ raise ValueError(f"Unsupported price type: {type(price)}")
@@ -0,0 +1,220 @@
1
+ """Tezos Scheme Type Definitions.
2
+
3
+ This module defines the Pydantic models and Protocol interfaces used by the
4
+ Tezos exact-direct payment scheme.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import Any, Dict, List, Protocol, runtime_checkable
10
+
11
+ from pydantic import BaseModel, ConfigDict, Field, field_validator
12
+ from pydantic.alias_generators import to_camel
13
+
14
+
15
+ @runtime_checkable
16
+ class ClientTezosSigner(Protocol):
17
+ """Protocol for Tezos client-side signing operations.
18
+
19
+ Implementations are responsible for managing private keys, constructing
20
+ FA2 transfer operations, signing, and injecting them into the Tezos network.
21
+
22
+ Example implementation:
23
+ ```python
24
+ class MyTezosSigner:
25
+ def __init__(self, private_key: str, rpc_url: str):
26
+ self._key = private_key
27
+ self._rpc_url = rpc_url
28
+
29
+ def address(self) -> str:
30
+ return "tz1..."
31
+
32
+ async def transfer_fa2(
33
+ self,
34
+ contract: str,
35
+ token_id: int,
36
+ to: str,
37
+ amount: int,
38
+ network: str,
39
+ ) -> str:
40
+ # Build FA2 transfer, sign and inject
41
+ return "o..." # operation hash
42
+ ```
43
+ """
44
+
45
+ def address(self) -> str:
46
+ """Return the Tezos address (tz1/tz2/tz3) of the signer."""
47
+ ...
48
+
49
+ async def transfer_fa2(
50
+ self,
51
+ contract: str,
52
+ token_id: int,
53
+ to: str,
54
+ amount: int,
55
+ network: str,
56
+ ) -> str:
57
+ """Execute an FA2 transfer operation on-chain.
58
+
59
+ Constructs the FA2 transfer call parameter, signs the operation,
60
+ and injects it into the Tezos network.
61
+
62
+ Args:
63
+ contract: The FA2 contract address (KT1...)
64
+ token_id: The token ID within the FA2 contract
65
+ to: Recipient Tezos address
66
+ amount: Amount in atomic units (integer)
67
+ network: CAIP-2 network identifier
68
+
69
+ Returns:
70
+ The operation hash (starts with 'o', 51 characters)
71
+
72
+ Raises:
73
+ Exception: If the transfer fails
74
+ """
75
+ ...
76
+
77
+
78
+ @runtime_checkable
79
+ class FacilitatorTezosSigner(Protocol):
80
+ """Protocol for Tezos facilitator-side operations.
81
+
82
+ Implementations query the Tezos blockchain (via RPC or indexer)
83
+ to verify operation status and details.
84
+
85
+ Example implementation:
86
+ ```python
87
+ class MyTezosQuerier:
88
+ def __init__(self, indexer_url: str):
89
+ self._indexer_url = indexer_url
90
+
91
+ async def get_operation(
92
+ self, op_hash: str, network: str
93
+ ) -> Dict[str, Any]:
94
+ # Query TzKT indexer for operation details
95
+ response = await httpx.get(
96
+ f"{self._indexer_url}/v1/operations/{op_hash}"
97
+ )
98
+ return response.json()
99
+ ```
100
+ """
101
+
102
+ async def get_operation(self, op_hash: str, network: str) -> Dict[str, Any]:
103
+ """Query an operation by its hash.
104
+
105
+ Returns the operation details from the Tezos blockchain,
106
+ typically via a TzKT indexer API.
107
+
108
+ Args:
109
+ op_hash: The operation hash to query
110
+ network: CAIP-2 network identifier
111
+
112
+ Returns:
113
+ Dict containing operation details with at minimum:
114
+ - "status": str ("applied", "failed", "backtracked", "skipped")
115
+ - "sender": Dict with "address" key
116
+ - "target": Dict with "address" key (the FA2 contract)
117
+ - "entrypoint": str (should be "transfer")
118
+ - "parameter": The FA2 transfer parameters
119
+
120
+ Raises:
121
+ Exception: If the query fails or operation not found
122
+ """
123
+ ...
124
+
125
+
126
+ class TezosFA2TransferTx(BaseModel):
127
+ """A single transfer transaction within an FA2 transfer batch.
128
+
129
+ Attributes:
130
+ to: Recipient address
131
+ token_id: Token ID within the FA2 contract
132
+ amount: Amount in atomic units
133
+ """
134
+
135
+ to: str = Field(alias="to_")
136
+ token_id: int = Field(alias="token_id")
137
+ amount: str
138
+
139
+ model_config = ConfigDict(
140
+ populate_by_name=True,
141
+ from_attributes=True,
142
+ )
143
+
144
+
145
+ class TezosFA2TransferParam(BaseModel):
146
+ """FA2 transfer parameter structure.
147
+
148
+ Represents a single sender's batch of transfers in the FA2 standard.
149
+
150
+ Attributes:
151
+ from_: Sender address
152
+ txs: List of transfer transactions
153
+ """
154
+
155
+ from_: str = Field(alias="from_")
156
+ txs: List[TezosFA2TransferTx]
157
+
158
+ model_config = ConfigDict(
159
+ populate_by_name=True,
160
+ from_attributes=True,
161
+ )
162
+
163
+
164
+ class ExactDirectPayload(BaseModel):
165
+ """Payload for the Tezos exact-direct payment scheme.
166
+
167
+ Contains the operation hash proving on-chain execution and metadata
168
+ about the FA2 transfer for verification.
169
+
170
+ Attributes:
171
+ op_hash: The Tezos operation hash (starts with 'o', 51 chars)
172
+ from_: Sender's Tezos address
173
+ to: Recipient's Tezos address
174
+ amount: Amount transferred in atomic units
175
+ contract_address: FA2 contract address
176
+ token_id: Token ID within the FA2 contract
177
+ """
178
+
179
+ op_hash: str = Field(alias="opHash")
180
+ from_: str = Field(alias="from")
181
+ to: str
182
+ amount: str
183
+ contract_address: str = Field(alias="contractAddress")
184
+ token_id: int = Field(alias="tokenId")
185
+
186
+ model_config = ConfigDict(
187
+ alias_generator=to_camel,
188
+ populate_by_name=True,
189
+ from_attributes=True,
190
+ )
191
+
192
+ @field_validator("amount")
193
+ @classmethod
194
+ def validate_amount(cls, v: str) -> str:
195
+ """Validate that amount is a positive integer string."""
196
+ try:
197
+ val = int(v)
198
+ if val <= 0:
199
+ raise ValueError("amount must be a positive integer")
200
+ except ValueError:
201
+ raise ValueError("amount must be a positive integer encoded as a string")
202
+ return v
203
+
204
+ @field_validator("op_hash")
205
+ @classmethod
206
+ def validate_op_hash(cls, v: str) -> str:
207
+ """Validate operation hash format."""
208
+ if not v.startswith("o"):
209
+ raise ValueError("Operation hash must start with 'o'")
210
+ if len(v) != 51:
211
+ raise ValueError("Operation hash must be 51 characters")
212
+ return v
213
+
214
+ def to_map(self) -> Dict[str, Any]:
215
+ """Convert to a dict suitable for payment payload.
216
+
217
+ Returns:
218
+ Dict with camelCase keys matching the protocol format.
219
+ """
220
+ return self.model_dump(by_alias=True)
@@ -9,14 +9,21 @@ Supported schemes:
9
9
  from t402.schemes.ton.exact import (
10
10
  ExactTonClientScheme,
11
11
  ExactTonServerScheme,
12
+ ExactTonFacilitatorScheme,
12
13
  TonSigner,
14
+ FacilitatorTonSigner,
13
15
  SCHEME_EXACT,
14
16
  )
15
17
 
16
18
  __all__ = [
17
- # Exact scheme
19
+ # Client
18
20
  "ExactTonClientScheme",
19
- "ExactTonServerScheme",
20
21
  "TonSigner",
22
+ # Server
23
+ "ExactTonServerScheme",
24
+ # Facilitator
25
+ "ExactTonFacilitatorScheme",
26
+ "FacilitatorTonSigner",
27
+ # Constants
21
28
  "SCHEME_EXACT",
22
29
  ]
@@ -15,6 +15,10 @@ from t402.schemes.ton.exact.client import (
15
15
  from t402.schemes.ton.exact.server import (
16
16
  ExactTonServerScheme,
17
17
  )
18
+ from t402.schemes.ton.exact.facilitator import (
19
+ ExactTonFacilitatorScheme,
20
+ FacilitatorTonSigner,
21
+ )
18
22
 
19
23
  __all__ = [
20
24
  # Client
@@ -22,6 +26,9 @@ __all__ = [
22
26
  "TonSigner",
23
27
  # Server
24
28
  "ExactTonServerScheme",
29
+ # Facilitator
30
+ "ExactTonFacilitatorScheme",
31
+ "FacilitatorTonSigner",
25
32
  # Constants
26
33
  "SCHEME_EXACT",
27
34
  ]