t402 1.7.1__py3-none-any.whl → 1.9.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- t402/__init__.py +2 -1
- t402/bridge/client.py +13 -5
- t402/bridge/constants.py +3 -1
- t402/bridge/router.py +1 -1
- t402/bridge/scan.py +3 -1
- t402/chains.py +268 -1
- t402/cli.py +31 -9
- t402/common.py +2 -0
- t402/cosmos_paywall_template.py +2 -0
- t402/encoding.py +9 -3
- t402/erc4337/accounts.py +56 -51
- t402/erc4337/bundlers.py +105 -99
- t402/erc4337/paymasters.py +100 -109
- t402/erc4337/types.py +39 -26
- t402/evm_paywall_template.py +1 -1
- t402/fastapi/middleware.py +1 -3
- t402/mcp/server.py +79 -46
- t402/near_paywall_template.py +2 -0
- t402/networks.py +34 -1
- t402/paywall.py +1 -3
- t402/schemes/__init__.py +164 -1
- t402/schemes/aptos/__init__.py +70 -0
- t402/schemes/aptos/constants.py +349 -0
- t402/schemes/aptos/exact_direct/__init__.py +44 -0
- t402/schemes/aptos/exact_direct/client.py +202 -0
- t402/schemes/aptos/exact_direct/facilitator.py +426 -0
- t402/schemes/aptos/exact_direct/server.py +272 -0
- t402/schemes/aptos/types.py +237 -0
- t402/schemes/evm/__init__.py +67 -1
- t402/schemes/evm/exact/__init__.py +11 -0
- t402/schemes/evm/exact/client.py +3 -1
- t402/schemes/evm/exact/facilitator.py +894 -0
- t402/schemes/evm/exact/server.py +1 -1
- t402/schemes/evm/exact_legacy/__init__.py +38 -0
- t402/schemes/evm/exact_legacy/client.py +291 -0
- t402/schemes/evm/exact_legacy/facilitator.py +777 -0
- t402/schemes/evm/exact_legacy/server.py +231 -0
- t402/schemes/evm/upto/__init__.py +70 -0
- t402/schemes/evm/upto/client.py +244 -0
- t402/schemes/evm/upto/facilitator.py +625 -0
- t402/schemes/evm/upto/server.py +243 -0
- t402/schemes/evm/upto/types.py +307 -0
- t402/schemes/interfaces.py +6 -2
- t402/schemes/near/__init__.py +112 -0
- t402/schemes/near/constants.py +189 -0
- t402/schemes/near/exact_direct/__init__.py +21 -0
- t402/schemes/near/exact_direct/client.py +204 -0
- t402/schemes/near/exact_direct/facilitator.py +455 -0
- t402/schemes/near/exact_direct/server.py +303 -0
- t402/schemes/near/types.py +419 -0
- t402/schemes/polkadot/__init__.py +72 -0
- t402/schemes/polkadot/constants.py +155 -0
- t402/schemes/polkadot/exact_direct/__init__.py +43 -0
- t402/schemes/polkadot/exact_direct/client.py +235 -0
- t402/schemes/polkadot/exact_direct/facilitator.py +428 -0
- t402/schemes/polkadot/exact_direct/server.py +292 -0
- t402/schemes/polkadot/types.py +385 -0
- t402/schemes/registry.py +6 -2
- t402/schemes/stacks/__init__.py +68 -0
- t402/schemes/stacks/constants.py +122 -0
- t402/schemes/stacks/exact_direct/__init__.py +43 -0
- t402/schemes/stacks/exact_direct/client.py +222 -0
- t402/schemes/stacks/exact_direct/facilitator.py +424 -0
- t402/schemes/stacks/exact_direct/server.py +292 -0
- t402/schemes/stacks/types.py +380 -0
- t402/schemes/svm/__init__.py +29 -0
- t402/schemes/svm/exact/__init__.py +35 -0
- t402/schemes/svm/exact/client.py +23 -0
- t402/schemes/svm/exact/facilitator.py +24 -0
- t402/schemes/svm/exact/server.py +20 -0
- t402/schemes/tezos/__init__.py +84 -0
- t402/schemes/tezos/constants.py +372 -0
- t402/schemes/tezos/exact_direct/__init__.py +22 -0
- t402/schemes/tezos/exact_direct/client.py +226 -0
- t402/schemes/tezos/exact_direct/facilitator.py +491 -0
- t402/schemes/tezos/exact_direct/server.py +277 -0
- t402/schemes/tezos/types.py +220 -0
- t402/schemes/ton/__init__.py +9 -2
- t402/schemes/ton/exact/__init__.py +7 -0
- t402/schemes/ton/exact/facilitator.py +730 -0
- t402/schemes/ton/exact/server.py +1 -1
- t402/schemes/tron/__init__.py +11 -2
- t402/schemes/tron/exact/__init__.py +9 -0
- t402/schemes/tron/exact/facilitator.py +673 -0
- t402/schemes/tron/exact/server.py +1 -1
- t402/schemes/upto/__init__.py +80 -0
- t402/schemes/upto/types.py +376 -0
- t402/stacks_paywall_template.py +2 -0
- t402/svm.py +45 -11
- t402/svm_paywall_template.py +1 -1
- t402/ton.py +5 -1
- t402/ton_paywall_template.py +1 -192
- t402/tron.py +2 -0
- t402/tron_paywall_template.py +2 -0
- t402/types.py +4 -2
- t402/wdk/errors.py +15 -5
- t402/wdk/signer.py +11 -2
- {t402-1.7.1.dist-info → t402-1.9.1.dist-info}/METADATA +42 -1
- t402-1.9.1.dist-info/RECORD +125 -0
- t402-1.7.1.dist-info/RECORD +0 -67
- {t402-1.7.1.dist-info → t402-1.9.1.dist-info}/WHEEL +0 -0
- {t402-1.7.1.dist-info → t402-1.9.1.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Solana SVM Exact Payment Scheme.
|
|
2
|
+
|
|
3
|
+
This package provides the exact payment scheme implementation for Solana
|
|
4
|
+
using SPL Token TransferChecked instructions with a facilitator fee payer.
|
|
5
|
+
|
|
6
|
+
The exact scheme allows users to sign transactions that transfer SPL tokens
|
|
7
|
+
to a recipient. The facilitator pays for gas (transaction fees) and broadcasts
|
|
8
|
+
the transaction.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from t402.schemes.svm.exact.client import (
|
|
12
|
+
ExactSvmClientScheme,
|
|
13
|
+
ClientSvmSigner,
|
|
14
|
+
SCHEME_EXACT,
|
|
15
|
+
)
|
|
16
|
+
from t402.schemes.svm.exact.server import (
|
|
17
|
+
ExactSvmServerScheme,
|
|
18
|
+
)
|
|
19
|
+
from t402.schemes.svm.exact.facilitator import (
|
|
20
|
+
ExactSvmFacilitatorScheme,
|
|
21
|
+
FacilitatorSvmSigner,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
# Client
|
|
26
|
+
"ExactSvmClientScheme",
|
|
27
|
+
"ClientSvmSigner",
|
|
28
|
+
# Server
|
|
29
|
+
"ExactSvmServerScheme",
|
|
30
|
+
# Facilitator
|
|
31
|
+
"ExactSvmFacilitatorScheme",
|
|
32
|
+
"FacilitatorSvmSigner",
|
|
33
|
+
# Constants
|
|
34
|
+
"SCHEME_EXACT",
|
|
35
|
+
]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Solana SVM Exact Scheme - Client Implementation.
|
|
2
|
+
|
|
3
|
+
This module provides the client-side implementation of the exact payment scheme
|
|
4
|
+
for Solana network using SPL Token TransferChecked instructions.
|
|
5
|
+
|
|
6
|
+
The client creates and signs a Solana transaction containing a TransferChecked
|
|
7
|
+
instruction, which the facilitator then co-signs (as fee payer) and broadcasts.
|
|
8
|
+
|
|
9
|
+
This module re-exports the client implementation from the monolithic svm module,
|
|
10
|
+
providing the standard scheme package structure.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from t402.svm import (
|
|
14
|
+
SCHEME_EXACT,
|
|
15
|
+
ExactSvmClientScheme,
|
|
16
|
+
ClientSvmSigner,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"SCHEME_EXACT",
|
|
21
|
+
"ExactSvmClientScheme",
|
|
22
|
+
"ClientSvmSigner",
|
|
23
|
+
]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Solana SVM Exact Scheme - Facilitator Implementation.
|
|
2
|
+
|
|
3
|
+
This module provides the facilitator-side implementation of the exact payment
|
|
4
|
+
scheme for Solana network using SPL Token TransferChecked instructions.
|
|
5
|
+
|
|
6
|
+
The facilitator:
|
|
7
|
+
1. Verifies signed transactions by checking transfer instruction parameters,
|
|
8
|
+
ensuring the facilitator's funds are not being stolen, and simulating
|
|
9
|
+
2. Settles payments by co-signing (as fee payer) and broadcasting the transaction
|
|
10
|
+
3. Waits for transaction confirmation
|
|
11
|
+
|
|
12
|
+
This module re-exports the facilitator implementation from the monolithic svm module,
|
|
13
|
+
providing the standard scheme package structure.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from t402.svm import (
|
|
17
|
+
ExactSvmFacilitatorScheme,
|
|
18
|
+
FacilitatorSvmSigner,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"ExactSvmFacilitatorScheme",
|
|
23
|
+
"FacilitatorSvmSigner",
|
|
24
|
+
]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Solana SVM Exact Scheme - Server Implementation.
|
|
2
|
+
|
|
3
|
+
This module provides the server-side implementation of the exact payment scheme
|
|
4
|
+
for Solana network.
|
|
5
|
+
|
|
6
|
+
The server parses user-friendly prices into atomic token amounts and enhances
|
|
7
|
+
payment requirements with the facilitator's fee payer address so clients can
|
|
8
|
+
build transactions correctly.
|
|
9
|
+
|
|
10
|
+
This module re-exports the server implementation from the monolithic svm module,
|
|
11
|
+
providing the standard scheme package structure.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from t402.svm import (
|
|
15
|
+
ExactSvmServerScheme,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"ExactSvmServerScheme",
|
|
20
|
+
]
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""Tezos Blockchain Payment Schemes.
|
|
2
|
+
|
|
3
|
+
This package provides payment scheme implementations for the Tezos blockchain.
|
|
4
|
+
|
|
5
|
+
Supported schemes:
|
|
6
|
+
- exact-direct: Client executes FA2 transfer directly, provides opHash as proof
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
```python
|
|
10
|
+
from t402.schemes.tezos import (
|
|
11
|
+
ExactDirectTezosClient,
|
|
12
|
+
ExactDirectTezosServer,
|
|
13
|
+
ExactDirectTezosFacilitator,
|
|
14
|
+
ClientTezosSigner,
|
|
15
|
+
FacilitatorTezosSigner,
|
|
16
|
+
SCHEME_EXACT_DIRECT,
|
|
17
|
+
)
|
|
18
|
+
```
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from t402.schemes.tezos.constants import (
|
|
22
|
+
SCHEME_EXACT_DIRECT,
|
|
23
|
+
TEZOS_MAINNET,
|
|
24
|
+
TEZOS_GHOSTNET,
|
|
25
|
+
USDT_MAINNET_CONTRACT,
|
|
26
|
+
USDT_MAINNET_TOKEN_ID,
|
|
27
|
+
USDT_DECIMALS,
|
|
28
|
+
USDT_MAINNET,
|
|
29
|
+
NETWORK_CONFIGS,
|
|
30
|
+
TOKEN_REGISTRY,
|
|
31
|
+
is_tezos_network,
|
|
32
|
+
is_valid_address,
|
|
33
|
+
is_valid_operation_hash,
|
|
34
|
+
create_asset_identifier,
|
|
35
|
+
parse_asset_identifier,
|
|
36
|
+
get_network_config,
|
|
37
|
+
get_token_info,
|
|
38
|
+
get_token_by_contract,
|
|
39
|
+
decimal_to_atomic,
|
|
40
|
+
parse_decimal_to_atomic,
|
|
41
|
+
)
|
|
42
|
+
from t402.schemes.tezos.types import (
|
|
43
|
+
ClientTezosSigner,
|
|
44
|
+
FacilitatorTezosSigner,
|
|
45
|
+
ExactDirectPayload,
|
|
46
|
+
)
|
|
47
|
+
from t402.schemes.tezos.exact_direct import (
|
|
48
|
+
ExactDirectTezosClient,
|
|
49
|
+
ExactDirectTezosServer,
|
|
50
|
+
ExactDirectTezosFacilitator,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
__all__ = [
|
|
54
|
+
# Scheme classes
|
|
55
|
+
"ExactDirectTezosClient",
|
|
56
|
+
"ExactDirectTezosServer",
|
|
57
|
+
"ExactDirectTezosFacilitator",
|
|
58
|
+
# Signer protocols
|
|
59
|
+
"ClientTezosSigner",
|
|
60
|
+
"FacilitatorTezosSigner",
|
|
61
|
+
# Payload type
|
|
62
|
+
"ExactDirectPayload",
|
|
63
|
+
# Constants
|
|
64
|
+
"SCHEME_EXACT_DIRECT",
|
|
65
|
+
"TEZOS_MAINNET",
|
|
66
|
+
"TEZOS_GHOSTNET",
|
|
67
|
+
"USDT_MAINNET_CONTRACT",
|
|
68
|
+
"USDT_MAINNET_TOKEN_ID",
|
|
69
|
+
"USDT_DECIMALS",
|
|
70
|
+
"USDT_MAINNET",
|
|
71
|
+
"NETWORK_CONFIGS",
|
|
72
|
+
"TOKEN_REGISTRY",
|
|
73
|
+
# Utility functions
|
|
74
|
+
"is_tezos_network",
|
|
75
|
+
"is_valid_address",
|
|
76
|
+
"is_valid_operation_hash",
|
|
77
|
+
"create_asset_identifier",
|
|
78
|
+
"parse_asset_identifier",
|
|
79
|
+
"get_network_config",
|
|
80
|
+
"get_token_info",
|
|
81
|
+
"get_token_by_contract",
|
|
82
|
+
"decimal_to_atomic",
|
|
83
|
+
"parse_decimal_to_atomic",
|
|
84
|
+
]
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
"""Tezos Blockchain Constants for T402 Protocol.
|
|
2
|
+
|
|
3
|
+
This module defines constants, token information, and network configurations
|
|
4
|
+
for the Tezos blockchain mechanism in the T402 protocol.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Any, Dict, Optional
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Scheme identifier
|
|
13
|
+
SCHEME_EXACT_DIRECT = "exact-direct"
|
|
14
|
+
|
|
15
|
+
# CAIP-2 network identifiers (derived from genesis block hash prefix)
|
|
16
|
+
TEZOS_MAINNET = "tezos:NetXdQprcVkpaWU"
|
|
17
|
+
TEZOS_GHOSTNET = "tezos:NetXnHfVqm9iesp"
|
|
18
|
+
|
|
19
|
+
# RPC endpoints
|
|
20
|
+
TEZOS_MAINNET_RPC = "https://mainnet.api.tez.ie"
|
|
21
|
+
TEZOS_GHOSTNET_RPC = "https://ghostnet.tezos.marigold.dev"
|
|
22
|
+
|
|
23
|
+
# Indexer API endpoints (TzKT)
|
|
24
|
+
TEZOS_MAINNET_INDEXER = "https://api.tzkt.io"
|
|
25
|
+
TEZOS_GHOSTNET_INDEXER = "https://api.ghostnet.tzkt.io"
|
|
26
|
+
|
|
27
|
+
# FA2 token standard (TZIP-12)
|
|
28
|
+
FA2_TRANSFER_ENTRYPOINT = "transfer"
|
|
29
|
+
|
|
30
|
+
# USDt on Tezos Mainnet
|
|
31
|
+
USDT_MAINNET_CONTRACT = "KT1XnTn74bUtxHfDtBmm2bGZAQfhPbvKWR8o"
|
|
32
|
+
USDT_MAINNET_TOKEN_ID = 0
|
|
33
|
+
USDT_DECIMALS = 6
|
|
34
|
+
|
|
35
|
+
# Address length for Tezos addresses (tz1/tz2/tz3/KT1)
|
|
36
|
+
TEZOS_ADDRESS_LENGTH = 36
|
|
37
|
+
|
|
38
|
+
# Operation hash length (starts with 'o')
|
|
39
|
+
TEZOS_OP_HASH_LENGTH = 51
|
|
40
|
+
|
|
41
|
+
# Valid address prefixes
|
|
42
|
+
VALID_ADDRESS_PREFIXES = ("tz1", "tz2", "tz3", "KT1")
|
|
43
|
+
|
|
44
|
+
# Base58 character set (no 0, O, I, l)
|
|
45
|
+
BASE58_CHARS = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class TokenInfo:
|
|
49
|
+
"""Information about a Tezos FA2 token.
|
|
50
|
+
|
|
51
|
+
Attributes:
|
|
52
|
+
contract_address: The FA2 contract address (KT1...)
|
|
53
|
+
token_id: The token ID within the FA2 contract
|
|
54
|
+
symbol: Token symbol (e.g., "USDt")
|
|
55
|
+
name: Full token name (e.g., "Tether USD")
|
|
56
|
+
decimals: Number of decimal places
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
def __init__(
|
|
60
|
+
self,
|
|
61
|
+
contract_address: str,
|
|
62
|
+
token_id: int,
|
|
63
|
+
symbol: str,
|
|
64
|
+
name: str,
|
|
65
|
+
decimals: int,
|
|
66
|
+
):
|
|
67
|
+
self.contract_address = contract_address
|
|
68
|
+
self.token_id = token_id
|
|
69
|
+
self.symbol = symbol
|
|
70
|
+
self.name = name
|
|
71
|
+
self.decimals = decimals
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class NetworkConfig:
|
|
75
|
+
"""Configuration for a Tezos network.
|
|
76
|
+
|
|
77
|
+
Attributes:
|
|
78
|
+
name: Human-readable network name
|
|
79
|
+
rpc_url: Tezos RPC endpoint URL
|
|
80
|
+
indexer_url: TzKT indexer API URL
|
|
81
|
+
default_token: Default token for price conversion (may be None)
|
|
82
|
+
is_testnet: Whether this is a testnet
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
def __init__(
|
|
86
|
+
self,
|
|
87
|
+
name: str,
|
|
88
|
+
rpc_url: str,
|
|
89
|
+
indexer_url: str,
|
|
90
|
+
default_token: Optional[TokenInfo] = None,
|
|
91
|
+
is_testnet: bool = False,
|
|
92
|
+
):
|
|
93
|
+
self.name = name
|
|
94
|
+
self.rpc_url = rpc_url
|
|
95
|
+
self.indexer_url = indexer_url
|
|
96
|
+
self.default_token = default_token
|
|
97
|
+
self.is_testnet = is_testnet
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
# Token definitions
|
|
101
|
+
USDT_MAINNET = TokenInfo(
|
|
102
|
+
contract_address=USDT_MAINNET_CONTRACT,
|
|
103
|
+
token_id=USDT_MAINNET_TOKEN_ID,
|
|
104
|
+
symbol="USDt",
|
|
105
|
+
name="Tether USD",
|
|
106
|
+
decimals=USDT_DECIMALS,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# Network configurations
|
|
110
|
+
NETWORK_CONFIGS: Dict[str, NetworkConfig] = {
|
|
111
|
+
TEZOS_MAINNET: NetworkConfig(
|
|
112
|
+
name="Tezos Mainnet",
|
|
113
|
+
rpc_url=TEZOS_MAINNET_RPC,
|
|
114
|
+
indexer_url=TEZOS_MAINNET_INDEXER,
|
|
115
|
+
default_token=USDT_MAINNET,
|
|
116
|
+
is_testnet=False,
|
|
117
|
+
),
|
|
118
|
+
TEZOS_GHOSTNET: NetworkConfig(
|
|
119
|
+
name="Tezos Ghostnet",
|
|
120
|
+
rpc_url=TEZOS_GHOSTNET_RPC,
|
|
121
|
+
indexer_url=TEZOS_GHOSTNET_INDEXER,
|
|
122
|
+
default_token=None, # No USDT on testnet
|
|
123
|
+
is_testnet=True,
|
|
124
|
+
),
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
# Token registry indexed by network and symbol
|
|
128
|
+
TOKEN_REGISTRY: Dict[str, Dict[str, TokenInfo]] = {
|
|
129
|
+
TEZOS_MAINNET: {
|
|
130
|
+
"USDt": USDT_MAINNET,
|
|
131
|
+
},
|
|
132
|
+
TEZOS_GHOSTNET: {},
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def get_network_config(network: str) -> Optional[NetworkConfig]:
|
|
137
|
+
"""Get the configuration for a Tezos network.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
network: CAIP-2 network identifier (e.g., "tezos:NetXdQprcVkpaWU")
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
NetworkConfig if found, None otherwise
|
|
144
|
+
"""
|
|
145
|
+
return NETWORK_CONFIGS.get(network)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def get_token_info(network: str, symbol: str) -> Optional[TokenInfo]:
|
|
149
|
+
"""Get token information by network and symbol.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
network: CAIP-2 network identifier
|
|
153
|
+
symbol: Token symbol (e.g., "USDt")
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
TokenInfo if found, None otherwise
|
|
157
|
+
"""
|
|
158
|
+
tokens = TOKEN_REGISTRY.get(network, {})
|
|
159
|
+
return tokens.get(symbol)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def get_token_by_contract(
|
|
163
|
+
network: str, contract_address: str, token_id: int
|
|
164
|
+
) -> Optional[TokenInfo]:
|
|
165
|
+
"""Get token information by contract address and token ID.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
network: CAIP-2 network identifier
|
|
169
|
+
contract_address: FA2 contract address
|
|
170
|
+
token_id: Token ID within the contract
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
TokenInfo if found, None otherwise
|
|
174
|
+
"""
|
|
175
|
+
tokens = TOKEN_REGISTRY.get(network, {})
|
|
176
|
+
for token in tokens.values():
|
|
177
|
+
if token.contract_address == contract_address and token.token_id == token_id:
|
|
178
|
+
return token
|
|
179
|
+
return None
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def is_tezos_network(network: str) -> bool:
|
|
183
|
+
"""Check if a network identifier belongs to the Tezos namespace.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
network: Network identifier string
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
True if the network starts with "tezos:"
|
|
190
|
+
"""
|
|
191
|
+
return network.startswith("tezos:")
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def is_valid_address(address: str) -> bool:
|
|
195
|
+
"""Validate a Tezos address format.
|
|
196
|
+
|
|
197
|
+
Valid addresses:
|
|
198
|
+
- Implicit accounts: tz1, tz2, tz3 (36 characters)
|
|
199
|
+
- Contract accounts: KT1 (36 characters)
|
|
200
|
+
|
|
201
|
+
All characters must be valid Base58 characters.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
address: The address to validate
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
True if the address format is valid
|
|
208
|
+
"""
|
|
209
|
+
if not address:
|
|
210
|
+
return False
|
|
211
|
+
if not address.startswith(VALID_ADDRESS_PREFIXES):
|
|
212
|
+
return False
|
|
213
|
+
if len(address) != TEZOS_ADDRESS_LENGTH:
|
|
214
|
+
return False
|
|
215
|
+
# Check all characters are valid base58
|
|
216
|
+
for char in address:
|
|
217
|
+
if char not in BASE58_CHARS:
|
|
218
|
+
return False
|
|
219
|
+
return True
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def is_valid_operation_hash(op_hash: str) -> bool:
|
|
223
|
+
"""Validate a Tezos operation hash format.
|
|
224
|
+
|
|
225
|
+
Operation hashes start with 'o' and are 51 characters of Base58.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
op_hash: The operation hash to validate
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
True if the operation hash format is valid
|
|
232
|
+
"""
|
|
233
|
+
if not op_hash:
|
|
234
|
+
return False
|
|
235
|
+
if not op_hash.startswith("o"):
|
|
236
|
+
return False
|
|
237
|
+
if len(op_hash) != TEZOS_OP_HASH_LENGTH:
|
|
238
|
+
return False
|
|
239
|
+
# Check all characters are valid base58
|
|
240
|
+
for char in op_hash:
|
|
241
|
+
if char not in BASE58_CHARS:
|
|
242
|
+
return False
|
|
243
|
+
return True
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def create_asset_identifier(network: str, contract_address: str, token_id: int) -> str:
|
|
247
|
+
"""Create a CAIP-19 asset identifier for a Tezos FA2 token.
|
|
248
|
+
|
|
249
|
+
Format: tezos:{chainRef}/fa2:{contractAddress}/{tokenId}
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
network: CAIP-2 network identifier (e.g., "tezos:NetXdQprcVkpaWU")
|
|
253
|
+
contract_address: FA2 contract address (e.g., "KT1XnTn74bUtxHfDtBmm2bGZAQfhPbvKWR8o")
|
|
254
|
+
token_id: Token ID within the FA2 contract
|
|
255
|
+
|
|
256
|
+
Returns:
|
|
257
|
+
CAIP-19 asset identifier string
|
|
258
|
+
"""
|
|
259
|
+
return f"{network}/fa2:{contract_address}/{token_id}"
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def parse_asset_identifier(asset: str) -> Dict[str, Any]:
|
|
263
|
+
"""Parse a CAIP-19 asset identifier for Tezos FA2 tokens.
|
|
264
|
+
|
|
265
|
+
Supports two formats:
|
|
266
|
+
- CAIP-19: tezos:{chainRef}/fa2:{contractAddress}/{tokenId}
|
|
267
|
+
- Simple: {contractAddress}/{tokenId} or {contractAddress} (tokenId defaults to 0)
|
|
268
|
+
|
|
269
|
+
Args:
|
|
270
|
+
asset: The asset identifier string
|
|
271
|
+
|
|
272
|
+
Returns:
|
|
273
|
+
Dict with "contract_address" (str) and "token_id" (int)
|
|
274
|
+
|
|
275
|
+
Raises:
|
|
276
|
+
ValueError: If the asset format is unrecognized
|
|
277
|
+
"""
|
|
278
|
+
if not asset:
|
|
279
|
+
raise ValueError("Asset identifier is empty")
|
|
280
|
+
|
|
281
|
+
# Try CAIP-19 format: tezos:{chainRef}/fa2:{contract}/{tokenId}
|
|
282
|
+
if asset.startswith("tezos:"):
|
|
283
|
+
parts = asset.split("/")
|
|
284
|
+
if len(parts) == 3 and parts[1].startswith("fa2:"):
|
|
285
|
+
contract_address = parts[1][4:] # Remove "fa2:" prefix
|
|
286
|
+
try:
|
|
287
|
+
token_id = int(parts[2])
|
|
288
|
+
except ValueError:
|
|
289
|
+
raise ValueError(f"Invalid token ID in asset: {asset}")
|
|
290
|
+
if not contract_address.startswith("KT1") or len(contract_address) != 36:
|
|
291
|
+
raise ValueError(f"Invalid contract address in asset: {asset}")
|
|
292
|
+
return {"contract_address": contract_address, "token_id": token_id}
|
|
293
|
+
|
|
294
|
+
# Try simple format: KT1.../tokenId or KT1...
|
|
295
|
+
if asset.startswith("KT1"):
|
|
296
|
+
parts = asset.split("/")
|
|
297
|
+
contract_address = parts[0]
|
|
298
|
+
if len(contract_address) != 36:
|
|
299
|
+
raise ValueError(f"Invalid contract address in asset: {asset}")
|
|
300
|
+
token_id = 0
|
|
301
|
+
if len(parts) == 2:
|
|
302
|
+
try:
|
|
303
|
+
token_id = int(parts[1])
|
|
304
|
+
except ValueError:
|
|
305
|
+
raise ValueError(f"Invalid token ID in asset: {asset}")
|
|
306
|
+
elif len(parts) > 2:
|
|
307
|
+
raise ValueError(f"Unrecognized asset format: {asset}")
|
|
308
|
+
return {"contract_address": contract_address, "token_id": token_id}
|
|
309
|
+
|
|
310
|
+
raise ValueError(
|
|
311
|
+
f"Unrecognized asset format: {asset} "
|
|
312
|
+
f"(expected tezos:{{chainRef}}/fa2:{{contract}}/{{tokenId}} or KT1...)"
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def decimal_to_atomic(amount: float, decimals: int) -> str:
|
|
317
|
+
"""Convert a decimal amount to atomic units string.
|
|
318
|
+
|
|
319
|
+
Args:
|
|
320
|
+
amount: Decimal amount (e.g., 1.50)
|
|
321
|
+
decimals: Number of decimal places for the token
|
|
322
|
+
|
|
323
|
+
Returns:
|
|
324
|
+
Atomic amount as string (e.g., "1500000" for 6 decimals)
|
|
325
|
+
"""
|
|
326
|
+
|
|
327
|
+
multiplier = 10**decimals
|
|
328
|
+
atomic = int(round(amount * multiplier))
|
|
329
|
+
return str(atomic)
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def parse_decimal_to_atomic(amount: str, decimals: int) -> str:
|
|
333
|
+
"""Convert a decimal string amount to atomic units.
|
|
334
|
+
|
|
335
|
+
Args:
|
|
336
|
+
amount: Decimal string (e.g., "1.50")
|
|
337
|
+
decimals: Number of decimal places for the token
|
|
338
|
+
|
|
339
|
+
Returns:
|
|
340
|
+
Atomic amount string (e.g., "1500000" for 6 decimals)
|
|
341
|
+
|
|
342
|
+
Raises:
|
|
343
|
+
ValueError: If the amount format is invalid
|
|
344
|
+
"""
|
|
345
|
+
parts = amount.split(".")
|
|
346
|
+
|
|
347
|
+
integer_part = parts[0]
|
|
348
|
+
fractional_part = ""
|
|
349
|
+
|
|
350
|
+
if len(parts) == 2:
|
|
351
|
+
fractional_part = parts[1]
|
|
352
|
+
elif len(parts) > 2:
|
|
353
|
+
raise ValueError(f"Invalid amount format: {amount}")
|
|
354
|
+
|
|
355
|
+
# Pad or truncate fractional part to match decimals
|
|
356
|
+
if len(fractional_part) > decimals:
|
|
357
|
+
fractional_part = fractional_part[:decimals]
|
|
358
|
+
else:
|
|
359
|
+
fractional_part = fractional_part + "0" * (decimals - len(fractional_part))
|
|
360
|
+
|
|
361
|
+
# Combine and parse as integer
|
|
362
|
+
combined = integer_part + fractional_part
|
|
363
|
+
|
|
364
|
+
# Remove leading zeros but keep at least one digit
|
|
365
|
+
combined = combined.lstrip("0") or "0"
|
|
366
|
+
|
|
367
|
+
try:
|
|
368
|
+
result = int(combined)
|
|
369
|
+
except ValueError:
|
|
370
|
+
raise ValueError(f"Failed to parse amount: {amount}")
|
|
371
|
+
|
|
372
|
+
return str(result)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Tezos Exact-Direct Payment Scheme.
|
|
2
|
+
|
|
3
|
+
This package provides the exact-direct payment scheme implementation for Tezos.
|
|
4
|
+
In this scheme, the client executes the FA2 transfer directly on-chain and
|
|
5
|
+
provides the operation hash as proof of payment. The facilitator then verifies
|
|
6
|
+
the operation on-chain.
|
|
7
|
+
|
|
8
|
+
Components:
|
|
9
|
+
- Client: Executes FA2 transfer and returns opHash
|
|
10
|
+
- Server: Parses prices and enhances requirements with CAIP-19 asset info
|
|
11
|
+
- Facilitator: Verifies operation status and transfer details on-chain
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from t402.schemes.tezos.exact_direct.client import ExactDirectTezosClient
|
|
15
|
+
from t402.schemes.tezos.exact_direct.server import ExactDirectTezosServer
|
|
16
|
+
from t402.schemes.tezos.exact_direct.facilitator import ExactDirectTezosFacilitator
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"ExactDirectTezosClient",
|
|
20
|
+
"ExactDirectTezosServer",
|
|
21
|
+
"ExactDirectTezosFacilitator",
|
|
22
|
+
]
|