uvd-x402-sdk 0.4.1__tar.gz → 0.5.0__tar.gz
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-0.4.1/src/uvd_x402_sdk.egg-info → uvd_x402_sdk-0.5.0}/PKG-INFO +4 -2
- {uvd_x402_sdk-0.4.1 → uvd_x402_sdk-0.5.0}/pyproject.toml +5 -2
- {uvd_x402_sdk-0.4.1 → uvd_x402_sdk-0.5.0}/src/uvd_x402_sdk/__init__.py +48 -4
- uvd_x402_sdk-0.5.0/src/uvd_x402_sdk/facilitator.py +384 -0
- uvd_x402_sdk-0.5.0/src/uvd_x402_sdk/networks/algorand.py +597 -0
- {uvd_x402_sdk-0.4.1 → uvd_x402_sdk-0.5.0}/src/uvd_x402_sdk/networks/near.py +46 -0
- {uvd_x402_sdk-0.4.1 → uvd_x402_sdk-0.5.0}/src/uvd_x402_sdk/networks/solana.py +58 -0
- {uvd_x402_sdk-0.4.1 → uvd_x402_sdk-0.5.0}/src/uvd_x402_sdk/networks/stellar.py +46 -0
- {uvd_x402_sdk-0.4.1 → uvd_x402_sdk-0.5.0/src/uvd_x402_sdk.egg-info}/PKG-INFO +4 -2
- {uvd_x402_sdk-0.4.1 → uvd_x402_sdk-0.5.0}/src/uvd_x402_sdk.egg-info/SOURCES.txt +1 -0
- {uvd_x402_sdk-0.4.1 → uvd_x402_sdk-0.5.0}/src/uvd_x402_sdk.egg-info/requires.txt +4 -1
- uvd_x402_sdk-0.4.1/src/uvd_x402_sdk/networks/algorand.py +0 -287
- {uvd_x402_sdk-0.4.1 → uvd_x402_sdk-0.5.0}/LICENSE +0 -0
- {uvd_x402_sdk-0.4.1 → uvd_x402_sdk-0.5.0}/README.md +0 -0
- {uvd_x402_sdk-0.4.1 → uvd_x402_sdk-0.5.0}/setup.cfg +0 -0
- {uvd_x402_sdk-0.4.1 → uvd_x402_sdk-0.5.0}/src/uvd_x402_sdk/client.py +0 -0
- {uvd_x402_sdk-0.4.1 → uvd_x402_sdk-0.5.0}/src/uvd_x402_sdk/config.py +0 -0
- {uvd_x402_sdk-0.4.1 → uvd_x402_sdk-0.5.0}/src/uvd_x402_sdk/decorators.py +0 -0
- {uvd_x402_sdk-0.4.1 → uvd_x402_sdk-0.5.0}/src/uvd_x402_sdk/exceptions.py +0 -0
- {uvd_x402_sdk-0.4.1 → uvd_x402_sdk-0.5.0}/src/uvd_x402_sdk/integrations/__init__.py +0 -0
- {uvd_x402_sdk-0.4.1 → uvd_x402_sdk-0.5.0}/src/uvd_x402_sdk/integrations/django_integration.py +0 -0
- {uvd_x402_sdk-0.4.1 → uvd_x402_sdk-0.5.0}/src/uvd_x402_sdk/integrations/fastapi_integration.py +0 -0
- {uvd_x402_sdk-0.4.1 → uvd_x402_sdk-0.5.0}/src/uvd_x402_sdk/integrations/flask_integration.py +0 -0
- {uvd_x402_sdk-0.4.1 → uvd_x402_sdk-0.5.0}/src/uvd_x402_sdk/integrations/lambda_integration.py +0 -0
- {uvd_x402_sdk-0.4.1 → uvd_x402_sdk-0.5.0}/src/uvd_x402_sdk/models.py +0 -0
- {uvd_x402_sdk-0.4.1 → uvd_x402_sdk-0.5.0}/src/uvd_x402_sdk/networks/__init__.py +0 -0
- {uvd_x402_sdk-0.4.1 → uvd_x402_sdk-0.5.0}/src/uvd_x402_sdk/networks/base.py +0 -0
- {uvd_x402_sdk-0.4.1 → uvd_x402_sdk-0.5.0}/src/uvd_x402_sdk/networks/evm.py +0 -0
- {uvd_x402_sdk-0.4.1 → uvd_x402_sdk-0.5.0}/src/uvd_x402_sdk/response.py +0 -0
- {uvd_x402_sdk-0.4.1 → uvd_x402_sdk-0.5.0}/src/uvd_x402_sdk.egg-info/dependency_links.txt +0 -0
- {uvd_x402_sdk-0.4.1 → uvd_x402_sdk-0.5.0}/src/uvd_x402_sdk.egg-info/top_level.txt +0 -0
- {uvd_x402_sdk-0.4.1 → uvd_x402_sdk-0.5.0}/tests/test_client.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: uvd-x402-sdk
|
|
3
|
-
Version: 0.
|
|
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
|
|
@@ -36,8 +36,10 @@ Provides-Extra: django
|
|
|
36
36
|
Requires-Dist: django>=4.0.0; extra == "django"
|
|
37
37
|
Provides-Extra: aws
|
|
38
38
|
Requires-Dist: boto3>=1.26.0; extra == "aws"
|
|
39
|
+
Provides-Extra: algorand
|
|
40
|
+
Requires-Dist: py-algorand-sdk>=2.0.0; extra == "algorand"
|
|
39
41
|
Provides-Extra: all
|
|
40
|
-
Requires-Dist: uvd-x402-sdk[aws,django,fastapi,flask,web3]; extra == "all"
|
|
42
|
+
Requires-Dist: uvd-x402-sdk[algorand,aws,django,fastapi,flask,web3]; extra == "all"
|
|
41
43
|
Provides-Extra: dev
|
|
42
44
|
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
43
45
|
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "uvd-x402-sdk"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.5.0"
|
|
8
8
|
description = "Python SDK for x402 payments - gasless crypto payments across 16 blockchains with multi-stablecoin support (USDC, EURC, AUSD, PYUSD, USDT)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -65,8 +65,11 @@ django = [
|
|
|
65
65
|
aws = [
|
|
66
66
|
"boto3>=1.26.0",
|
|
67
67
|
]
|
|
68
|
+
algorand = [
|
|
69
|
+
"py-algorand-sdk>=2.0.0",
|
|
70
|
+
]
|
|
68
71
|
all = [
|
|
69
|
-
"uvd-x402-sdk[web3,flask,fastapi,django,aws]",
|
|
72
|
+
"uvd-x402-sdk[web3,flask,fastapi,django,aws,algorand]",
|
|
70
73
|
]
|
|
71
74
|
dev = [
|
|
72
75
|
"pytest>=7.0.0",
|
|
@@ -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
|
|
6
|
-
|
|
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 (
|
|
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.
|
|
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
|