uvd-x402-sdk 0.4.1__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
@@ -1,33 +1,51 @@
1
1
  """
2
- Algorand network configurations.
2
+ Algorand network configurations for x402 payments.
3
3
 
4
4
  This module supports Algorand blockchain networks:
5
- - Algorand mainnet
6
- - Algorand testnet
5
+ - Algorand mainnet (network: "algorand-mainnet" or "algorand")
6
+ - Algorand testnet (network: "algorand-testnet")
7
7
 
8
8
  Algorand uses ASA (Algorand Standard Assets) for USDC:
9
9
  - Mainnet USDC ASA ID: 31566704
10
10
  - Testnet USDC ASA ID: 10458941
11
11
 
12
- Payment Flow:
13
- 1. User creates a signed ASA transfer transaction via Pera Wallet
14
- 2. Transaction transfers USDC from user to recipient
15
- 3. Facilitator submits the pre-signed transaction on-chain
16
- 4. User pays ZERO transaction fees (facilitator covers fees)
17
-
18
- Transaction Structure:
19
- - ASA TransferAsset transaction
20
- - Signed by user wallet (Pera Wallet)
21
- - Facilitator submits the signed transaction
12
+ Payment Flow (GoPlausible x402-avm Atomic Group Spec):
13
+ 1. Client creates an ATOMIC GROUP of TWO transactions:
14
+ - Transaction 0 (fee tx): Zero-amount payment FROM facilitator TO facilitator
15
+ This transaction pays fees for both txns. Client creates this UNSIGNED.
16
+ - Transaction 1 (payment tx): ASA transfer FROM client TO merchant
17
+ Client SIGNS this transaction.
18
+ 2. Both transactions share a GROUP ID computed by Algorand SDK.
19
+ 3. Fee pooling: Transaction 0's fee covers Transaction 1's fee (gasless).
20
+ 4. Facilitator completes: Signs transaction 0 and submits the atomic group.
21
+
22
+ Payload Format:
23
+ {
24
+ "x402Version": 1,
25
+ "scheme": "exact",
26
+ "network": "algorand-mainnet",
27
+ "payload": {
28
+ "paymentIndex": 1,
29
+ "paymentGroup": [
30
+ "<base64-msgpack-UNSIGNED-fee-tx>",
31
+ "<base64-msgpack-SIGNED-asa-transfer>"
32
+ ]
33
+ }
34
+ }
22
35
 
23
36
  Address Format:
24
37
  - Algorand addresses are 58 characters, base32 encoded
25
38
  - Example: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ
39
+
40
+ Dependencies:
41
+ - algosdk (optional): Required for building atomic groups
42
+ Install with: pip install py-algorand-sdk
26
43
  """
27
44
 
28
45
  import base64
29
46
  import re
30
- from typing import Any, Dict, Optional
47
+ from dataclasses import dataclass
48
+ from typing import Any, Callable, Dict, List, Optional
31
49
 
32
50
  from uvd_x402_sdk.networks.base import (
33
51
  NetworkConfig,
@@ -35,6 +53,20 @@ from uvd_x402_sdk.networks.base import (
35
53
  register_network,
36
54
  )
37
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
+
38
70
 
39
71
  # =============================================================================
40
72
  # Algorand Networks Configuration
@@ -63,6 +95,8 @@ ALGORAND = NetworkConfig(
63
95
  "genesis_id": "mainnet-v1.0",
64
96
  # Genesis hash (for CAIP-2)
65
97
  "genesis_hash": "wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=",
98
+ # x402 network name (facilitator expects this format)
99
+ "x402_network": "algorand-mainnet",
66
100
  },
67
101
  )
68
102
 
@@ -89,6 +123,8 @@ ALGORAND_TESTNET = NetworkConfig(
89
123
  "genesis_id": "testnet-v1.0",
90
124
  # Genesis hash
91
125
  "genesis_hash": "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
126
+ # x402 network name (facilitator expects this format)
127
+ "x402_network": "algorand-testnet",
92
128
  },
93
129
  )
94
130
 
@@ -97,6 +133,34 @@ register_network(ALGORAND)
97
133
  register_network(ALGORAND_TESTNET)
98
134
 
99
135
 
136
+ # =============================================================================
137
+ # Algorand Payment Payload (x402-avm Atomic Group Spec)
138
+ # =============================================================================
139
+
140
+
141
+ @dataclass
142
+ class AlgorandPaymentPayload:
143
+ """
144
+ Algorand payment payload for x402 atomic group format.
145
+
146
+ Attributes:
147
+ payment_index: Index of the payment transaction in the group (typically 1)
148
+ payment_group: List of base64-encoded msgpack transactions
149
+ [0] = unsigned fee transaction
150
+ [1] = signed ASA transfer transaction
151
+ """
152
+
153
+ payment_index: int
154
+ payment_group: List[str]
155
+
156
+ def to_dict(self) -> Dict[str, Any]:
157
+ """Convert to dictionary for JSON serialization."""
158
+ return {
159
+ "paymentIndex": self.payment_index,
160
+ "paymentGroup": self.payment_group,
161
+ }
162
+
163
+
100
164
  # =============================================================================
101
165
  # Algorand-specific utilities
102
166
  # =============================================================================
@@ -162,59 +226,99 @@ def is_valid_algorand_address(address: str) -> bool:
162
226
 
163
227
  def validate_algorand_payload(payload: Dict[str, Any]) -> bool:
164
228
  """
165
- Validate an Algorand payment payload structure.
229
+ Validate an Algorand payment payload structure (x402-avm atomic group format).
166
230
 
167
231
  The payload must contain:
168
- - from: Sender's Algorand address
169
- - to: Recipient's Algorand address
170
- - amount: Amount in base units (microUSDC)
171
- - assetId: ASA ID for USDC
172
- - signedTxn: Base64-encoded signed transaction
232
+ - paymentIndex: Index of the payment transaction (typically 1)
233
+ - paymentGroup: List of base64-encoded msgpack transactions
234
+ - [0]: Unsigned fee transaction (facilitator -> facilitator)
235
+ - [1]: Signed ASA transfer (client -> merchant)
173
236
 
174
237
  Args:
175
- payload: Payload dictionary from x402 payment
238
+ payload: Payload dictionary from x402 payment (the inner "payload" field)
176
239
 
177
240
  Returns:
178
241
  True if valid, raises ValueError if invalid
242
+
243
+ Example:
244
+ >>> payload = {
245
+ ... "paymentIndex": 1,
246
+ ... "paymentGroup": [
247
+ ... "base64-unsigned-fee-tx...",
248
+ ... "base64-signed-payment-tx..."
249
+ ... ]
250
+ ... }
251
+ >>> validate_algorand_payload(payload)
252
+ True
179
253
  """
180
- required_fields = ["from", "to", "amount", "assetId", "signedTxn"]
254
+ # Check required fields
255
+ if "paymentIndex" not in payload:
256
+ raise ValueError("Algorand payload missing 'paymentIndex' field")
257
+ if "paymentGroup" not in payload:
258
+ raise ValueError("Algorand payload missing 'paymentGroup' field")
259
+
260
+ # Validate paymentIndex
261
+ payment_index = payload["paymentIndex"]
262
+ if not isinstance(payment_index, int) or payment_index < 0:
263
+ raise ValueError(f"paymentIndex must be a non-negative integer: {payment_index}")
264
+
265
+ # Validate paymentGroup
266
+ payment_group = payload["paymentGroup"]
267
+ if not isinstance(payment_group, list):
268
+ raise ValueError("paymentGroup must be a list")
269
+
270
+ if len(payment_group) < 2:
271
+ raise ValueError(
272
+ f"paymentGroup must contain at least 2 transactions, got {len(payment_group)}"
273
+ )
274
+
275
+ if payment_index >= len(payment_group):
276
+ raise ValueError(
277
+ f"paymentIndex ({payment_index}) out of range for paymentGroup "
278
+ f"(length {len(payment_group)})"
279
+ )
280
+
281
+ # Validate each transaction in the group is valid base64
282
+ for i, txn_b64 in enumerate(payment_group):
283
+ if not isinstance(txn_b64, str):
284
+ raise ValueError(f"paymentGroup[{i}] must be a string")
285
+
286
+ try:
287
+ txn_bytes = base64.b64decode(txn_b64)
288
+ if len(txn_bytes) < 50:
289
+ raise ValueError(
290
+ f"paymentGroup[{i}] transaction too short: {len(txn_bytes)} bytes"
291
+ )
292
+ except Exception as e:
293
+ raise ValueError(
294
+ f"paymentGroup[{i}] is not valid base64: {e}"
295
+ ) from e
181
296
 
182
- for field in required_fields:
183
- if field not in payload:
184
- raise ValueError(f"Algorand payload missing '{field}' field")
297
+ return True
185
298
 
186
- # Validate addresses
187
- if not is_valid_algorand_address(payload["from"]):
188
- raise ValueError(f"Invalid 'from' address: {payload['from']}")
189
- if not is_valid_algorand_address(payload["to"]):
190
- raise ValueError(f"Invalid 'to' address: {payload['to']}")
191
299
 
192
- # Validate amount
193
- try:
194
- amount = int(payload["amount"])
195
- if amount <= 0:
196
- raise ValueError(f"Amount must be positive: {amount}")
197
- except (ValueError, TypeError) as e:
198
- raise ValueError(f"Invalid amount: {payload['amount']}") from e
300
+ def get_x402_network_name(network_name: str) -> str:
301
+ """
302
+ Get the x402 network name for an Algorand network.
199
303
 
200
- # Validate assetId
201
- try:
202
- asset_id = int(payload["assetId"])
203
- if asset_id <= 0:
204
- raise ValueError(f"Asset ID must be positive: {asset_id}")
205
- except (ValueError, TypeError) as e:
206
- raise ValueError(f"Invalid assetId: {payload['assetId']}") from e
304
+ The facilitator expects "algorand-mainnet" or "algorand-testnet".
207
305
 
208
- # Validate signedTxn is valid base64
209
- try:
210
- signed_txn = payload["signedTxn"]
211
- tx_bytes = base64.b64decode(signed_txn)
212
- if len(tx_bytes) < 50:
213
- raise ValueError(f"Signed transaction too short: {len(tx_bytes)} bytes")
214
- except Exception as e:
215
- raise ValueError(f"Invalid signedTxn (not valid base64): {e}") from e
306
+ Args:
307
+ network_name: SDK network name ('algorand' or 'algorand-testnet')
216
308
 
217
- return True
309
+ Returns:
310
+ x402 network name ('algorand-mainnet' or 'algorand-testnet')
311
+ """
312
+ from uvd_x402_sdk.networks.base import get_network
313
+
314
+ network = get_network(network_name)
315
+ if not network:
316
+ # Default mapping
317
+ if network_name == "algorand":
318
+ return "algorand-mainnet"
319
+ return network_name
320
+
321
+ return network.extra_config.get("x402_network", network_name)
218
322
 
219
323
 
220
324
  def get_explorer_tx_url(network_name: str, tx_id: str) -> Optional[str]:
@@ -285,3 +389,209 @@ def get_usdc_asa_id(network_name: str) -> Optional[int]:
285
389
  return int(network.usdc_address)
286
390
  except (ValueError, TypeError):
287
391
  return None
392
+
393
+
394
+ # =============================================================================
395
+ # Atomic Group Builder (requires algosdk)
396
+ # =============================================================================
397
+
398
+
399
+ def build_atomic_group(
400
+ sender_address: str,
401
+ recipient_address: str,
402
+ amount: int,
403
+ asset_id: int,
404
+ facilitator_address: str,
405
+ sign_transaction: Callable,
406
+ algod_client: Optional[Any] = None,
407
+ suggested_params: Optional[Any] = None,
408
+ ) -> AlgorandPaymentPayload:
409
+ """
410
+ Build an Algorand atomic group for x402 payment.
411
+
412
+ This creates the two-transaction atomic group required by the facilitator:
413
+ - Transaction 0: Unsigned fee payment (facilitator -> facilitator, 0 amount)
414
+ - Transaction 1: Signed ASA transfer (sender -> recipient)
415
+
416
+ Requires: pip install py-algorand-sdk
417
+
418
+ Args:
419
+ sender_address: Client's Algorand address
420
+ recipient_address: Merchant's Algorand address (from payTo)
421
+ amount: Amount in micro-units (1 USDC = 1,000,000)
422
+ asset_id: USDC ASA ID (31566704 mainnet, 10458941 testnet)
423
+ facilitator_address: Facilitator address (from extra.feePayer)
424
+ sign_transaction: Function that signs a transaction.
425
+ Signature: (transaction) -> SignedTransaction
426
+ Can use algosdk's transaction.sign(private_key)
427
+ algod_client: Optional AlgodClient for getting suggested params.
428
+ If not provided, suggested_params must be given.
429
+ suggested_params: Optional SuggestedParams. If not provided,
430
+ algod_client.suggested_params() is called.
431
+
432
+ Returns:
433
+ AlgorandPaymentPayload with paymentIndex and paymentGroup
434
+
435
+ Raises:
436
+ ImportError: If algosdk is not installed
437
+ ValueError: If neither algod_client nor suggested_params provided
438
+
439
+ Example:
440
+ >>> from algosdk import transaction
441
+ >>> from algosdk.v2client import algod
442
+ >>>
443
+ >>> client = algod.AlgodClient("", "https://mainnet-api.algonode.cloud")
444
+ >>> payload = build_atomic_group(
445
+ ... sender_address="SENDER...",
446
+ ... recipient_address="MERCHANT...",
447
+ ... amount=1000000, # 1 USDC
448
+ ... asset_id=31566704,
449
+ ... facilitator_address="FACILITATOR...",
450
+ ... sign_transaction=lambda txn: txn.sign(private_key),
451
+ ... algod_client=client,
452
+ ... )
453
+ """
454
+ try:
455
+ from algosdk import encoding, transaction
456
+ except ImportError as e:
457
+ raise ImportError(
458
+ "algosdk is required for building atomic groups. "
459
+ "Install with: pip install py-algorand-sdk"
460
+ ) from e
461
+
462
+ # Get suggested params
463
+ if suggested_params is None:
464
+ if algod_client is None:
465
+ raise ValueError(
466
+ "Either algod_client or suggested_params must be provided"
467
+ )
468
+ suggested_params = algod_client.suggested_params()
469
+
470
+ # Transaction 0: Fee payment (facilitator -> facilitator, 0 amount)
471
+ # This transaction pays fees for both txns in the group
472
+ fee_txn = transaction.PaymentTxn(
473
+ sender=facilitator_address,
474
+ receiver=facilitator_address, # self-transfer
475
+ amt=0,
476
+ sp=suggested_params,
477
+ )
478
+ # Cover both transactions (1000 microAlgos each = 2000 total)
479
+ fee_txn.fee = 2000
480
+
481
+ # Transaction 1: ASA transfer (client -> merchant)
482
+ payment_txn = transaction.AssetTransferTxn(
483
+ sender=sender_address,
484
+ receiver=recipient_address,
485
+ amt=amount,
486
+ index=asset_id,
487
+ sp=suggested_params,
488
+ )
489
+ # Fee paid by transaction 0
490
+ payment_txn.fee = 0
491
+
492
+ # Assign group ID to both transactions
493
+ group_id = transaction.calculate_group_id([fee_txn, payment_txn])
494
+ fee_txn.group = group_id
495
+ payment_txn.group = group_id
496
+
497
+ # Encode fee transaction (UNSIGNED - facilitator will sign)
498
+ unsigned_fee_txn_bytes = encoding.msgpack_encode(fee_txn)
499
+ unsigned_fee_txn_base64 = base64.b64encode(unsigned_fee_txn_bytes).decode("utf-8")
500
+
501
+ # Sign and encode payment transaction
502
+ signed_payment_txn = sign_transaction(payment_txn)
503
+ signed_payment_txn_bytes = encoding.msgpack_encode(signed_payment_txn)
504
+ signed_payment_txn_base64 = base64.b64encode(signed_payment_txn_bytes).decode("utf-8")
505
+
506
+ return AlgorandPaymentPayload(
507
+ payment_index=1, # Index of the payment transaction
508
+ payment_group=[
509
+ unsigned_fee_txn_base64, # Transaction 0: unsigned fee tx
510
+ signed_payment_txn_base64, # Transaction 1: signed payment tx
511
+ ],
512
+ )
513
+
514
+
515
+ def create_private_key_signer(private_key: str) -> Callable:
516
+ """
517
+ Create a transaction signer from a private key.
518
+
519
+ Args:
520
+ private_key: Algorand private key (base64 encoded)
521
+
522
+ Returns:
523
+ Function that signs transactions
524
+
525
+ Example:
526
+ >>> signer = create_private_key_signer(my_private_key)
527
+ >>> payload = build_atomic_group(..., sign_transaction=signer)
528
+ """
529
+ def sign(txn: Any) -> Any:
530
+ return txn.sign(private_key)
531
+ return sign
532
+
533
+
534
+ def build_x402_payment_request(
535
+ payload: AlgorandPaymentPayload,
536
+ network: str = "algorand-mainnet",
537
+ scheme: str = "exact",
538
+ version: int = 1,
539
+ ) -> Dict[str, Any]:
540
+ """
541
+ Build a complete x402 payment request for Algorand.
542
+
543
+ Args:
544
+ payload: AlgorandPaymentPayload from build_atomic_group()
545
+ network: Network name ("algorand-mainnet" or "algorand-testnet")
546
+ scheme: Payment scheme (default "exact")
547
+ version: x402 version (default 1)
548
+
549
+ Returns:
550
+ Complete x402 payment request dictionary
551
+
552
+ Example:
553
+ >>> payload = build_atomic_group(...)
554
+ >>> request = build_x402_payment_request(payload)
555
+ >>> # Send as X-PAYMENT header (base64 encoded JSON)
556
+ >>> import json, base64
557
+ >>> header = base64.b64encode(json.dumps(request).encode()).decode()
558
+ """
559
+ return {
560
+ "x402Version": version,
561
+ "scheme": scheme,
562
+ "network": network,
563
+ "payload": payload.to_dict(),
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.1
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
@@ -24,8 +24,10 @@ Description-Content-Type: text/markdown
24
24
  License-File: LICENSE
25
25
  Requires-Dist: httpx>=0.24.0
26
26
  Requires-Dist: pydantic>=2.0.0
27
+ Provides-Extra: algorand
28
+ Requires-Dist: py-algorand-sdk>=2.0.0; extra == "algorand"
27
29
  Provides-Extra: all
28
- Requires-Dist: uvd-x402-sdk[aws,django,fastapi,flask,web3]; extra == "all"
30
+ Requires-Dist: uvd-x402-sdk[algorand,aws,django,fastapi,flask,web3]; extra == "all"
29
31
  Provides-Extra: aws
30
32
  Requires-Dist: boto3>=1.26.0; extra == "aws"
31
33
  Provides-Extra: dev
@@ -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=ACaVMyXItRX3M29GPwwMMxECMjNXx3FlYARNBeAztH4,8601
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.1.dist-info/LICENSE,sha256=OcLzB_iSgMbvk7b0dlyvleY_IbL2WUaPxvn1CHw2uAc,1073
21
- uvd_x402_sdk-0.4.1.dist-info/METADATA,sha256=3cD7Kz9_fONGwbZVUBMOxXqglRmC_wikuBH8OWpIT24,29618
22
- uvd_x402_sdk-0.4.1.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
23
- uvd_x402_sdk-0.4.1.dist-info/top_level.txt,sha256=Exyjj_Kl7CDAGFMi72lT9oFPOYiRNZb3l8tr906mMmc,13
24
- uvd_x402_sdk-0.4.1.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,,