uvd-x402-sdk 0.2.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.
@@ -0,0 +1,348 @@
1
+ """
2
+ Base network configuration and registry.
3
+
4
+ This module provides the foundation for network configuration, including:
5
+ - NetworkConfig dataclass for defining network parameters
6
+ - NetworkType enum for categorizing networks
7
+ - Global registry for storing and retrieving network configurations
8
+ """
9
+
10
+ from dataclasses import dataclass, field
11
+ from enum import Enum
12
+ from typing import Dict, List, Optional, Any
13
+
14
+
15
+ class NetworkType(Enum):
16
+ """
17
+ Network type categorization.
18
+
19
+ Different network types use different signature/transaction formats:
20
+ - EVM: EIP-712 signed TransferWithAuthorization (ERC-3009)
21
+ - SVM: Partially-signed VersionedTransaction (SPL token transfer) - Solana, Fogo
22
+ - NEAR: NEP-366 SignedDelegateAction (meta-transaction)
23
+ - STELLAR: Soroban Authorization Entry XDR
24
+
25
+ Note: SOLANA is deprecated, use SVM instead for Solana-compatible chains.
26
+ """
27
+
28
+ EVM = "evm"
29
+ SVM = "svm" # Solana Virtual Machine chains (Solana, Fogo, etc.)
30
+ SOLANA = "solana" # Deprecated: use SVM
31
+ NEAR = "near"
32
+ STELLAR = "stellar"
33
+
34
+ @classmethod
35
+ def is_svm(cls, network_type: "NetworkType") -> bool:
36
+ """Check if network type is SVM-compatible (Solana, Fogo, etc.)."""
37
+ return network_type in (cls.SVM, cls.SOLANA)
38
+
39
+
40
+ @dataclass
41
+ class NetworkConfig:
42
+ """
43
+ Configuration for a blockchain network supporting x402 payments.
44
+
45
+ Attributes:
46
+ name: Lowercase network identifier (e.g., 'base', 'solana')
47
+ display_name: Human-readable name (e.g., 'Base', 'Solana')
48
+ network_type: Type of network (EVM, SOLANA, NEAR, STELLAR)
49
+ chain_id: EVM chain ID (0 for non-EVM networks)
50
+ usdc_address: USDC contract/token address
51
+ usdc_decimals: Number of decimals for USDC (usually 6, BSC=18, Stellar=7)
52
+ usdc_domain_name: EIP-712 domain name for USDC (EVM only)
53
+ usdc_domain_version: EIP-712 domain version (EVM only)
54
+ rpc_url: Default RPC endpoint
55
+ enabled: Whether network is currently enabled
56
+ extra_config: Additional network-specific configuration
57
+ """
58
+
59
+ name: str
60
+ display_name: str
61
+ network_type: NetworkType
62
+ chain_id: int = 0
63
+ usdc_address: str = ""
64
+ usdc_decimals: int = 6
65
+ usdc_domain_name: str = "USD Coin"
66
+ usdc_domain_version: str = "2"
67
+ rpc_url: str = ""
68
+ enabled: bool = True
69
+ extra_config: Dict[str, Any] = field(default_factory=dict)
70
+
71
+ def __post_init__(self) -> None:
72
+ """Validate configuration after initialization."""
73
+ if not self.name:
74
+ raise ValueError("Network name is required")
75
+ if not self.usdc_address:
76
+ raise ValueError(f"USDC address is required for network {self.name}")
77
+
78
+ def get_token_amount(self, usd_amount: float) -> int:
79
+ """
80
+ Convert USD amount to token base units.
81
+
82
+ Args:
83
+ usd_amount: Amount in USD (e.g., 10.50)
84
+
85
+ Returns:
86
+ Amount in token base units (e.g., 10500000 for 6 decimals)
87
+ """
88
+ return int(usd_amount * (10**self.usdc_decimals))
89
+
90
+ def format_token_amount(self, base_units: int) -> float:
91
+ """
92
+ Convert token base units to USD amount.
93
+
94
+ Args:
95
+ base_units: Amount in token base units
96
+
97
+ Returns:
98
+ Amount in USD
99
+ """
100
+ return base_units / (10**self.usdc_decimals)
101
+
102
+
103
+ # Global network registry
104
+ _NETWORK_REGISTRY: Dict[str, NetworkConfig] = {}
105
+
106
+
107
+ def register_network(config: NetworkConfig) -> None:
108
+ """
109
+ Register a network configuration.
110
+
111
+ This allows adding custom networks or overriding built-in configurations.
112
+
113
+ Args:
114
+ config: NetworkConfig instance to register
115
+
116
+ Example:
117
+ >>> from uvd_x402_sdk.networks import register_network, NetworkConfig, NetworkType
118
+ >>> custom_network = NetworkConfig(
119
+ ... name="mychain",
120
+ ... display_name="My Custom Chain",
121
+ ... network_type=NetworkType.EVM,
122
+ ... chain_id=12345,
123
+ ... usdc_address="0x...",
124
+ ... )
125
+ >>> register_network(custom_network)
126
+ """
127
+ _NETWORK_REGISTRY[config.name.lower()] = config
128
+
129
+
130
+ def get_network(name: str) -> Optional[NetworkConfig]:
131
+ """
132
+ Get network configuration by name.
133
+
134
+ Args:
135
+ name: Network identifier (case-insensitive)
136
+
137
+ Returns:
138
+ NetworkConfig if found, None otherwise
139
+ """
140
+ return _NETWORK_REGISTRY.get(name.lower())
141
+
142
+
143
+ def get_network_by_chain_id(chain_id: int) -> Optional[NetworkConfig]:
144
+ """
145
+ Get network configuration by EVM chain ID.
146
+
147
+ Args:
148
+ chain_id: EVM chain ID
149
+
150
+ Returns:
151
+ NetworkConfig if found, None otherwise
152
+ """
153
+ for config in _NETWORK_REGISTRY.values():
154
+ if config.chain_id == chain_id and config.network_type == NetworkType.EVM:
155
+ return config
156
+ return None
157
+
158
+
159
+ def list_networks(
160
+ enabled_only: bool = True,
161
+ network_type: Optional[NetworkType] = None,
162
+ ) -> List[NetworkConfig]:
163
+ """
164
+ List all registered networks.
165
+
166
+ Args:
167
+ enabled_only: Only return enabled networks
168
+ network_type: Filter by network type
169
+
170
+ Returns:
171
+ List of matching NetworkConfig instances
172
+ """
173
+ networks = list(_NETWORK_REGISTRY.values())
174
+
175
+ if enabled_only:
176
+ networks = [n for n in networks if n.enabled]
177
+
178
+ if network_type:
179
+ networks = [n for n in networks if n.network_type == network_type]
180
+
181
+ return networks
182
+
183
+
184
+ def get_supported_chain_ids() -> List[int]:
185
+ """
186
+ Get list of supported EVM chain IDs.
187
+
188
+ Returns:
189
+ List of chain IDs for enabled EVM networks
190
+ """
191
+ return [
192
+ n.chain_id
193
+ for n in _NETWORK_REGISTRY.values()
194
+ if n.enabled and n.network_type == NetworkType.EVM and n.chain_id > 0
195
+ ]
196
+
197
+
198
+ def get_supported_network_names() -> List[str]:
199
+ """
200
+ Get list of supported network names.
201
+
202
+ Returns:
203
+ List of network names for enabled networks
204
+ """
205
+ return [n.name for n in _NETWORK_REGISTRY.values() if n.enabled]
206
+
207
+
208
+ # Expose registry for inspection
209
+ SUPPORTED_NETWORKS = _NETWORK_REGISTRY
210
+
211
+
212
+ # =============================================================================
213
+ # CAIP-2 Utilities (x402 v2 support)
214
+ # =============================================================================
215
+
216
+ # CAIP-2 namespace to network mapping
217
+ _CAIP2_NAMESPACE_MAP = {
218
+ "eip155": NetworkType.EVM,
219
+ "solana": NetworkType.SVM,
220
+ "near": NetworkType.NEAR,
221
+ "stellar": NetworkType.STELLAR,
222
+ }
223
+
224
+ # Network name to CAIP-2 format
225
+ _NETWORK_TO_CAIP2 = {
226
+ # EVM chains (eip155:chainId)
227
+ "base": "eip155:8453",
228
+ "ethereum": "eip155:1",
229
+ "polygon": "eip155:137",
230
+ "arbitrum": "eip155:42161",
231
+ "optimism": "eip155:10",
232
+ "avalanche": "eip155:43114",
233
+ "celo": "eip155:42220",
234
+ "hyperevm": "eip155:999",
235
+ "unichain": "eip155:130",
236
+ "monad": "eip155:143",
237
+ "bsc": "eip155:56",
238
+ # SVM chains (solana:genesisHash first 32 chars)
239
+ "solana": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
240
+ "fogo": "solana:fogo", # Placeholder - update when known
241
+ # NEAR
242
+ "near": "near:mainnet",
243
+ # Stellar
244
+ "stellar": "stellar:pubnet",
245
+ }
246
+
247
+ # CAIP-2 to network name mapping (reverse of above)
248
+ _CAIP2_TO_NETWORK = {v: k for k, v in _NETWORK_TO_CAIP2.items()}
249
+
250
+
251
+ def parse_caip2_network(caip2_id: str) -> Optional[str]:
252
+ """
253
+ Parse a CAIP-2 network identifier to network name.
254
+
255
+ CAIP-2 format: namespace:reference
256
+ Examples:
257
+ - "eip155:8453" -> "base"
258
+ - "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp" -> "solana"
259
+ - "near:mainnet" -> "near"
260
+
261
+ Args:
262
+ caip2_id: CAIP-2 format network identifier
263
+
264
+ Returns:
265
+ Network name if recognized, None otherwise
266
+ """
267
+ if not caip2_id or ":" not in caip2_id:
268
+ return None
269
+
270
+ # Direct lookup first
271
+ if caip2_id in _CAIP2_TO_NETWORK:
272
+ return _CAIP2_TO_NETWORK[caip2_id]
273
+
274
+ # Parse namespace and reference
275
+ parts = caip2_id.split(":", 1)
276
+ if len(parts) != 2:
277
+ return None
278
+
279
+ namespace, reference = parts
280
+
281
+ # For EIP-155 (EVM), the reference is the chain ID
282
+ if namespace == "eip155":
283
+ try:
284
+ chain_id = int(reference)
285
+ network = get_network_by_chain_id(chain_id)
286
+ return network.name if network else None
287
+ except ValueError:
288
+ return None
289
+
290
+ # For other namespaces, check if reference matches known patterns
291
+ # This handles cases like "solana:mainnet" or "near:mainnet"
292
+ if namespace == "solana" and reference in ("mainnet", "mainnet-beta"):
293
+ return "solana"
294
+ if namespace == "near" and reference == "mainnet":
295
+ return "near"
296
+ if namespace == "stellar" and reference in ("pubnet", "mainnet"):
297
+ return "stellar"
298
+
299
+ return None
300
+
301
+
302
+ def to_caip2_network(network_name: str) -> Optional[str]:
303
+ """
304
+ Convert network name to CAIP-2 format.
305
+
306
+ Args:
307
+ network_name: Network identifier (e.g., 'base', 'solana')
308
+
309
+ Returns:
310
+ CAIP-2 format string (e.g., 'eip155:8453'), or None if unknown
311
+ """
312
+ return _NETWORK_TO_CAIP2.get(network_name.lower())
313
+
314
+
315
+ def is_caip2_format(network: str) -> bool:
316
+ """
317
+ Check if a network identifier is in CAIP-2 format.
318
+
319
+ Args:
320
+ network: Network identifier to check
321
+
322
+ Returns:
323
+ True if CAIP-2 format (contains colon), False if v1 format
324
+ """
325
+ return ":" in network
326
+
327
+
328
+ def normalize_network(network: str) -> str:
329
+ """
330
+ Normalize a network identifier to v1 format (network name).
331
+
332
+ Handles both v1 ("base") and v2 CAIP-2 ("eip155:8453") formats.
333
+
334
+ Args:
335
+ network: Network identifier in either format
336
+
337
+ Returns:
338
+ Normalized network name (v1 format)
339
+
340
+ Raises:
341
+ ValueError: If network cannot be parsed
342
+ """
343
+ if is_caip2_format(network):
344
+ normalized = parse_caip2_network(network)
345
+ if normalized is None:
346
+ raise ValueError(f"Unknown CAIP-2 network: {network}")
347
+ return normalized
348
+ return network.lower()
@@ -0,0 +1,235 @@
1
+ """
2
+ EVM network configurations.
3
+
4
+ This module defines configurations for all supported EVM-compatible chains.
5
+ Each chain uses ERC-3009 TransferWithAuthorization for USDC transfers.
6
+
7
+ Important EIP-712 domain considerations:
8
+ - Most chains use 'USD Coin' as the domain name
9
+ - Celo, HyperEVM, Unichain, Monad use 'USDC' as the domain name
10
+ - BSC USDC uses 18 decimals (not standard 6)
11
+ """
12
+
13
+ from uvd_x402_sdk.networks.base import (
14
+ NetworkConfig,
15
+ NetworkType,
16
+ register_network,
17
+ )
18
+
19
+ # =============================================================================
20
+ # EVM Networks Configuration
21
+ # =============================================================================
22
+
23
+ # Base (Layer 2)
24
+ BASE = NetworkConfig(
25
+ name="base",
26
+ display_name="Base",
27
+ network_type=NetworkType.EVM,
28
+ chain_id=8453,
29
+ usdc_address="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
30
+ usdc_decimals=6,
31
+ usdc_domain_name="USD Coin",
32
+ usdc_domain_version="2",
33
+ rpc_url="https://mainnet.base.org",
34
+ enabled=True,
35
+ )
36
+
37
+ # Ethereum Mainnet
38
+ ETHEREUM = NetworkConfig(
39
+ name="ethereum",
40
+ display_name="Ethereum",
41
+ network_type=NetworkType.EVM,
42
+ chain_id=1,
43
+ usdc_address="0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
44
+ usdc_decimals=6,
45
+ usdc_domain_name="USD Coin",
46
+ usdc_domain_version="2",
47
+ rpc_url="https://eth.llamarpc.com",
48
+ enabled=True,
49
+ )
50
+
51
+ # Polygon (PoS)
52
+ POLYGON = NetworkConfig(
53
+ name="polygon",
54
+ display_name="Polygon",
55
+ network_type=NetworkType.EVM,
56
+ chain_id=137,
57
+ usdc_address="0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
58
+ usdc_decimals=6,
59
+ usdc_domain_name="USD Coin",
60
+ usdc_domain_version="2",
61
+ rpc_url="https://polygon-rpc.com",
62
+ enabled=True,
63
+ )
64
+
65
+ # Arbitrum One
66
+ ARBITRUM = NetworkConfig(
67
+ name="arbitrum",
68
+ display_name="Arbitrum One",
69
+ network_type=NetworkType.EVM,
70
+ chain_id=42161,
71
+ usdc_address="0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
72
+ usdc_decimals=6,
73
+ usdc_domain_name="USD Coin",
74
+ usdc_domain_version="2",
75
+ rpc_url="https://arb1.arbitrum.io/rpc",
76
+ enabled=True,
77
+ )
78
+
79
+ # Optimism
80
+ OPTIMISM = NetworkConfig(
81
+ name="optimism",
82
+ display_name="Optimism",
83
+ network_type=NetworkType.EVM,
84
+ chain_id=10,
85
+ usdc_address="0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
86
+ usdc_decimals=6,
87
+ usdc_domain_name="USD Coin",
88
+ usdc_domain_version="2",
89
+ rpc_url="https://mainnet.optimism.io",
90
+ enabled=True,
91
+ )
92
+
93
+ # Avalanche C-Chain
94
+ AVALANCHE = NetworkConfig(
95
+ name="avalanche",
96
+ display_name="Avalanche C-Chain",
97
+ network_type=NetworkType.EVM,
98
+ chain_id=43114,
99
+ usdc_address="0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
100
+ usdc_decimals=6,
101
+ usdc_domain_name="USD Coin",
102
+ usdc_domain_version="2",
103
+ rpc_url="https://avalanche-c-chain-rpc.publicnode.com",
104
+ enabled=True,
105
+ )
106
+
107
+ # Celo
108
+ # NOTE: Celo uses 'USDC' (not 'USD Coin') for EIP-712 domain name
109
+ CELO = NetworkConfig(
110
+ name="celo",
111
+ display_name="Celo",
112
+ network_type=NetworkType.EVM,
113
+ chain_id=42220,
114
+ usdc_address="0xcebA9300f2b948710d2653dD7B07f33A8B32118C",
115
+ usdc_decimals=6,
116
+ usdc_domain_name="USDC", # Different from other chains!
117
+ usdc_domain_version="2",
118
+ rpc_url="https://forno.celo.org",
119
+ enabled=True,
120
+ )
121
+
122
+ # HyperEVM (Hyperliquid)
123
+ # NOTE: HyperEVM uses 'USDC' (not 'USD Coin') for EIP-712 domain name
124
+ HYPEREVM = NetworkConfig(
125
+ name="hyperevm",
126
+ display_name="HyperEVM",
127
+ network_type=NetworkType.EVM,
128
+ chain_id=999,
129
+ usdc_address="0xb88339CB7199b77E23DB6E890353E22632Ba630f",
130
+ usdc_decimals=6,
131
+ usdc_domain_name="USDC", # Different from other chains!
132
+ usdc_domain_version="2",
133
+ rpc_url="https://rpc.hyperliquid.xyz/evm",
134
+ enabled=True,
135
+ )
136
+
137
+ # Unichain
138
+ # NOTE: Unichain uses 'USDC' (not 'USD Coin') for EIP-712 domain name
139
+ UNICHAIN = NetworkConfig(
140
+ name="unichain",
141
+ display_name="Unichain",
142
+ network_type=NetworkType.EVM,
143
+ chain_id=130,
144
+ usdc_address="0x078d782b760474a361dda0af3839290b0ef57ad6",
145
+ usdc_decimals=6,
146
+ usdc_domain_name="USDC", # Different from other chains!
147
+ usdc_domain_version="2",
148
+ rpc_url="https://unichain-rpc.publicnode.com",
149
+ enabled=True,
150
+ )
151
+
152
+ # Monad
153
+ # NOTE: Monad uses 'USDC' (not 'USD Coin') for EIP-712 domain name
154
+ MONAD = NetworkConfig(
155
+ name="monad",
156
+ display_name="Monad",
157
+ network_type=NetworkType.EVM,
158
+ chain_id=143,
159
+ usdc_address="0x754704bc059f8c67012fed69bc8a327a5aafb603",
160
+ usdc_decimals=6,
161
+ usdc_domain_name="USDC", # Different from other chains!
162
+ usdc_domain_version="2",
163
+ rpc_url="https://rpc.monad.xyz",
164
+ enabled=True,
165
+ )
166
+
167
+ # BNB Smart Chain (BSC)
168
+ # NOTE: BSC USDC uses 18 decimals (not 6 like other chains)
169
+ # NOTE: Binance-Peg USDC doesn't support ERC-3009 - DISABLED
170
+ BSC = NetworkConfig(
171
+ name="bsc",
172
+ display_name="BNB Smart Chain",
173
+ network_type=NetworkType.EVM,
174
+ chain_id=56,
175
+ usdc_address="0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d",
176
+ usdc_decimals=18, # Different from other chains!
177
+ usdc_domain_name="USD Coin",
178
+ usdc_domain_version="2",
179
+ rpc_url="https://binance.llamarpc.com",
180
+ enabled=False, # Disabled: Binance-Peg USDC doesn't support ERC-3009
181
+ )
182
+
183
+ # =============================================================================
184
+ # Register all EVM networks
185
+ # =============================================================================
186
+
187
+ _EVM_NETWORKS = [
188
+ BASE,
189
+ ETHEREUM,
190
+ POLYGON,
191
+ ARBITRUM,
192
+ OPTIMISM,
193
+ AVALANCHE,
194
+ CELO,
195
+ HYPEREVM,
196
+ UNICHAIN,
197
+ MONAD,
198
+ BSC,
199
+ ]
200
+
201
+ for network in _EVM_NETWORKS:
202
+ register_network(network)
203
+
204
+
205
+ def get_usdc_domain_name(network_name: str) -> str:
206
+ """
207
+ Get the correct EIP-712 domain name for USDC on a network.
208
+
209
+ Args:
210
+ network_name: Network identifier
211
+
212
+ Returns:
213
+ Domain name string ('USD Coin' or 'USDC')
214
+ """
215
+ # Networks that use 'USDC' instead of 'USD Coin'
216
+ usdc_domain_networks = {"celo", "hyperevm", "unichain", "monad"}
217
+
218
+ if network_name.lower() in usdc_domain_networks:
219
+ return "USDC"
220
+ return "USD Coin"
221
+
222
+
223
+ def get_token_decimals(network_name: str) -> int:
224
+ """
225
+ Get USDC token decimals for a network.
226
+
227
+ Args:
228
+ network_name: Network identifier
229
+
230
+ Returns:
231
+ Number of decimals (6 for most chains, 18 for BSC)
232
+ """
233
+ if network_name.lower() == "bsc":
234
+ return 18
235
+ return 6