solanab-jup-python-sdk 2.0.3__py3-none-any.whl → 2.0.6__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.
@@ -15,11 +15,26 @@ class _CoreJupiterClient:
15
15
  """
16
16
 
17
17
  def __init__(self, api_key: Optional[str], private_key_env_var: str):
18
+ """
19
+ Initialize the core Jupiter client.
20
+
21
+ Args:
22
+ api_key: Optional API key for enhanced access to Jupiter API.
23
+ If provided, uses https://api.jup.ag endpoint.
24
+ private_key_env_var: Name of environment variable containing the
25
+ private key. Defaults to 'PRIVATE_KEY'.
26
+ """
18
27
  self.api_key = api_key
19
28
  self.base_url = "https://api.jup.ag" if api_key else "https://lite-api.jup.ag"
20
29
  self.private_key_env_var = private_key_env_var
21
30
 
22
31
  def _get_headers(self) -> dict[str, str]:
32
+ """
33
+ Get headers for GET requests.
34
+
35
+ Returns:
36
+ Dict containing headers with Accept and optional API key.
37
+ """
23
38
  headers = {
24
39
  "Accept": "application/json",
25
40
  }
@@ -28,6 +43,12 @@ class _CoreJupiterClient:
28
43
  return headers
29
44
 
30
45
  def _post_headers(self) -> dict[str, str]:
46
+ """
47
+ Get headers for POST requests.
48
+
49
+ Returns:
50
+ Dict containing headers with Accept, Content-Type, and optional API key.
51
+ """
31
52
  headers = {
32
53
  "Accept": "application/json",
33
54
  "Content-Type": "application/json",
@@ -58,13 +79,34 @@ class _CoreJupiterClient:
58
79
  raise ValueError(f"Invalid base58 private key format: {e}") from e
59
80
 
60
81
  def get_public_key(self) -> str:
82
+ """
83
+ Get the public key from the loaded private key.
84
+
85
+ Returns:
86
+ Public key as a base58-encoded string.
87
+ """
61
88
  wallet = Keypair.from_bytes(self._load_private_key_bytes())
62
89
  return str(wallet.pubkey())
63
90
 
64
91
  async def get_public_key_async(self) -> str:
92
+ """
93
+ Async wrapper for get_public_key().
94
+
95
+ Returns:
96
+ Public key as a base58-encoded string.
97
+ """
65
98
  return self.get_public_key()
66
99
 
67
100
  def _sign_base64_transaction(self, transaction_base64: str) -> VersionedTransaction:
101
+ """
102
+ Sign a base64-encoded transaction.
103
+
104
+ Args:
105
+ transaction_base64: Base64-encoded transaction string.
106
+
107
+ Returns:
108
+ Signed VersionedTransaction object.
109
+ """
68
110
  transaction_bytes = base64.b64decode(transaction_base64)
69
111
  versioned_transaction = VersionedTransaction.from_bytes(transaction_bytes)
70
112
  return self._sign_versioned_transaction(versioned_transaction)
@@ -72,6 +114,15 @@ class _CoreJupiterClient:
72
114
  def _sign_versioned_transaction(
73
115
  self, versioned_transaction: VersionedTransaction
74
116
  ) -> VersionedTransaction:
117
+ """
118
+ Sign a VersionedTransaction with the loaded private key.
119
+
120
+ Args:
121
+ versioned_transaction: VersionedTransaction to sign.
122
+
123
+ Returns:
124
+ Signed VersionedTransaction with signature applied.
125
+ """
75
126
  wallet = Keypair.from_bytes(self._load_private_key_bytes())
76
127
  account_keys = versioned_transaction.message.account_keys
77
128
  wallet_index = account_keys.index(wallet.pubkey())
@@ -87,6 +138,15 @@ class _CoreJupiterClient:
87
138
  def _serialize_versioned_transaction(
88
139
  self, versioned_transaction: VersionedTransaction
89
140
  ) -> str:
141
+ """
142
+ Serialize a VersionedTransaction to base64 string.
143
+
144
+ Args:
145
+ versioned_transaction: VersionedTransaction to serialize.
146
+
147
+ Returns:
148
+ Base64-encoded string representation of the transaction.
149
+ """
90
150
  return base64.b64encode(bytes(versioned_transaction)).decode("utf-8")
91
151
 
92
152
 
@@ -102,12 +162,29 @@ class JupiterClient(_CoreJupiterClient):
102
162
  private_key_env_var: str = "PRIVATE_KEY",
103
163
  client_kwargs: Optional[dict[str, Any]] = None,
104
164
  ):
165
+ """
166
+ Initialize the synchronous Jupiter client.
167
+
168
+ Args:
169
+ api_key: Optional API key for enhanced access to Jupiter API.
170
+ private_key_env_var: Name of environment variable containing the
171
+ private key.
172
+ client_kwargs: Optional kwargs to pass to curl_cffi Session.
173
+ Common options include 'proxies', 'timeout', 'impersonate'.
174
+ """
105
175
  super().__init__(api_key, private_key_env_var)
106
176
  kwargs = client_kwargs or {}
107
- kwargs.setdefault("impersonate", "chrome110")
177
+ # Use realworld random browser impersonation based on market share
178
+ # if not specified
179
+ kwargs.setdefault("impersonate", "realworld")
108
180
  self.client = requests.Session(**kwargs)
109
181
 
110
182
  def close(self) -> None:
183
+ """
184
+ Close the underlying HTTP session.
185
+
186
+ Always call this method when done to properly cleanup resources.
187
+ """
111
188
  self.client.close()
112
189
 
113
190
 
@@ -123,14 +200,37 @@ class AsyncJupiterClient(_CoreJupiterClient):
123
200
  private_key_env_var: str = "PRIVATE_KEY",
124
201
  client_kwargs: Optional[dict[str, Any]] = None,
125
202
  ):
203
+ """
204
+ Initialize the asynchronous Jupiter client.
205
+
206
+ Args:
207
+ api_key: Optional API key for enhanced access to Jupiter API.
208
+ private_key_env_var: Name of environment variable containing the
209
+ private key.
210
+ client_kwargs: Optional kwargs to pass to curl_cffi AsyncSession.
211
+ Common options include 'proxies', 'timeout', 'impersonate'.
212
+ """
126
213
  super().__init__(api_key, private_key_env_var)
127
214
  kwargs = client_kwargs or {}
128
- kwargs.setdefault("impersonate", "chrome110")
215
+ # Use realworld random browser impersonation based on market share
216
+ # if not specified
217
+ kwargs.setdefault("impersonate", "realworld")
129
218
  self.client = AsyncSession(**kwargs)
130
219
 
131
220
  async def close(self) -> None:
221
+ """
222
+ Close the underlying HTTP session.
223
+
224
+ Always call this method when done to properly cleanup resources.
225
+ """
132
226
  await self.client.close()
133
227
 
134
228
  # Override get_public_key for async context consistency
135
229
  async def get_public_key(self) -> str: # type: ignore[override]
230
+ """
231
+ Get the public key from the loaded private key.
232
+
233
+ Returns:
234
+ Public key as a base58-encoded string.
235
+ """
136
236
  return super().get_public_key()
@@ -3,6 +3,13 @@ from urllib.parse import quote
3
3
 
4
4
 
5
5
  class DexEnum(str, Enum):
6
+ """
7
+ Enumeration of supported DEXes (Decentralized Exchanges) on Jupiter.
8
+
9
+ Each value represents a different liquidity source that Jupiter can route through.
10
+ The string values are URL-encoded when used in API requests.
11
+ """
12
+
6
13
  WOOFI = "Woofi"
7
14
  PUMP_FUN = "Pump.fun"
8
15
  WHIRLPOOL = "Whirlpool"
@@ -54,4 +61,10 @@ class DexEnum(str, Enum):
54
61
  GUACSWAP = "Guacswap"
55
62
 
56
63
  def __str__(self) -> str:
64
+ """
65
+ Return URL-encoded string representation of the DEX name.
66
+
67
+ Returns:
68
+ URL-encoded DEX name suitable for API requests.
69
+ """
57
70
  return quote(self.value)
@@ -5,10 +5,24 @@ from pydantic.alias_generators import to_camel
5
5
 
6
6
 
7
7
  class UltraExecuteRequest(BaseModel):
8
+ """
9
+ Pydantic model for executing a previously created order.
10
+
11
+ Attributes:
12
+ signed_transaction: Base64-encoded signed transaction string.
13
+ request_id: The request ID returned from the order endpoint.
14
+ """
15
+
8
16
  signed_transaction: str
9
17
  request_id: str
10
18
 
11
19
  def to_dict(self) -> dict[str, Any]:
20
+ """
21
+ Convert the model to a dictionary with camelCase keys.
22
+
23
+ Returns:
24
+ Dict with camelCase keys suitable for API requests.
25
+ """
12
26
  params = self.model_dump(exclude_none=True)
13
27
 
14
28
  camel_case_params = {to_camel(key): value for key, value in params.items()}
@@ -5,6 +5,18 @@ from pydantic.alias_generators import to_camel
5
5
 
6
6
 
7
7
  class UltraOrderRequest(BaseModel):
8
+ """
9
+ Pydantic model for creating swap orders on Jupiter Ultra API.
10
+
11
+ Attributes:
12
+ input_mint: Mint address of the input token.
13
+ output_mint: Mint address of the output token.
14
+ amount: Amount to swap in the smallest unit (e.g., lamports for SOL).
15
+ taker: Optional public key of the taker (usually your wallet address).
16
+ referral_account: Optional referral account address for fee sharing.
17
+ referral_fee: Optional referral fee in basis points (1 bp = 0.01%).
18
+ """
19
+
8
20
  input_mint: str
9
21
  output_mint: str
10
22
  amount: int
@@ -13,6 +25,12 @@ class UltraOrderRequest(BaseModel):
13
25
  referral_fee: Optional[int] = None
14
26
 
15
27
  def to_dict(self) -> dict[str, Any]:
28
+ """
29
+ Convert the model to a dictionary with camelCase keys.
30
+
31
+ Returns:
32
+ Dict with camelCase keys suitable for API requests.
33
+ """
16
34
  params = self.model_dump(exclude_none=True)
17
35
 
18
36
  camel_case_params = {to_camel(key): value for key, value in params.items()}
@@ -0,0 +1,592 @@
1
+ Metadata-Version: 2.4
2
+ Name: solanab-jup-python-sdk
3
+ Version: 2.0.6
4
+ Summary: High-performance sync/async Python SDK for Jupiter Exchange APIs, powered by curl_cffi.
5
+ Project-URL: homepage, https://github.com/solanab/jup-python-sdk
6
+ Project-URL: repository, https://github.com/solanab/jup-python-sdk
7
+ Author-email: Fiji <charismoutafidis@gmail.com>, solanab <whiredj@gmail.com>
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: jupiter,sdk,solana
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Requires-Python: >=3.9
23
+ Requires-Dist: base58>=2.1.1
24
+ Requires-Dist: curl-cffi>=0.12
25
+ Requires-Dist: pydantic>=2.11.3
26
+ Requires-Dist: solders>=0.26.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: bandit>=1.7; extra == 'dev'
29
+ Requires-Dist: mypy>=1.15.0; extra == 'dev'
30
+ Requires-Dist: pip-audit>=2.8.0; extra == 'dev'
31
+ Requires-Dist: pre-commit>=4.1.0; extra == 'dev'
32
+ Requires-Dist: pytest-asyncio>=0.23.7; extra == 'dev'
33
+ Requires-Dist: pytest-cov>=6.1.1; extra == 'dev'
34
+ Requires-Dist: pytest>=8.3.4; extra == 'dev'
35
+ Requires-Dist: python-dotenv>=1.1.0; extra == 'dev'
36
+ Requires-Dist: ruff>=0.8.0; extra == 'dev'
37
+ Description-Content-Type: text/markdown
38
+
39
+ # **Jupiter Python SDK**
40
+
41
+ [![PyPI version](https://badge.fury.io/py/solanab-jup-python-sdk.svg)](https://badge.fury.io/py/solanab-jup-python-sdk)
42
+ [![Python](https://img.shields.io/pypi/pyversions/solanab-jup-python-sdk.svg)](https://pypi.org/project/solanab-jup-python-sdk/)
43
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
44
+
45
+ A high-performance, async-first Python SDK for seamless interaction with the Jupiter Ultra API, powered by `curl_cffi` for maximum speed and flexibility.
46
+
47
+ With Ultra API, you don't need to manage or connect to any RPC endpoints, or deal with complex configurations. Everything from getting quotes to transaction execution happens directly through a powerful API.
48
+
49
+ Or as we like to say around here: **"RPCs are for NPCs."**
50
+
51
+ ## **Table of Contents**
52
+
53
+ - [Features](#features)
54
+ - [Installation](#installation)
55
+ - [Quick Start](#quick-start)
56
+ - [Configuration](#configuration)
57
+ - [API Reference](#api-reference)
58
+ - [Usage Examples](#usage-examples)
59
+ - [Best Practices](#best-practices)
60
+ - [Advanced Usage](#advanced-usage)
61
+ - [Error Handling](#error-handling)
62
+ - [Contributing](#contributing)
63
+ - [Resources](#resources)
64
+
65
+ ## **Features**
66
+
67
+ - 🚀 **High Performance**: Built on `curl_cffi` for blazing-fast HTTP requests
68
+ - 🔄 **Async/Sync Support**: Both asynchronous and synchronous clients available
69
+ - 🛡️ **Token Safety**: Built-in shield API for token security warnings
70
+ - 💰 **Balance Checking**: Easy balance retrieval for any Solana address
71
+ - 🔧 **Advanced Configuration**: Support for proxies, custom DNS, and more
72
+ - 📦 **Type Safety**: Full type hints with Pydantic models
73
+ - 🎯 **Zero Configuration**: Works out of the box with minimal setup
74
+
75
+ ## **Installation**
76
+
77
+ ```bash
78
+ pip install solanab-jup-python-sdk
79
+ ```
80
+
81
+ ### Requirements
82
+
83
+ - Python 3.9 or higher
84
+ - A Solana wallet private key (for transaction signing)
85
+
86
+ ## **Quick Start**
87
+
88
+ ### Async Example
89
+
90
+ ```python
91
+ import asyncio
92
+ from jup_python_sdk.clients.ultra_api_client import AsyncUltraApiClient
93
+ from jup_python_sdk.models.ultra_api.ultra_order_request_model import UltraOrderRequest
94
+
95
+ async def main():
96
+ # Initialize the async client
97
+ client = AsyncUltraApiClient()
98
+
99
+ # Create a swap order
100
+ order_request = UltraOrderRequest(
101
+ input_mint="So11111111111111111111111111111111111111112", # WSOL
102
+ output_mint="EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", # USDC
103
+ amount=10000000, # 0.01 WSOL
104
+ taker=await client.get_public_key(),
105
+ )
106
+
107
+ try:
108
+ # Execute the swap
109
+ response = await client.order_and_execute(order_request)
110
+ print(f"Transaction: https://solscan.io/tx/{response['signature']}")
111
+ finally:
112
+ await client.close()
113
+
114
+ asyncio.run(main())
115
+ ```
116
+
117
+ ### Sync Example
118
+
119
+ ```python
120
+ from jup_python_sdk.clients.ultra_api_client import UltraApiClient
121
+ from jup_python_sdk.models.ultra_api.ultra_order_request_model import UltraOrderRequest
122
+
123
+ # Initialize the sync client
124
+ client = UltraApiClient()
125
+
126
+ # Create and execute a swap
127
+ order_request = UltraOrderRequest(
128
+ input_mint="So11111111111111111111111111111111111111112", # WSOL
129
+ output_mint="EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", # USDC
130
+ amount=10000000, # 0.01 WSOL
131
+ taker=client.get_public_key(),
132
+ )
133
+
134
+ response = client.order_and_execute(order_request)
135
+ print(f"Transaction: https://solscan.io/tx/{response['signature']}")
136
+ client.close()
137
+ ```
138
+
139
+ ## **Configuration**
140
+
141
+ ### Environment Variables
142
+
143
+ Set up your private key as an environment variable:
144
+
145
+ ```bash
146
+ # Base58 format (standard Solana format)
147
+ export PRIVATE_KEY=your_base58_private_key_here
148
+
149
+ # OR as a uint8 array
150
+ export PRIVATE_KEY=[10,229,131,132,213,96,74,22,...]
151
+ ```
152
+
153
+ ### Client Configuration
154
+
155
+ ```python
156
+ from jup_python_sdk.clients.ultra_api_client import AsyncUltraApiClient
157
+
158
+ # With API key (for enhanced access)
159
+ client = AsyncUltraApiClient(
160
+ api_key="YOUR_API_KEY", # Get from https://portal.jup.ag/onboard
161
+ private_key_env_var="CUSTOM_PRIVATE_KEY" # Custom env var name
162
+ )
163
+
164
+ # With custom client configuration
165
+ client = AsyncUltraApiClient(
166
+ client_kwargs={
167
+ "timeout": 30, # 30 seconds timeout
168
+ "impersonate": "chrome110", # Browser impersonation
169
+ "verify": True, # SSL verification
170
+ }
171
+ )
172
+ ```
173
+
174
+ ## **API Reference**
175
+
176
+ ### UltraApiClient / AsyncUltraApiClient
177
+
178
+ The main client classes for interacting with the Jupiter Ultra API.
179
+
180
+ #### Methods
181
+
182
+ ##### `order(request: UltraOrderRequest) -> dict`
183
+
184
+ Get a swap order from the Jupiter Ultra API.
185
+
186
+ **Parameters:**
187
+
188
+ - `request`: An `UltraOrderRequest` object containing:
189
+ - `input_mint` (str): Input token mint address
190
+ - `output_mint` (str): Output token mint address
191
+ - `amount` (int): Amount in smallest unit (e.g., lamports for SOL)
192
+ - `taker` (str, optional): Taker's public key
193
+ - `referral_account` (str, optional): Referral account address
194
+ - `referral_fee` (int, optional): Referral fee in basis points
195
+
196
+ **Returns:** Dict containing order details including `requestId` and `transaction`
197
+
198
+ ##### `execute(request: UltraExecuteRequest) -> dict`
199
+
200
+ Execute a previously created order.
201
+
202
+ **Parameters:**
203
+
204
+ - `request`: An `UltraExecuteRequest` object containing:
205
+ - `request_id` (str): The request ID from the order
206
+ - `signed_transaction` (str): Base64-encoded signed transaction
207
+
208
+ **Returns:** Dict containing execution result including `signature` and `status`
209
+
210
+ ##### `order_and_execute(request: UltraOrderRequest) -> dict`
211
+
212
+ Create and execute an order in a single call.
213
+
214
+ **Parameters:**
215
+
216
+ - `request`: Same as `order()` method
217
+
218
+ **Returns:** Dict containing execution result including `signature` and `status`
219
+
220
+ ##### `balances(address: str) -> dict`
221
+
222
+ Get token balances for a Solana address.
223
+
224
+ **Parameters:**
225
+
226
+ - `address` (str): Solana public key address
227
+
228
+ **Returns:** Dict mapping token symbols to balance details:
229
+
230
+ ```python
231
+ {
232
+ "SOL": {
233
+ "amount": "100000000",
234
+ "uiAmount": 0.1,
235
+ "slot": 123456,
236
+ "isFrozen": False
237
+ }
238
+ }
239
+ ```
240
+
241
+ ##### `shield(mints: list[str]) -> dict`
242
+
243
+ Check tokens for safety warnings.
244
+
245
+ **Parameters:**
246
+
247
+ - `mints` (list[str]): List of token mint addresses to check
248
+
249
+ **Returns:** Dict containing warnings for each mint:
250
+
251
+ ```python
252
+ {
253
+ "warnings": {
254
+ "mint_address": [
255
+ {
256
+ "type": "warning_type",
257
+ "message": "Warning description"
258
+ }
259
+ ]
260
+ }
261
+ }
262
+ ```
263
+
264
+ ### Models
265
+
266
+ #### UltraOrderRequest
267
+
268
+ Pydantic model for creating swap orders.
269
+
270
+ ```python
271
+ UltraOrderRequest(
272
+ input_mint="So11111111111111111111111111111111111111112",
273
+ output_mint="EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
274
+ amount=10000000,
275
+ taker="your_public_key",
276
+ referral_account="optional_referral_address",
277
+ referral_fee=50 # 0.5% in basis points
278
+ )
279
+ ```
280
+
281
+ #### UltraExecuteRequest
282
+
283
+ Pydantic model for executing orders.
284
+
285
+ ```python
286
+ UltraExecuteRequest(
287
+ request_id="order_request_id",
288
+ signed_transaction="base64_encoded_signed_transaction"
289
+ )
290
+ ```
291
+
292
+ ## **Usage Examples**
293
+
294
+ ### Check Token Balances
295
+
296
+ ```python
297
+ import asyncio
298
+ from jup_python_sdk.clients.ultra_api_client import AsyncUltraApiClient
299
+
300
+ async def check_balances():
301
+ client = AsyncUltraApiClient()
302
+
303
+ # Get your wallet address
304
+ address = await client.get_public_key()
305
+
306
+ # Fetch balances
307
+ balances = await client.balances(address)
308
+
309
+ for token, details in balances.items():
310
+ print(f"{token}: {details['uiAmount']} (frozen: {details['isFrozen']})")
311
+
312
+ await client.close()
313
+
314
+ asyncio.run(check_balances())
315
+ ```
316
+
317
+ ### Check Token Safety
318
+
319
+ ```python
320
+ import asyncio
321
+ from jup_python_sdk.clients.ultra_api_client import AsyncUltraApiClient
322
+
323
+ async def check_token_safety():
324
+ client = AsyncUltraApiClient()
325
+
326
+ # Popular tokens to check
327
+ mints = [
328
+ "So11111111111111111111111111111111111111112", # WSOL
329
+ "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", # USDC
330
+ "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263", # BONK
331
+ ]
332
+
333
+ shield_response = await client.shield(mints)
334
+
335
+ for mint, warnings in shield_response.get("warnings", {}).items():
336
+ if warnings:
337
+ print(f"⚠️ {mint} has warnings:")
338
+ for warning in warnings:
339
+ print(f" - {warning['type']}: {warning['message']}")
340
+ else:
341
+ print(f"✅ {mint} appears safe")
342
+
343
+ await client.close()
344
+
345
+ asyncio.run(check_token_safety())
346
+ ```
347
+
348
+ ### Concurrent Operations
349
+
350
+ ```python
351
+ import asyncio
352
+ from jup_python_sdk.clients.ultra_api_client import AsyncUltraApiClient
353
+
354
+ async def concurrent_operations():
355
+ client = AsyncUltraApiClient()
356
+
357
+ # Define multiple tokens to check
358
+ tokens = [
359
+ "So11111111111111111111111111111111111111112",
360
+ "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
361
+ "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB",
362
+ ]
363
+
364
+ # Create tasks for concurrent execution
365
+ tasks = []
366
+ for token in tokens:
367
+ task = client.shield([token])
368
+ tasks.append(task)
369
+
370
+ # Execute all requests concurrently
371
+ results = await asyncio.gather(*tasks)
372
+
373
+ # Process results
374
+ for token, result in zip(tokens, results):
375
+ print(f"Token {token}: {result}")
376
+
377
+ await client.close()
378
+
379
+ asyncio.run(concurrent_operations())
380
+ ```
381
+
382
+ ## **Best Practices**
383
+
384
+ ### 1. Always Close Clients
385
+
386
+ ```python
387
+ # Using try/finally
388
+ client = AsyncUltraApiClient()
389
+ try:
390
+ # Your code here
391
+ pass
392
+ finally:
393
+ await client.close()
394
+
395
+ # Or using async context manager (if implemented)
396
+ async with AsyncUltraApiClient() as client:
397
+ # Your code here
398
+ pass
399
+ ```
400
+
401
+ ### 2. Error Handling
402
+
403
+ ```python
404
+ try:
405
+ response = await client.order_and_execute(order_request)
406
+
407
+ if response.get("status") == "Failed":
408
+ print(f"Transaction failed: {response.get('error')}")
409
+ else:
410
+ print(f"Success: {response['signature']}")
411
+
412
+ except Exception as e:
413
+ print(f"Error occurred: {e}")
414
+ ```
415
+
416
+ ### 3. Rate Limiting
417
+
418
+ ```python
419
+ import asyncio
420
+
421
+ # Use semaphore to limit concurrent requests
422
+ semaphore = asyncio.Semaphore(5) # Max 5 concurrent requests
423
+
424
+ async def rate_limited_request(client, mint):
425
+ async with semaphore:
426
+ return await client.shield([mint])
427
+ ```
428
+
429
+ ### 4. Retry Logic
430
+
431
+ ```python
432
+ import asyncio
433
+ from typing import Optional
434
+
435
+ async def retry_request(func, max_retries=3, delay=1.0):
436
+ for attempt in range(max_retries):
437
+ try:
438
+ return await func()
439
+ except Exception as e:
440
+ if attempt == max_retries - 1:
441
+ raise
442
+ await asyncio.sleep(delay * (attempt + 1))
443
+ ```
444
+
445
+ ### 5. Token Amount Calculations
446
+
447
+ ```python
448
+ # Always work with the smallest unit (lamports for SOL)
449
+ sol_amount = 0.01 # SOL
450
+ lamports = int(sol_amount * 10**9) # Convert to lamports
451
+
452
+ # For other tokens, check their decimals
453
+ usdc_amount = 10.0 # USDC
454
+ usdc_smallest_unit = int(usdc_amount * 10**6) # USDC has 6 decimals
455
+ ```
456
+
457
+ ## **Advanced Usage**
458
+
459
+ ### Using Proxies
460
+
461
+ ```python
462
+ # SOCKS5 proxy
463
+ proxies = {"https": "socks5://user:pass@host:port"}
464
+ client = AsyncUltraApiClient(client_kwargs={"proxies": proxies})
465
+
466
+ # HTTP proxy
467
+ proxies = {
468
+ "http": "http://user:pass@proxy.example.com:8080",
469
+ "https": "http://user:pass@proxy.example.com:8080",
470
+ }
471
+ client = AsyncUltraApiClient(client_kwargs={"proxies": proxies})
472
+ ```
473
+
474
+ ### Custom DNS Resolution
475
+
476
+ ```python
477
+ # Force specific DNS resolution
478
+ client = AsyncUltraApiClient(
479
+ client_kwargs={
480
+ "resolve": ["api.jup.ag:443:1.2.3.4"],
481
+ "dns_servers": ["1.1.1.1", "1.0.0.1"],
482
+ }
483
+ )
484
+ ```
485
+
486
+ ### Custom Headers and Browser Impersonation
487
+
488
+ ```python
489
+ # Default: Uses "realworld" - randomly selects browser based on market share
490
+ client = AsyncUltraApiClient()
491
+
492
+ # Specify a specific browser version
493
+ client = AsyncUltraApiClient(
494
+ client_kwargs={
495
+ "impersonate": "chrome124", # Specific browser version
496
+ }
497
+ )
498
+
499
+ # Use latest version of a browser
500
+ client = AsyncUltraApiClient(
501
+ client_kwargs={
502
+ "impersonate": "chrome", # Latest Chrome
503
+ }
504
+ )
505
+
506
+ # Custom headers with browser impersonation
507
+ client = AsyncUltraApiClient(
508
+ client_kwargs={
509
+ "impersonate": "safari", # Latest Safari
510
+ "headers": {
511
+ "Accept-Language": "en-US,en;q=0.9",
512
+ }
513
+ }
514
+ )
515
+ ```
516
+
517
+ **Note**: By default, the SDK uses `impersonate="realworld"` which randomly selects a browser version based on current market share. This helps avoid detection and provides better anonymity.
518
+
519
+ ## **Error Handling**
520
+
521
+ The SDK may raise various exceptions:
522
+
523
+ ### Common Exceptions
524
+
525
+ ```python
526
+ try:
527
+ response = await client.order_and_execute(order_request)
528
+ except ValueError as e:
529
+ # Invalid private key format
530
+ print(f"Configuration error: {e}")
531
+ except requests.HTTPError as e:
532
+ # HTTP errors (4xx, 5xx)
533
+ print(f"API error: {e}")
534
+ except Exception as e:
535
+ # Other errors
536
+ print(f"Unexpected error: {e}")
537
+ ```
538
+
539
+ ### Response Status Handling
540
+
541
+ ```python
542
+ response = await client.order_and_execute(order_request)
543
+
544
+ if response.get("status") == "Failed":
545
+ error_code = response.get("code")
546
+ error_message = response.get("error")
547
+
548
+ if error_code == "INSUFFICIENT_BALANCE":
549
+ print("Not enough balance for the swap")
550
+ elif error_code == "SLIPPAGE_EXCEEDED":
551
+ print("Slippage tolerance exceeded")
552
+ else:
553
+ print(f"Transaction failed: {error_message}")
554
+ ```
555
+
556
+ ## **Contributing**
557
+
558
+ Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
559
+
560
+ ### Development Setup
561
+
562
+ ```bash
563
+ # Clone the repository
564
+ git clone https://github.com/solanab/jup-python-sdk.git
565
+ cd jup-python-sdk
566
+
567
+ # Install development dependencies
568
+ pip install -e ".[dev]"
569
+
570
+ # Run tests
571
+ pytest
572
+
573
+ # Run linters
574
+ ruff check .
575
+ ruff format .
576
+ ```
577
+
578
+ ## **Resources**
579
+
580
+ - [Ultra API Documentation](https://dev.jup.ag/docs/ultra-api/)
581
+ - [Jupiter Portal](https://portal.jup.ag/onboard) - Get your API key
582
+ - [Discord Community](https://discord.gg/jup)
583
+ - [GitHub Repository](https://github.com/solanab/jup-python-sdk)
584
+
585
+ ## **License**
586
+
587
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
588
+
589
+ ## **Disclaimer**
590
+
591
+ 🚨 **This project is actively maintained.**
592
+ While we strive for stability, the SDK is under active development. We recommend staying updated with the latest releases. Important updates will be announced in the [Discord server](https://discord.gg/jup).
@@ -1,14 +1,14 @@
1
1
  jup_python_sdk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  jup_python_sdk/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- jup_python_sdk/clients/jupiter_client.py,sha256=3zX4e9iEmLP5KqSzmHt2vrry4UVbAKLaKSir6HFLf-U,4607
3
+ jup_python_sdk/clients/jupiter_client.py,sha256=9XXruDRIyFs8n3H2RsU0FhaihF--9mzNwMDs0ldiYPA,7762
4
4
  jup_python_sdk/clients/ultra_api_client.py,sha256=jgNgf04bDSJxu8MY1oGRx3Bxemyy4B4y5eUuQBN61qA,6750
5
5
  jup_python_sdk/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  jup_python_sdk/models/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- jup_python_sdk/models/common/dex_enum.py,sha256=javWWg5TTbPAVNhoBQJlaVEUlpTkwfin7SO4xGKToaI,1510
7
+ jup_python_sdk/models/common/dex_enum.py,sha256=ie9bTkVYTdNZijwLB4mCZ-AN3g-cV5OahXke1z8V0PY,1921
8
8
  jup_python_sdk/models/ultra_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- jup_python_sdk/models/ultra_api/ultra_execute_request_model.py,sha256=jvoPgCa5QxUFuMmZ7brFPcAyQzU7WgxtnwXH1KgxIPM,403
10
- jup_python_sdk/models/ultra_api/ultra_order_request_model.py,sha256=DSxQtLALHgHGwr5gw3Iu1fkg4LbydUUmEo-aHF55G4Y,534
11
- solanab_jup_python_sdk-2.0.3.dist-info/METADATA,sha256=OoA0fmw4M3EEXukcMU_LcViHFLl-oa1_vxdc6U6fh2A,5992
12
- solanab_jup_python_sdk-2.0.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
13
- solanab_jup_python_sdk-2.0.3.dist-info/licenses/LICENSE,sha256=3va7FgUbc2istAEEchr1jkoUjBoSYPMkguY1BGuHt2U,1061
14
- solanab_jup_python_sdk-2.0.3.dist-info/RECORD,,
9
+ jup_python_sdk/models/ultra_api/ultra_execute_request_model.py,sha256=jgvBjzVwslq1aItsxOJX9BWechEGiJq8Nr4H3om352I,806
10
+ jup_python_sdk/models/ultra_api/ultra_order_request_model.py,sha256=gv_xxvVjQVbD9NSJa3d06WMe9MkTK31KkKmKq0e7UOc,1221
11
+ solanab_jup_python_sdk-2.0.6.dist-info/METADATA,sha256=P0T43FXZAWo-Vm8Fx_zMWNacHCZhqbYxcTMieFHOIMQ,15957
12
+ solanab_jup_python_sdk-2.0.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
13
+ solanab_jup_python_sdk-2.0.6.dist-info/licenses/LICENSE,sha256=3va7FgUbc2istAEEchr1jkoUjBoSYPMkguY1BGuHt2U,1061
14
+ solanab_jup_python_sdk-2.0.6.dist-info/RECORD,,
@@ -1,158 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: solanab-jup-python-sdk
3
- Version: 2.0.3
4
- Summary: High-performance sync/async Python SDK for Jupiter Exchange APIs, powered by curl_cffi.
5
- Project-URL: homepage, https://github.com/solanab/jup-python-sdk
6
- Project-URL: repository, https://github.com/solanab/jup-python-sdk
7
- Author-email: Fiji <charismoutafidis@gmail.com>, solanab <whiredj@gmail.com>
8
- License: MIT
9
- License-File: LICENSE
10
- Keywords: jupiter,sdk,solana
11
- Classifier: Operating System :: OS Independent
12
- Classifier: Programming Language :: Python :: 3
13
- Requires-Python: >=3.9
14
- Requires-Dist: base58>=2.1.1
15
- Requires-Dist: curl-cffi>=0.12
16
- Requires-Dist: pydantic>=2.11.3
17
- Requires-Dist: solana>=0.36.6
18
- Requires-Dist: solders>=0.26.0
19
- Provides-Extra: dev
20
- Requires-Dist: bandit>=1.7; extra == 'dev'
21
- Requires-Dist: black>=25.1.0; extra == 'dev'
22
- Requires-Dist: flake8>=7.1.1; extra == 'dev'
23
- Requires-Dist: isort>=6.0.0; extra == 'dev'
24
- Requires-Dist: mypy>=1.15.0; extra == 'dev'
25
- Requires-Dist: pre-commit>=4.1.0; extra == 'dev'
26
- Requires-Dist: pytest-asyncio>=0.23.7; extra == 'dev'
27
- Requires-Dist: pytest-cov>=6.1.1; extra == 'dev'
28
- Requires-Dist: pytest>=8.3.4; extra == 'dev'
29
- Requires-Dist: python-dotenv>=1.1.0; extra == 'dev'
30
- Requires-Dist: responses>=0.25.6; extra == 'dev'
31
- Requires-Dist: safety>=2.3; extra == 'dev'
32
- Description-Content-Type: text/markdown
33
-
34
- # **Jup Python SDK**
35
-
36
- A high-performance, async-first Python SDK for seamless interaction with the Jupiter Ultra API, powered by `curl_cffi` for maximum speed and flexibility.<br>
37
- With Ultra API, you don't need to manage or connect to any RPC endpoints, or deal with complex configurations.<br>
38
- Everything from getting quotes to transaction execution happens directly through a powerful API.<br>
39
-
40
- Or as we like to say around here:<br>
41
- **"RPCs are for NPCs."**
42
-
43
- For a deeper understanding of the Ultra API, including its features and benefits, check out the [Ultra API Docs](https://dev.jup.ag/docs/ultra-api/).
44
-
45
- ## **Installation**
46
-
47
- To install the SDK in your project, run:
48
- ```sh
49
- pip install jup-python-sdk
50
- ```
51
-
52
- ## **Quick Start (Async)**
53
-
54
- Below is a simple asynchronous example to fetch and execute an Ultra order.
55
-
56
- ```python
57
- import asyncio
58
- from dotenv import load_dotenv
59
- from jup_python_sdk.clients.ultra_api_client import AsyncUltraApiClient
60
- from jup_python_sdk.models.ultra_api.ultra_order_request_model import UltraOrderRequest
61
-
62
- async def main():
63
- load_dotenv()
64
- # Note: For async client, methods are awaited.
65
- client = AsyncUltraApiClient()
66
-
67
- order_request = UltraOrderRequest(
68
- input_mint="So11111111111111111111111111111111111111112", # WSOL
69
- output_mint="EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", # USDC
70
- amount=10000000, # 0.01 WSOL
71
- taker=await client.get_public_key(),
72
- )
73
-
74
- try:
75
- client_response = await client.order_and_execute(order_request)
76
- signature = str(client_response["signature"])
77
-
78
- print("Order and Execute API Response:")
79
- print(f" - Status: {client_response.get('status')}")
80
- if client_response.get("status") == "Failed":
81
- print(f" - Code: {client_response.get('code')}")
82
- print(f" - Error: {client_response.get('error')}")
83
-
84
- print(f" - Transaction Signature: {signature}")
85
- print(f" - View on Solscan: https://solscan.io/tx/{signature}")
86
-
87
- except Exception as e:
88
- print("Error occurred while processing the swap:", str(e))
89
- finally:
90
- await client.close()
91
-
92
- if __name__ == "__main__":
93
- asyncio.run(main())
94
- ```
95
-
96
- > **Note**: A synchronous client (`UltraApiClient`) is also available. See the [examples](./examples) folder for both sync and async usage.
97
-
98
- ## **Setup Instructions**
99
-
100
- Before using the SDK, please ensure you have completed the following steps:
101
-
102
- 1. **Environment Variables**:
103
- Set up your required environment variables.
104
- The SDK supports both base58 string and uint8 array formats for your private key.
105
- ```sh
106
- # Base58 format
107
- export PRIVATE_KEY=your_base58_private_key_here
108
-
109
- # OR as a uint8 array
110
- export PRIVATE_KEY=[10,229,131,132,213,96,74,22,...]
111
- ```
112
-
113
- > **Note**: `PRIVATE_KEY` can be either a base58-encoded string (default Solana format), or a uint8 array (e.g. `[181,99,240,...]`). The SDK will automatically detect and parse the format.
114
-
115
- 2. **Optional Configuration**:
116
- Depending on your credentials and setup, you have a couple of options for initializing the `UltraApiClient`:
117
- - **API Key**: Use an API key from [the Jupiter Portal](https://portal.jup.ag/onboard) for enhanced access. This will use the `https://api.jup.ag/` endpoint.
118
- - **Custom Private Key Env Var**: Specify a different environment variable name for your private key.
119
-
120
- ```python
121
- from jup_python_sdk.clients.ultra_api_client import AsyncUltraApiClient
122
-
123
- client = AsyncUltraApiClient(
124
- api_key="YOUR_API_KEY",
125
- private_key_env_var="YOUR_CUSTOM_ENV_VAR"
126
- )
127
- ```
128
-
129
- ## **Advanced Configuration (Proxies, Custom DNS)**
130
-
131
- The SDK is built on `curl_cffi`, allowing you to pass any valid `curl_cffi` client parameter during initialization for advanced network control.
132
-
133
- ### Using a SOCKS5 Proxy
134
-
135
- ```python
136
- from jup_python_sdk.clients.ultra_api_client import AsyncUltraApiClient
137
-
138
- proxies = {"httpss": "socks5://user:pass@host:port"}
139
- client = AsyncUltraApiClient(client_kwargs={"proxies": proxies, "impersonate": "chrome110"})
140
- ```
141
-
142
- ### Using Custom DNS Resolution
143
-
144
- This is useful for bypassing local DNS caches or using a specialized DNS resolver.
145
-
146
- ```python
147
- from jup_python_sdk.clients.ultra_api_client import AsyncUltraApiClient
148
-
149
- # Tell the client to resolve jup.ag to a specific IP address
150
- resolve_string = "jup.ag:443:1.2.3.4"
151
- client = AsyncUltraApiClient(client_kwargs={"resolve": [resolve_string]})
152
- ```
153
-
154
- ## **Disclaimer**
155
-
156
- 🚨 **This project is actively worked on.**
157
- While we don't expect breaking changes as the SDK evolves, we recommend you stay updated with the latest releases.
158
- Any important updates will be announced in the [Discord server](https://discord.gg/jup).