uvd-x402-sdk 0.4.2__py3-none-any.whl → 0.5.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.
uvd_x402_sdk/__init__.py CHANGED
@@ -2,8 +2,11 @@
2
2
  uvd-x402-sdk: Python SDK for x402 payments via Ultravioleta DAO facilitator.
3
3
 
4
4
  This SDK enables developers to easily integrate x402 cryptocurrency payments
5
- into their Python applications with support for 14 blockchain networks across
6
- 4 network types (EVM, SVM, NEAR, Stellar).
5
+ into their Python applications with support for 16 blockchain networks across
6
+ 5 network types (EVM, SVM, NEAR, Stellar, Algorand).
7
+
8
+ The SDK automatically handles facilitator configuration - users don't need to
9
+ configure fee payer addresses or other facilitator details manually.
7
10
 
8
11
  Supports both x402 v1 and v2 protocols:
9
12
  - v1: network as string ("base", "solana")
@@ -29,15 +32,16 @@ Example usage:
29
32
  def protected_endpoint():
30
33
  return {"message": "Payment verified!"}
31
34
 
32
- Supported Networks (14 total):
35
+ Supported Networks (16 total):
33
36
  - EVM (10): Base, Ethereum, Polygon, Arbitrum, Optimism, Avalanche, Celo,
34
37
  HyperEVM, Unichain, Monad
35
38
  - SVM (2): Solana, Fogo
36
39
  - NEAR (1): NEAR Protocol
37
40
  - Stellar (1): Stellar
41
+ - Algorand (2): Algorand mainnet, Algorand testnet
38
42
  """
39
43
 
40
- __version__ = "0.3.4"
44
+ __version__ = "0.5.0"
41
45
  __author__ = "Ultravioleta DAO"
42
46
 
43
47
  from uvd_x402_sdk.client import X402Client
@@ -109,6 +113,28 @@ from uvd_x402_sdk.response import (
109
113
  payment_required_response_v2,
110
114
  Payment402BuilderV2,
111
115
  )
116
+ from uvd_x402_sdk.facilitator import (
117
+ # Facilitator URL and constants
118
+ DEFAULT_FACILITATOR_URL,
119
+ get_facilitator_url,
120
+ # Fee payer addresses by chain
121
+ ALGORAND_FEE_PAYER_MAINNET,
122
+ ALGORAND_FEE_PAYER_TESTNET,
123
+ SOLANA_FEE_PAYER_MAINNET,
124
+ SOLANA_FEE_PAYER_DEVNET,
125
+ FOGO_FEE_PAYER_MAINNET,
126
+ FOGO_FEE_PAYER_TESTNET,
127
+ NEAR_FEE_PAYER_MAINNET,
128
+ NEAR_FEE_PAYER_TESTNET,
129
+ STELLAR_FEE_PAYER_MAINNET,
130
+ STELLAR_FEE_PAYER_TESTNET,
131
+ # Helper functions
132
+ get_fee_payer,
133
+ get_facilitator_address,
134
+ requires_fee_payer,
135
+ get_all_fee_payers,
136
+ build_payment_info,
137
+ )
112
138
 
113
139
  __all__ = [
114
140
  # Version
@@ -182,4 +208,22 @@ __all__ = [
182
208
  "create_402_headers_v2",
183
209
  "payment_required_response_v2",
184
210
  "Payment402BuilderV2",
211
+ # Facilitator constants and helpers
212
+ "DEFAULT_FACILITATOR_URL",
213
+ "get_facilitator_url",
214
+ "ALGORAND_FEE_PAYER_MAINNET",
215
+ "ALGORAND_FEE_PAYER_TESTNET",
216
+ "SOLANA_FEE_PAYER_MAINNET",
217
+ "SOLANA_FEE_PAYER_DEVNET",
218
+ "FOGO_FEE_PAYER_MAINNET",
219
+ "FOGO_FEE_PAYER_TESTNET",
220
+ "NEAR_FEE_PAYER_MAINNET",
221
+ "NEAR_FEE_PAYER_TESTNET",
222
+ "STELLAR_FEE_PAYER_MAINNET",
223
+ "STELLAR_FEE_PAYER_TESTNET",
224
+ "get_fee_payer",
225
+ "get_facilitator_address",
226
+ "requires_fee_payer",
227
+ "get_all_fee_payers",
228
+ "build_payment_info",
185
229
  ]
@@ -0,0 +1,384 @@
1
+ """
2
+ Facilitator configuration and constants.
3
+
4
+ This module provides:
5
+ - Default facilitator URL
6
+ - Fee payer addresses for all supported non-EVM networks
7
+ - Helper functions to get the appropriate facilitator address for any network
8
+
9
+ Users of this SDK should NOT need to configure facilitator details manually.
10
+ All addresses are embedded as constants, extracted from the official facilitator.
11
+
12
+ Facilitator URL: https://facilitator.ultravioletadao.xyz
13
+ """
14
+
15
+ from typing import Dict, Optional
16
+
17
+ from uvd_x402_sdk.networks.base import (
18
+ NetworkType,
19
+ get_network,
20
+ is_caip2_format,
21
+ normalize_network,
22
+ )
23
+
24
+
25
+ # =============================================================================
26
+ # Facilitator URL
27
+ # =============================================================================
28
+
29
+ DEFAULT_FACILITATOR_URL = "https://facilitator.ultravioletadao.xyz"
30
+
31
+ # Alternative facilitator URLs (for future use)
32
+ FACILITATOR_URLS = {
33
+ "production": "https://facilitator.ultravioletadao.xyz",
34
+ }
35
+
36
+
37
+ # =============================================================================
38
+ # Fee Payer Addresses (Non-EVM chains require fee payer for gasless payments)
39
+ # =============================================================================
40
+
41
+ # Algorand fee payer addresses
42
+ ALGORAND_FEE_PAYER_MAINNET = "KIMS5H6QLCUDL65L5UBTOXDPWLMTS7N3AAC3I6B2NCONEI5QIVK7LH2C2I"
43
+ ALGORAND_FEE_PAYER_TESTNET = "5DPPDQNYUPCTXRZWRYSF3WPYU6RKAUR25F3YG4EKXQRHV5AUAI62H5GXL4"
44
+
45
+ # Solana fee payer addresses (also used for Fogo mainnet)
46
+ SOLANA_FEE_PAYER_MAINNET = "F742C4VfFLQ9zRQyithoj5229ZgtX2WqKCSFKgH2EThq"
47
+ SOLANA_FEE_PAYER_DEVNET = "6xNPewUdKRbEZDReQdpyfNUdgNg8QRc8Mt263T5GZSRv"
48
+
49
+ # Fogo (SVM) fee payer addresses
50
+ FOGO_FEE_PAYER_MAINNET = "F742C4VfFLQ9zRQyithoj5229ZgtX2WqKCSFKgH2EThq"
51
+ FOGO_FEE_PAYER_TESTNET = "6xNPewUdKRbEZDReQdpyfNUdgNg8QRc8Mt263T5GZSRv"
52
+
53
+ # NEAR fee payer addresses (account IDs)
54
+ NEAR_FEE_PAYER_MAINNET = "uvd-facilitator.near"
55
+ NEAR_FEE_PAYER_TESTNET = "uvd-facilitator.testnet"
56
+
57
+ # Stellar fee payer addresses (public keys)
58
+ STELLAR_FEE_PAYER_MAINNET = "GCHPGXJT2WFFRFCA5TV4G4E3PMMXLNIDUH27PKDYA4QJ2XGYZWGFZNHB"
59
+ STELLAR_FEE_PAYER_TESTNET = "GBBFZMLUJEZVI32EN4XA2KPP445XIBTMTRBLYWFIL556RDTHS2OWFQ2Z"
60
+
61
+
62
+ # =============================================================================
63
+ # Network to Fee Payer Mapping
64
+ # =============================================================================
65
+
66
+ # Maps network names to their fee payer addresses
67
+ # EVM networks don't have fee payers (they use EIP-3009 transferWithAuthorization)
68
+ _FEE_PAYER_BY_NETWORK: Dict[str, str] = {
69
+ # Algorand
70
+ "algorand": ALGORAND_FEE_PAYER_MAINNET,
71
+ "algorand-mainnet": ALGORAND_FEE_PAYER_MAINNET,
72
+ "algorand-testnet": ALGORAND_FEE_PAYER_TESTNET,
73
+ # Solana
74
+ "solana": SOLANA_FEE_PAYER_MAINNET,
75
+ "solana-mainnet": SOLANA_FEE_PAYER_MAINNET,
76
+ "solana-devnet": SOLANA_FEE_PAYER_DEVNET,
77
+ # Fogo (SVM)
78
+ "fogo": FOGO_FEE_PAYER_MAINNET,
79
+ "fogo-mainnet": FOGO_FEE_PAYER_MAINNET,
80
+ "fogo-testnet": FOGO_FEE_PAYER_TESTNET,
81
+ # NEAR
82
+ "near": NEAR_FEE_PAYER_MAINNET,
83
+ "near-mainnet": NEAR_FEE_PAYER_MAINNET,
84
+ "near-testnet": NEAR_FEE_PAYER_TESTNET,
85
+ # Stellar
86
+ "stellar": STELLAR_FEE_PAYER_MAINNET,
87
+ "stellar-mainnet": STELLAR_FEE_PAYER_MAINNET,
88
+ "stellar-testnet": STELLAR_FEE_PAYER_TESTNET,
89
+ }
90
+
91
+ # CAIP-2 format mappings (x402 v2)
92
+ _FEE_PAYER_BY_CAIP2: Dict[str, str] = {
93
+ # Algorand
94
+ "algorand:mainnet": ALGORAND_FEE_PAYER_MAINNET,
95
+ "algorand:testnet": ALGORAND_FEE_PAYER_TESTNET,
96
+ # Solana
97
+ "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp": SOLANA_FEE_PAYER_MAINNET,
98
+ "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1": SOLANA_FEE_PAYER_DEVNET,
99
+ # Fogo (SVM)
100
+ "fogo:mainnet": FOGO_FEE_PAYER_MAINNET,
101
+ "fogo:testnet": FOGO_FEE_PAYER_TESTNET,
102
+ # NEAR
103
+ "near:mainnet": NEAR_FEE_PAYER_MAINNET,
104
+ "near:testnet": NEAR_FEE_PAYER_TESTNET,
105
+ # Stellar
106
+ "stellar:pubnet": STELLAR_FEE_PAYER_MAINNET,
107
+ "stellar:testnet": STELLAR_FEE_PAYER_TESTNET,
108
+ }
109
+
110
+
111
+ # =============================================================================
112
+ # Helper Functions
113
+ # =============================================================================
114
+
115
+
116
+ def get_fee_payer(network: str) -> Optional[str]:
117
+ """
118
+ Get the fee payer address for a network.
119
+
120
+ Fee payers are only needed for non-EVM chains (Algorand, Solana, NEAR, Stellar).
121
+ EVM chains use EIP-3009 transferWithAuthorization which is gasless by design.
122
+
123
+ Args:
124
+ network: Network identifier (v1 name or CAIP-2 format)
125
+ Examples: "algorand", "algorand-mainnet", "algorand:mainnet"
126
+
127
+ Returns:
128
+ Fee payer address if applicable, None for EVM chains
129
+
130
+ Example:
131
+ >>> get_fee_payer("algorand")
132
+ 'KIMS5H6QLCUDL65L5UBTOXDPWLMTS7N3AAC3I6B2NCONEI5QIVK7LH2C2I'
133
+ >>> get_fee_payer("solana")
134
+ 'F742C4VfFLQ9zRQyithoj5229ZgtX2WqKCSFKgH2EThq'
135
+ >>> get_fee_payer("base") # EVM chain
136
+ None
137
+ """
138
+ # Check CAIP-2 format first
139
+ if is_caip2_format(network):
140
+ return _FEE_PAYER_BY_CAIP2.get(network)
141
+
142
+ # Check v1 network name
143
+ network_lower = network.lower()
144
+ return _FEE_PAYER_BY_NETWORK.get(network_lower)
145
+
146
+
147
+ def get_facilitator_address(network: str) -> Optional[str]:
148
+ """
149
+ Alias for get_fee_payer() for backward compatibility.
150
+
151
+ Args:
152
+ network: Network identifier
153
+
154
+ Returns:
155
+ Facilitator/fee payer address if applicable
156
+ """
157
+ return get_fee_payer(network)
158
+
159
+
160
+ def requires_fee_payer(network: str) -> bool:
161
+ """
162
+ Check if a network requires a fee payer address.
163
+
164
+ Args:
165
+ network: Network identifier (v1 name or CAIP-2 format)
166
+
167
+ Returns:
168
+ True if network requires fee payer (non-EVM), False otherwise
169
+
170
+ Example:
171
+ >>> requires_fee_payer("algorand")
172
+ True
173
+ >>> requires_fee_payer("base")
174
+ False
175
+ """
176
+ # Try to get network config
177
+ try:
178
+ normalized = normalize_network(network)
179
+ net_config = get_network(normalized)
180
+ if net_config:
181
+ return net_config.network_type != NetworkType.EVM
182
+ except ValueError:
183
+ pass
184
+
185
+ # Fall back to checking if we have a fee payer registered
186
+ return get_fee_payer(network) is not None
187
+
188
+
189
+ def get_network_type_from_fee_payer(address: str) -> Optional[NetworkType]:
190
+ """
191
+ Determine network type from a fee payer address format.
192
+
193
+ Args:
194
+ address: Fee payer address
195
+
196
+ Returns:
197
+ NetworkType if recognizable, None otherwise
198
+
199
+ Example:
200
+ >>> get_network_type_from_fee_payer("KIMS5H6QLCUDL65L5UBTOXDPWLMTS7N3AAC3I6B2NCONEI5QIVK7LH2C2I")
201
+ NetworkType.ALGORAND
202
+ >>> get_network_type_from_fee_payer("F742C4VfFLQ9zRQyithoj5229ZgtX2WqKCSFKgH2EThq")
203
+ NetworkType.SVM
204
+ """
205
+ if not address:
206
+ return None
207
+
208
+ # Algorand: 58 characters, base32 (A-Z2-7)
209
+ if len(address) == 58 and address.isalnum():
210
+ import re
211
+ if re.match(r'^[A-Z2-7]+$', address):
212
+ return NetworkType.ALGORAND
213
+
214
+ # Solana/Fogo: 32-44 characters, base58
215
+ if 32 <= len(address) <= 44:
216
+ # Base58 uses alphanumeric chars except 0, O, I, l
217
+ import re
218
+ if re.match(r'^[A-HJ-NP-Za-km-z1-9]+$', address):
219
+ return NetworkType.SVM
220
+
221
+ # NEAR: ends with .near or .testnet
222
+ if address.endswith('.near') or address.endswith('.testnet'):
223
+ return NetworkType.NEAR
224
+
225
+ # Stellar: starts with G, 56 characters
226
+ if len(address) == 56 and address.startswith('G'):
227
+ return NetworkType.STELLAR
228
+
229
+ return None
230
+
231
+
232
+ def validate_fee_payer_for_network(network: str, address: str) -> bool:
233
+ """
234
+ Validate that a fee payer address format matches the network type.
235
+
236
+ Args:
237
+ network: Network identifier
238
+ address: Fee payer address to validate
239
+
240
+ Returns:
241
+ True if address format is valid for network
242
+
243
+ Example:
244
+ >>> validate_fee_payer_for_network("algorand", "KIMS5H6...")
245
+ True
246
+ >>> validate_fee_payer_for_network("algorand", "F742C4V...") # Solana address
247
+ False
248
+ """
249
+ try:
250
+ normalized = normalize_network(network)
251
+ net_config = get_network(normalized)
252
+ if not net_config:
253
+ return False
254
+
255
+ detected_type = get_network_type_from_fee_payer(address)
256
+ if detected_type is None:
257
+ return False
258
+
259
+ # SVM includes both SOLANA and SVM types
260
+ if net_config.network_type in (NetworkType.SVM, NetworkType.SOLANA):
261
+ return detected_type == NetworkType.SVM
262
+
263
+ return net_config.network_type == detected_type
264
+ except ValueError:
265
+ return False
266
+
267
+
268
+ def get_all_fee_payers() -> Dict[str, str]:
269
+ """
270
+ Get all registered fee payer addresses.
271
+
272
+ Returns:
273
+ Dictionary mapping network names to fee payer addresses
274
+
275
+ Example:
276
+ >>> payers = get_all_fee_payers()
277
+ >>> for network, address in payers.items():
278
+ ... print(f"{network}: {address}")
279
+ """
280
+ return dict(_FEE_PAYER_BY_NETWORK)
281
+
282
+
283
+ def get_facilitator_url() -> str:
284
+ """
285
+ Get the default facilitator URL.
286
+
287
+ Returns:
288
+ Facilitator API URL
289
+
290
+ Example:
291
+ >>> url = get_facilitator_url()
292
+ >>> print(url)
293
+ 'https://facilitator.ultravioletadao.xyz'
294
+ """
295
+ return DEFAULT_FACILITATOR_URL
296
+
297
+
298
+ # =============================================================================
299
+ # Payment Info Builder
300
+ # =============================================================================
301
+
302
+
303
+ def build_payment_info(
304
+ network: str,
305
+ pay_to: str,
306
+ max_amount_required: str,
307
+ description: str = "",
308
+ resource: str = "",
309
+ asset: Optional[str] = None,
310
+ token_type: str = "usdc",
311
+ extra: Optional[Dict] = None,
312
+ ) -> Dict:
313
+ """
314
+ Build a payment info dict with all facilitator details pre-configured.
315
+
316
+ This function automatically includes the correct fee payer address for
317
+ non-EVM networks, so SDK users don't need to configure this manually.
318
+
319
+ Args:
320
+ network: Network identifier (e.g., "algorand", "solana", "base")
321
+ pay_to: Recipient address
322
+ max_amount_required: Maximum amount in token units (string)
323
+ description: Optional payment description
324
+ resource: Optional resource being purchased
325
+ asset: Optional token address (defaults to USDC for network)
326
+ token_type: Token type (default "usdc")
327
+ extra: Additional extra fields to include
328
+
329
+ Returns:
330
+ Payment info dictionary ready for 402 response
331
+
332
+ Example:
333
+ >>> info = build_payment_info(
334
+ ... network="algorand",
335
+ ... pay_to="MERCHANT_ADDRESS...",
336
+ ... max_amount_required="1000000",
337
+ ... description="API access"
338
+ ... )
339
+ >>> # info includes facilitator fee payer automatically
340
+ """
341
+ # Get network config for defaults
342
+ try:
343
+ normalized = normalize_network(network)
344
+ net_config = get_network(normalized)
345
+ except ValueError:
346
+ net_config = None
347
+
348
+ # Build base payment info
349
+ payment_info: Dict = {
350
+ "network": network,
351
+ "payTo": pay_to,
352
+ "maxAmountRequired": max_amount_required,
353
+ }
354
+
355
+ if description:
356
+ payment_info["description"] = description
357
+ if resource:
358
+ payment_info["resource"] = resource
359
+
360
+ # Set asset (token address)
361
+ if asset:
362
+ payment_info["asset"] = asset
363
+ elif net_config:
364
+ payment_info["asset"] = net_config.usdc_address
365
+
366
+ # Build extra field
367
+ payment_extra: Dict = {}
368
+
369
+ # Add token type info
370
+ payment_extra["token"] = token_type
371
+
372
+ # Add fee payer for non-EVM networks
373
+ fee_payer = get_fee_payer(network)
374
+ if fee_payer:
375
+ payment_extra["feePayer"] = fee_payer
376
+
377
+ # Merge user-provided extra
378
+ if extra:
379
+ payment_extra.update(extra)
380
+
381
+ if payment_extra:
382
+ payment_info["extra"] = payment_extra
383
+
384
+ return payment_info
@@ -53,6 +53,20 @@ from uvd_x402_sdk.networks.base import (
53
53
  register_network,
54
54
  )
55
55
 
56
+ # Algorand fee payer addresses are defined in uvd_x402_sdk.facilitator
57
+ # Import here for convenience
58
+ try:
59
+ from uvd_x402_sdk.facilitator import (
60
+ ALGORAND_FEE_PAYER_MAINNET,
61
+ ALGORAND_FEE_PAYER_TESTNET,
62
+ get_fee_payer,
63
+ )
64
+ except ImportError:
65
+ # Fallback if facilitator module not loaded yet
66
+ ALGORAND_FEE_PAYER_MAINNET = "KIMS5H6QLCUDL65L5UBTOXDPWLMTS7N3AAC3I6B2NCONEI5QIVK7LH2C2I"
67
+ ALGORAND_FEE_PAYER_TESTNET = "5DPPDQNYUPCTXRZWRYSF3WPYU6RKAUR25F3YG4EKXQRHV5AUAI62H5GXL4"
68
+ get_fee_payer = None # type: ignore
69
+
56
70
 
57
71
  # =============================================================================
58
72
  # Algorand Networks Configuration
@@ -548,3 +562,36 @@ def build_x402_payment_request(
548
562
  "network": network,
549
563
  "payload": payload.to_dict(),
550
564
  }
565
+
566
+
567
+ def get_algorand_fee_payer(network_name: str = "algorand") -> str:
568
+ """
569
+ Get the fee payer address for an Algorand network.
570
+
571
+ The fee payer is the facilitator address that pays transaction fees
572
+ for the atomic group. This address is used to construct Transaction 0
573
+ (the fee payment transaction).
574
+
575
+ Args:
576
+ network_name: Network name ('algorand' or 'algorand-testnet')
577
+
578
+ Returns:
579
+ Fee payer address for the specified network
580
+
581
+ Example:
582
+ >>> get_algorand_fee_payer("algorand")
583
+ 'KIMS5H6QLCUDL65L5UBTOXDPWLMTS7N3AAC3I6B2NCONEI5QIVK7LH2C2I'
584
+ >>> get_algorand_fee_payer("algorand-testnet")
585
+ '5DPPDQNYUPCTXRZWRYSF3WPYU6RKAUR25F3YG4EKXQRHV5AUAI62H5GXL4'
586
+ """
587
+ # Use facilitator module if available
588
+ if get_fee_payer is not None:
589
+ fee_payer = get_fee_payer(network_name)
590
+ if fee_payer:
591
+ return fee_payer
592
+
593
+ # Fallback to direct lookup
594
+ network_lower = network_name.lower()
595
+ if "testnet" in network_lower:
596
+ return ALGORAND_FEE_PAYER_TESTNET
597
+ return ALGORAND_FEE_PAYER_MAINNET
@@ -32,6 +32,20 @@ from uvd_x402_sdk.networks.base import (
32
32
  register_network,
33
33
  )
34
34
 
35
+ # NEAR fee payer addresses are defined in uvd_x402_sdk.facilitator
36
+ # Import here for convenience
37
+ try:
38
+ from uvd_x402_sdk.facilitator import (
39
+ NEAR_FEE_PAYER_MAINNET,
40
+ NEAR_FEE_PAYER_TESTNET,
41
+ get_fee_payer,
42
+ )
43
+ except ImportError:
44
+ # Fallback if facilitator module not loaded yet
45
+ NEAR_FEE_PAYER_MAINNET = "uvd-facilitator.near"
46
+ NEAR_FEE_PAYER_TESTNET = "uvd-facilitator.testnet"
47
+ get_fee_payer = None # type: ignore
48
+
35
49
  # NEP-366 hash prefix: (2^30 + 366) = 1073742190
36
50
  NEP366_PREFIX = ((2**30) + 366).to_bytes(4, 'little')
37
51
 
@@ -395,3 +409,35 @@ def is_valid_near_account_id(account_id: str) -> bool:
395
409
 
396
410
  allowed = set('abcdefghijklmnopqrstuvwxyz0123456789_-.')
397
411
  return all(c in allowed for c in account_id)
412
+
413
+
414
+ def get_near_fee_payer(network_name: str = "near") -> str:
415
+ """
416
+ Get the fee payer account ID for a NEAR network.
417
+
418
+ The fee payer is the facilitator account that pays gas fees.
419
+ This account wraps the SignedDelegateAction and submits it.
420
+
421
+ Args:
422
+ network_name: Network name ('near' or 'near-testnet')
423
+
424
+ Returns:
425
+ Fee payer account ID for the specified network
426
+
427
+ Example:
428
+ >>> get_near_fee_payer("near")
429
+ 'uvd-facilitator.near'
430
+ >>> get_near_fee_payer("near-testnet")
431
+ 'uvd-facilitator.testnet'
432
+ """
433
+ # Use facilitator module if available
434
+ if get_fee_payer is not None:
435
+ fee_payer = get_fee_payer(network_name)
436
+ if fee_payer:
437
+ return fee_payer
438
+
439
+ # Fallback to direct lookup
440
+ network_lower = network_name.lower()
441
+ if "testnet" in network_lower:
442
+ return NEAR_FEE_PAYER_TESTNET
443
+ return NEAR_FEE_PAYER_MAINNET
@@ -38,6 +38,24 @@ from uvd_x402_sdk.networks.base import (
38
38
  register_network,
39
39
  )
40
40
 
41
+ # SVM fee payer addresses are defined in uvd_x402_sdk.facilitator
42
+ # Import here for convenience
43
+ try:
44
+ from uvd_x402_sdk.facilitator import (
45
+ SOLANA_FEE_PAYER_MAINNET,
46
+ SOLANA_FEE_PAYER_DEVNET,
47
+ FOGO_FEE_PAYER_MAINNET,
48
+ FOGO_FEE_PAYER_TESTNET,
49
+ get_fee_payer,
50
+ )
51
+ except ImportError:
52
+ # Fallback if facilitator module not loaded yet
53
+ SOLANA_FEE_PAYER_MAINNET = "F742C4VfFLQ9zRQyithoj5229ZgtX2WqKCSFKgH2EThq"
54
+ SOLANA_FEE_PAYER_DEVNET = "6xNPewUdKRbEZDReQdpyfNUdgNg8QRc8Mt263T5GZSRv"
55
+ FOGO_FEE_PAYER_MAINNET = "F742C4VfFLQ9zRQyithoj5229ZgtX2WqKCSFKgH2EThq"
56
+ FOGO_FEE_PAYER_TESTNET = "6xNPewUdKRbEZDReQdpyfNUdgNg8QRc8Mt263T5GZSRv"
57
+ get_fee_payer = None # type: ignore
58
+
41
59
 
42
60
  # =============================================================================
43
61
  # SVM Networks Configuration
@@ -351,3 +369,43 @@ def is_token_2022(token_type: str) -> bool:
351
369
  True if token uses Token2022, False for standard SPL
352
370
  """
353
371
  return token_type.lower() in TOKEN_2022_TOKENS
372
+
373
+
374
+ def get_svm_fee_payer(network_name: str = "solana") -> str:
375
+ """
376
+ Get the fee payer address for an SVM network.
377
+
378
+ The fee payer is the facilitator address that pays transaction fees.
379
+ This address should be used as the fee payer in VersionedTransaction.
380
+
381
+ Args:
382
+ network_name: Network name ('solana', 'solana-devnet', 'fogo', 'fogo-testnet')
383
+
384
+ Returns:
385
+ Fee payer address for the specified network
386
+
387
+ Example:
388
+ >>> get_svm_fee_payer("solana")
389
+ 'F742C4VfFLQ9zRQyithoj5229ZgtX2WqKCSFKgH2EThq'
390
+ >>> get_svm_fee_payer("solana-devnet")
391
+ '6xNPewUdKRbEZDReQdpyfNUdgNg8QRc8Mt263T5GZSRv'
392
+ """
393
+ # Use facilitator module if available
394
+ if get_fee_payer is not None:
395
+ fee_payer = get_fee_payer(network_name)
396
+ if fee_payer:
397
+ return fee_payer
398
+
399
+ # Fallback to direct lookup
400
+ network_lower = network_name.lower()
401
+ if "fogo" in network_lower:
402
+ if "testnet" in network_lower:
403
+ return FOGO_FEE_PAYER_TESTNET
404
+ return FOGO_FEE_PAYER_MAINNET
405
+ elif "devnet" in network_lower:
406
+ return SOLANA_FEE_PAYER_DEVNET
407
+ return SOLANA_FEE_PAYER_MAINNET
408
+
409
+
410
+ # Alias for backward compatibility
411
+ get_solana_fee_payer = get_svm_fee_payer
@@ -19,6 +19,20 @@ from uvd_x402_sdk.networks.base import (
19
19
  register_network,
20
20
  )
21
21
 
22
+ # Stellar fee payer addresses are defined in uvd_x402_sdk.facilitator
23
+ # Import here for convenience
24
+ try:
25
+ from uvd_x402_sdk.facilitator import (
26
+ STELLAR_FEE_PAYER_MAINNET,
27
+ STELLAR_FEE_PAYER_TESTNET,
28
+ get_fee_payer,
29
+ )
30
+ except ImportError:
31
+ # Fallback if facilitator module not loaded yet
32
+ STELLAR_FEE_PAYER_MAINNET = "GCHPGXJT2WFFRFCA5TV4G4E3PMMXLNIDUH27PKDYA4QJ2XGYZWGFZNHB"
33
+ STELLAR_FEE_PAYER_TESTNET = "GBBFZMLUJEZVI32EN4XA2KPP445XIBTMTRBLYWFIL556RDTHS2OWFQ2Z"
34
+ get_fee_payer = None # type: ignore
35
+
22
36
  # Stellar Mainnet
23
37
  STELLAR = NetworkConfig(
24
38
  name="stellar",
@@ -127,3 +141,35 @@ def calculate_expiration_ledger(current_ledger: int, validity_ledgers: int = 60)
127
141
  Expiration ledger number
128
142
  """
129
143
  return current_ledger + validity_ledgers
144
+
145
+
146
+ def get_stellar_fee_payer(network_name: str = "stellar") -> str:
147
+ """
148
+ Get the fee payer address for a Stellar network.
149
+
150
+ The fee payer is the facilitator address that pays XLM transaction fees.
151
+ This address wraps the SorobanAuthorizationEntry in a fee-bump transaction.
152
+
153
+ Args:
154
+ network_name: Network name ('stellar' or 'stellar-testnet')
155
+
156
+ Returns:
157
+ Fee payer public key (G... address) for the specified network
158
+
159
+ Example:
160
+ >>> get_stellar_fee_payer("stellar")
161
+ 'GCHPGXJT2WFFRFCA5TV4G4E3PMMXLNIDUH27PKDYA4QJ2XGYZWGFZNHB'
162
+ >>> get_stellar_fee_payer("stellar-testnet")
163
+ 'GBBFZMLUJEZVI32EN4XA2KPP445XIBTMTRBLYWFIL556RDTHS2OWFQ2Z'
164
+ """
165
+ # Use facilitator module if available
166
+ if get_fee_payer is not None:
167
+ fee_payer = get_fee_payer(network_name)
168
+ if fee_payer:
169
+ return fee_payer
170
+
171
+ # Fallback to direct lookup
172
+ network_lower = network_name.lower()
173
+ if "testnet" in network_lower:
174
+ return STELLAR_FEE_PAYER_TESTNET
175
+ return STELLAR_FEE_PAYER_MAINNET
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: uvd-x402-sdk
3
- Version: 0.4.2
3
+ Version: 0.5.0
4
4
  Summary: Python SDK for x402 payments - gasless crypto payments across 16 blockchains with multi-stablecoin support (USDC, EURC, AUSD, PYUSD, USDT)
5
5
  Author-email: Ultravioleta DAO <dev@ultravioletadao.xyz>
6
6
  Project-URL: Homepage, https://github.com/UltravioletaDAO/uvd-x402-sdk-python
@@ -1,8 +1,9 @@
1
- uvd_x402_sdk/__init__.py,sha256=9nsWXd6hA-22hXr8NM_JioVWzJbFu3wIllmKe9O8DW0,4901
1
+ uvd_x402_sdk/__init__.py,sha256=S_oA8b99W7CP6_RPtOlc7ZotYLvdTZJM9Sqc2s3O6zo,6261
2
2
  uvd_x402_sdk/client.py,sha256=QbK22DtC3HmvvCezphQ-UsYX468vKrIN-M_wF4pv9cM,18389
3
3
  uvd_x402_sdk/config.py,sha256=BNGnX2RwZ_ELIcSKU7RkwTUcln4LMFZdCwG1ptASKN8,8644
4
4
  uvd_x402_sdk/decorators.py,sha256=XJ7V4554hsa-AVDrizF1oKmeTysg5zlkQRcaeGBI73E,9767
5
5
  uvd_x402_sdk/exceptions.py,sha256=kzYNHFn41dSOZ5HaBDrbf4tdwJYQs1YcU9YybTrGZxo,6527
6
+ uvd_x402_sdk/facilitator.py,sha256=kX--Uk6BYlDCalKD-2LpPOgEi-t26C-sjcxgalphJp8,11859
6
7
  uvd_x402_sdk/models.py,sha256=nfA8Ak0pyIveiNpstjqxnQ6-aPQ8TRJzJq2LJwlJc-E,14282
7
8
  uvd_x402_sdk/response.py,sha256=4wxH4kWg1F8pokKEbN1kDvDF55cQxmQUN88HTxXht8g,13608
8
9
  uvd_x402_sdk/integrations/__init__.py,sha256=Hq1Y0YIMYWBAtmbOLXDC40KQuCrbSpQVjAqEsbjH56s,1912
@@ -11,14 +12,14 @@ uvd_x402_sdk/integrations/fastapi_integration.py,sha256=j5h1IJwFLBBoWov7ANLCFaxe
11
12
  uvd_x402_sdk/integrations/flask_integration.py,sha256=0iQKO5-WRxE76Pv-1jEl4lYhjCLmq_R-jxR5g9xIcKw,8825
12
13
  uvd_x402_sdk/integrations/lambda_integration.py,sha256=nRf4o3nS6Syx-d5P0kEhz66y7jb_S4w-mwaIazgiA9c,10184
13
14
  uvd_x402_sdk/networks/__init__.py,sha256=LKl_TljVoCDb27YB4X_VbQN8XKbdwWFAsCwgiqQtlgo,2092
14
- uvd_x402_sdk/networks/algorand.py,sha256=zcpU5U4V9weiFZ_Zfc-khWaadTldO6KKOIg5ZdxXaRs,17819
15
+ uvd_x402_sdk/networks/algorand.py,sha256=_eQfR8xguvTloJBR7_yGbWjC2KgmPux44pXeDVM7b0w,19458
15
16
  uvd_x402_sdk/networks/base.py,sha256=gOPWfqasGbgtg9w2uG5pWnfjdOEain92L2egnDSBguc,14863
16
17
  uvd_x402_sdk/networks/evm.py,sha256=4IbeaMH2I1c9DYCijghys0qYNeL2Nl92IMKLwq-b0Zg,10065
17
- uvd_x402_sdk/networks/near.py,sha256=sxbxT1NqjcENh8ysFLDpAx5DGizf1EI0YjwgviLfqcY,11608
18
- uvd_x402_sdk/networks/solana.py,sha256=-snAeE3OxJU_kaGb_z4NOU6k0SGAD4DBPhJcPbgrdgo,11675
19
- uvd_x402_sdk/networks/stellar.py,sha256=c-6re-dVc2-6gJ5rL4krUTaFsPz5vkactOJD-0wowBA,3534
20
- uvd_x402_sdk-0.4.2.dist-info/LICENSE,sha256=OcLzB_iSgMbvk7b0dlyvleY_IbL2WUaPxvn1CHw2uAc,1073
21
- uvd_x402_sdk-0.4.2.dist-info/METADATA,sha256=dOuyD4J4cWQtHx3Ia0uhst7xIf4BMwhEr3ULgPkbljw,29711
22
- uvd_x402_sdk-0.4.2.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
23
- uvd_x402_sdk-0.4.2.dist-info/top_level.txt,sha256=Exyjj_Kl7CDAGFMi72lT9oFPOYiRNZb3l8tr906mMmc,13
24
- uvd_x402_sdk-0.4.2.dist-info/RECORD,,
18
+ uvd_x402_sdk/networks/near.py,sha256=HMMWJr-Jckj3YQTbSXkavlJWZB7ZV8OQX0bq8DuVxIg,12990
19
+ uvd_x402_sdk/networks/solana.py,sha256=GVUeh0O2W6f8Vbgoom2UQSGI8joZV68Pnpzhh87Bnqg,13640
20
+ uvd_x402_sdk/networks/stellar.py,sha256=ZuF-crx41N9MxSsgf2kAy88fsM-xvDyXY_D2ND6M71Y,5142
21
+ uvd_x402_sdk-0.5.0.dist-info/LICENSE,sha256=OcLzB_iSgMbvk7b0dlyvleY_IbL2WUaPxvn1CHw2uAc,1073
22
+ uvd_x402_sdk-0.5.0.dist-info/METADATA,sha256=2BkT_uVVDdWwuAnmjgS61pNXJWKP1e2wjWGf9R8VAEw,29711
23
+ uvd_x402_sdk-0.5.0.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
24
+ uvd_x402_sdk-0.5.0.dist-info/top_level.txt,sha256=Exyjj_Kl7CDAGFMi72lT9oFPOYiRNZb3l8tr906mMmc,13
25
+ uvd_x402_sdk-0.5.0.dist-info/RECORD,,