pumpswap-python-sdk 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,315 @@
1
+ """
2
+ Input validation utilities for the PumpSwap SDK
3
+ """
4
+
5
+ from typing import Optional, Union
6
+ from solders.pubkey import Pubkey
7
+ from .constants import MAX_SLIPPAGE_BPS, BASIS_POINTS
8
+ from .exceptions import ValidationError
9
+
10
+
11
+ def validate_amount(amount: int, name: str = "amount", allow_zero: bool = False) -> None:
12
+ """
13
+ Validate token amount
14
+
15
+ Args:
16
+ amount: Amount to validate
17
+ name: Parameter name for error messages
18
+ allow_zero: Whether zero is allowed
19
+
20
+ Raises:
21
+ ValidationError: If amount is invalid
22
+ """
23
+ if not isinstance(amount, int):
24
+ raise ValidationError(f"{name} must be an integer")
25
+
26
+ if amount < 0:
27
+ raise ValidationError(f"{name} cannot be negative")
28
+
29
+ if not allow_zero and amount == 0:
30
+ raise ValidationError(f"{name} must be greater than zero")
31
+
32
+ # Check for unreasonably large values (overflow protection)
33
+ max_u64 = 2**64 - 1
34
+ if amount > max_u64:
35
+ raise ValidationError(f"{name} exceeds maximum allowed value")
36
+
37
+
38
+ def validate_slippage(slippage_bps: int) -> None:
39
+ """
40
+ Validate slippage tolerance
41
+
42
+ Args:
43
+ slippage_bps: Slippage in basis points
44
+
45
+ Raises:
46
+ ValidationError: If slippage is invalid
47
+ """
48
+ validate_amount(slippage_bps, "slippage", allow_zero=True)
49
+
50
+ if slippage_bps > MAX_SLIPPAGE_BPS:
51
+ raise ValidationError(f"Slippage ({slippage_bps} bps) exceeds maximum ({MAX_SLIPPAGE_BPS} bps)")
52
+
53
+
54
+ def validate_fee_rate(fee_bps: int, name: str = "fee rate") -> None:
55
+ """
56
+ Validate fee rate in basis points
57
+
58
+ Args:
59
+ fee_bps: Fee rate in basis points
60
+ name: Parameter name for error messages
61
+
62
+ Raises:
63
+ ValidationError: If fee rate is invalid
64
+ """
65
+ validate_amount(fee_bps, name, allow_zero=True)
66
+
67
+ if fee_bps > BASIS_POINTS:
68
+ raise ValidationError(f"{name} ({fee_bps} bps) cannot exceed 100% ({BASIS_POINTS} bps)")
69
+
70
+
71
+ def validate_pubkey(pubkey: Union[str, Pubkey], name: str = "pubkey") -> Pubkey:
72
+ """
73
+ Validate and convert pubkey
74
+
75
+ Args:
76
+ pubkey: Pubkey as string or Pubkey object
77
+ name: Parameter name for error messages
78
+
79
+ Returns:
80
+ Validated Pubkey object
81
+
82
+ Raises:
83
+ ValidationError: If pubkey is invalid
84
+ """
85
+ if isinstance(pubkey, str):
86
+ try:
87
+ return Pubkey.from_string(pubkey)
88
+ except Exception as e:
89
+ raise ValidationError(f"Invalid {name}: {str(e)}")
90
+
91
+ if isinstance(pubkey, Pubkey):
92
+ return pubkey
93
+
94
+ raise ValidationError(f"{name} must be a string or Pubkey object")
95
+
96
+
97
+ def validate_pool_reserves(base_reserve: int, quote_reserve: int) -> None:
98
+ """
99
+ Validate pool reserves
100
+
101
+ Args:
102
+ base_reserve: Base token reserve
103
+ quote_reserve: Quote token reserve
104
+
105
+ Raises:
106
+ ValidationError: If reserves are invalid
107
+ """
108
+ validate_amount(base_reserve, "base_reserve")
109
+ validate_amount(quote_reserve, "quote_reserve")
110
+
111
+ # Check for minimum liquidity
112
+ min_liquidity = 1000 # Minimum tokens to prevent precision issues
113
+ if base_reserve < min_liquidity:
114
+ raise ValidationError(f"Base reserve ({base_reserve}) below minimum ({min_liquidity})")
115
+
116
+ if quote_reserve < min_liquidity:
117
+ raise ValidationError(f"Quote reserve ({quote_reserve}) below minimum ({min_liquidity})")
118
+
119
+
120
+ def validate_lp_supply(lp_supply: int, allow_zero: bool = False) -> None:
121
+ """
122
+ Validate LP token supply
123
+
124
+ Args:
125
+ lp_supply: LP token supply
126
+ allow_zero: Whether zero supply is allowed (for initial pools)
127
+
128
+ Raises:
129
+ ValidationError: If supply is invalid
130
+ """
131
+ validate_amount(lp_supply, "lp_supply", allow_zero=allow_zero)
132
+
133
+
134
+ def validate_swap_parameters(
135
+ amount_in: int,
136
+ amount_out_min: int,
137
+ slippage_bps: int
138
+ ) -> None:
139
+ """
140
+ Validate swap parameters
141
+
142
+ Args:
143
+ amount_in: Input amount
144
+ amount_out_min: Minimum output amount
145
+ slippage_bps: Slippage tolerance
146
+
147
+ Raises:
148
+ ValidationError: If parameters are invalid
149
+ """
150
+ validate_amount(amount_in, "amount_in")
151
+ validate_amount(amount_out_min, "amount_out_min", allow_zero=True)
152
+ validate_slippage(slippage_bps)
153
+
154
+
155
+ def validate_deposit_parameters(
156
+ base_amount: int,
157
+ quote_amount: int,
158
+ lp_amount: int,
159
+ slippage_bps: int
160
+ ) -> None:
161
+ """
162
+ Validate liquidity deposit parameters
163
+
164
+ Args:
165
+ base_amount: Base token amount
166
+ quote_amount: Quote token amount
167
+ lp_amount: Expected LP token amount
168
+ slippage_bps: Slippage tolerance
169
+
170
+ Raises:
171
+ ValidationError: If parameters are invalid
172
+ """
173
+ validate_amount(base_amount, "base_amount", allow_zero=True)
174
+ validate_amount(quote_amount, "quote_amount", allow_zero=True)
175
+ validate_amount(lp_amount, "lp_amount")
176
+ validate_slippage(slippage_bps)
177
+
178
+ # At least one token amount must be positive
179
+ if base_amount == 0 and quote_amount == 0:
180
+ raise ValidationError("Either base_amount or quote_amount must be positive")
181
+
182
+
183
+ def validate_withdrawal_parameters(
184
+ lp_amount: int,
185
+ min_base: int,
186
+ min_quote: int,
187
+ slippage_bps: int
188
+ ) -> None:
189
+ """
190
+ Validate liquidity withdrawal parameters
191
+
192
+ Args:
193
+ lp_amount: LP token amount to burn
194
+ min_base: Minimum base token amount
195
+ min_quote: Minimum quote token amount
196
+ slippage_bps: Slippage tolerance
197
+
198
+ Raises:
199
+ ValidationError: If parameters are invalid
200
+ """
201
+ validate_amount(lp_amount, "lp_amount")
202
+ validate_amount(min_base, "min_base", allow_zero=True)
203
+ validate_amount(min_quote, "min_quote", allow_zero=True)
204
+ validate_slippage(slippage_bps)
205
+
206
+
207
+ def validate_pool_creation_parameters(
208
+ index: int,
209
+ creator: Union[str, Pubkey],
210
+ base_mint: Union[str, Pubkey],
211
+ quote_mint: Union[str, Pubkey],
212
+ initial_base: int,
213
+ initial_quote: int
214
+ ) -> None:
215
+ """
216
+ Validate pool creation parameters
217
+
218
+ Args:
219
+ index: Pool index
220
+ creator: Pool creator address
221
+ base_mint: Base token mint
222
+ quote_mint: Quote token mint
223
+ initial_base: Initial base token amount
224
+ initial_quote: Initial quote token amount
225
+
226
+ Raises:
227
+ ValidationError: If parameters are invalid
228
+ """
229
+ validate_amount(index, "index", allow_zero=True)
230
+ validate_pubkey(creator, "creator")
231
+ validate_pubkey(base_mint, "base_mint")
232
+ validate_pubkey(quote_mint, "quote_mint")
233
+ validate_amount(initial_base, "initial_base")
234
+ validate_amount(initial_quote, "initial_quote")
235
+
236
+ # Base and quote mints must be different
237
+ base_pubkey = validate_pubkey(base_mint, "base_mint")
238
+ quote_pubkey = validate_pubkey(quote_mint, "quote_mint")
239
+
240
+ if base_pubkey == quote_pubkey:
241
+ raise ValidationError("Base mint and quote mint cannot be the same")
242
+
243
+
244
+ def validate_fee_calculation_inputs(
245
+ market_cap: int,
246
+ volume: int,
247
+ fee_rates: dict
248
+ ) -> None:
249
+ """
250
+ Validate inputs for fee calculation
251
+
252
+ Args:
253
+ market_cap: Token market cap
254
+ volume: Trading volume
255
+ fee_rates: Dictionary of fee rates
256
+
257
+ Raises:
258
+ ValidationError: If inputs are invalid
259
+ """
260
+ validate_amount(market_cap, "market_cap", allow_zero=True)
261
+ validate_amount(volume, "volume", allow_zero=True)
262
+
263
+ required_rates = ["lp_fee_bps", "protocol_fee_bps", "creator_fee_bps"]
264
+ for rate_name in required_rates:
265
+ if rate_name not in fee_rates:
266
+ raise ValidationError(f"Missing required fee rate: {rate_name}")
267
+ validate_fee_rate(fee_rates[rate_name], rate_name)
268
+
269
+ # Validate total fee rate
270
+ total_fee_bps = sum(fee_rates[rate] for rate in required_rates)
271
+ if total_fee_bps > BASIS_POINTS:
272
+ raise ValidationError(f"Total fee rates ({total_fee_bps} bps) exceed 100%")
273
+
274
+
275
+ def validate_range(
276
+ value: int,
277
+ min_value: int,
278
+ max_value: int,
279
+ name: str = "value"
280
+ ) -> None:
281
+ """
282
+ Validate that a value is within a specific range
283
+
284
+ Args:
285
+ value: Value to validate
286
+ min_value: Minimum allowed value
287
+ max_value: Maximum allowed value
288
+ name: Parameter name for error messages
289
+
290
+ Raises:
291
+ ValidationError: If value is out of range
292
+ """
293
+ if not isinstance(value, int):
294
+ raise ValidationError(f"{name} must be an integer")
295
+
296
+ if value < min_value or value > max_value:
297
+ raise ValidationError(f"{name} ({value}) must be between {min_value} and {max_value}")
298
+
299
+
300
+ def validate_percentage(percentage: float, name: str = "percentage") -> None:
301
+ """
302
+ Validate percentage value (0-100)
303
+
304
+ Args:
305
+ percentage: Percentage value
306
+ name: Parameter name for error messages
307
+
308
+ Raises:
309
+ ValidationError: If percentage is invalid
310
+ """
311
+ if not isinstance(percentage, (int, float)):
312
+ raise ValidationError(f"{name} must be a number")
313
+
314
+ if percentage < 0 or percentage > 100:
315
+ raise ValidationError(f"{name} ({percentage}%) must be between 0 and 100")
@@ -0,0 +1,399 @@
1
+ Metadata-Version: 2.4
2
+ Name: pumpswap-python-sdk
3
+ Version: 1.0.0
4
+ Summary: Python SDK for interacting with Pump Swap AMM protocol on Solana
5
+ Home-page: https://github.com/pump-fun/pump-swap-sdk-python
6
+ Author: PumpSwap Python SDK Contributors
7
+ Author-email: PumpSwap Python SDK Contributors <dev@pump.fun>
8
+ Maintainer-email: PumpSwap Team <dev@pump.fun>
9
+ License: MIT
10
+ Project-URL: Documentation, https://docs.pump.fun/sdk/python
11
+ Project-URL: Repository, https://github.com/pump-fun/pump-swap-sdk-python
12
+ Project-URL: Issues, https://github.com/pump-fun/pump-swap-sdk-python/issues
13
+ Project-URL: Homepage, https://pump.fun
14
+ Keywords: solana,blockchain,defi,amm,swap,liquidity,pump,cryptocurrency,trading
15
+ Classifier: Development Status :: 4 - Beta
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.8
20
+ Classifier: Programming Language :: Python :: 3.9
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
+ Classifier: Topic :: Office/Business :: Financial :: Investment
26
+ Classifier: Topic :: System :: Networking
27
+ Requires-Python: >=3.8
28
+ Description-Content-Type: text/markdown
29
+ License-File: LICENSE
30
+ Requires-Dist: solders>=0.20.0
31
+ Requires-Dist: solana>=0.32.0
32
+ Requires-Dist: construct>=2.10.0
33
+ Requires-Dist: typing-extensions>=4.0.0; python_version < "3.10"
34
+ Provides-Extra: dev
35
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
36
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
37
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
38
+ Requires-Dist: black>=23.0.0; extra == "dev"
39
+ Requires-Dist: isort>=5.0.0; extra == "dev"
40
+ Requires-Dist: flake8>=6.0.0; extra == "dev"
41
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
42
+ Requires-Dist: pre-commit>=3.0.0; extra == "dev"
43
+ Provides-Extra: docs
44
+ Requires-Dist: sphinx>=6.0.0; extra == "docs"
45
+ Requires-Dist: sphinx-rtd-theme>=1.2.0; extra == "docs"
46
+ Requires-Dist: myst-parser>=1.0.0; extra == "docs"
47
+ Provides-Extra: testing
48
+ Requires-Dist: pytest>=7.0.0; extra == "testing"
49
+ Requires-Dist: pytest-cov>=4.0.0; extra == "testing"
50
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "testing"
51
+ Requires-Dist: responses>=0.23.0; extra == "testing"
52
+ Dynamic: author
53
+ Dynamic: home-page
54
+ Dynamic: license-file
55
+ Dynamic: requires-python
56
+
57
+ # PumpSwap SDK Python
58
+
59
+ A complete Python SDK for interacting with the Pump Swap AMM protocol on Solana. This SDK provides both high-level and low-level interfaces for creating pools, performing swaps, managing liquidity, and collecting fees.
60
+
61
+ ## Features
62
+
63
+ - 🔄 **Token Swaps**: Buy and sell tokens using constant product AMM formula
64
+ - 💧 **Liquidity Management**: Add and remove liquidity from pools
65
+ - 🏭 **Pool Creation**: Create new AMM pools with custom parameters
66
+ - 💰 **Fee Collection**: Collect protocol and creator fees
67
+ - 🎁 **Token Incentives**: Claim trading rewards and incentives
68
+ - 📊 **Real-time Data**: Fetch live pool data and user balances
69
+ - 🔧 **Admin Functions**: Protocol administration and configuration
70
+ - ✅ **Full Validation**: Comprehensive input validation and error handling
71
+
72
+ ## Installation
73
+
74
+ ```bash
75
+ pip install pumpswap-python-sdk
76
+ ```
77
+
78
+ For development:
79
+
80
+ ```bash
81
+ pip install pumpswap-python-sdk[dev]
82
+ ```
83
+
84
+ ## Quick Start
85
+
86
+ ### Basic Usage
87
+
88
+ ```python
89
+ from pump_swap_sdk import PumpAmmSdk, OnlinePumpAmmSdk
90
+ from solana.rpc.api import Client
91
+ from solders.pubkey import Pubkey
92
+
93
+ # Initialize SDK with blockchain connection
94
+ connection = Client("https://api.mainnet-beta.solana.com")
95
+ sdk = OnlinePumpAmmSdk(connection=connection)
96
+
97
+ # Get pool state for swaps
98
+ pool_address = Pubkey.from_string("YOUR_POOL_ADDRESS")
99
+ user_address = Pubkey.from_string("YOUR_WALLET_ADDRESS")
100
+
101
+ swap_state = sdk.swap_solana_state(pool_address, user_address)
102
+
103
+ # Calculate swap amounts
104
+ result = sdk.buy_base_input(
105
+ swap_state=swap_state,
106
+ base=1_000_000, # 1 token (6 decimals)
107
+ slippage=100 # 1% slippage
108
+ )
109
+
110
+ print(f"Quote needed: {result.ui_quote}")
111
+ print(f"Max quote with slippage: {result.max_quote}")
112
+ ```
113
+
114
+ ### Creating a Pool
115
+
116
+ ```python
117
+ from pump_swap_sdk import OnlinePumpAmmSdk
118
+ from solders.keypair import Keypair
119
+
120
+ # Initialize with your keypair
121
+ creator_keypair = Keypair.from_secret_key(your_secret_key)
122
+ sdk = OnlinePumpAmmSdk(connection=connection)
123
+
124
+ # Create pool state
125
+ create_state = sdk.create_pool_solana_state(
126
+ index=0,
127
+ creator=creator_keypair.pubkey(),
128
+ base_mint=Pubkey.from_string("BASE_TOKEN_MINT"),
129
+ quote_mint=Pubkey.from_string("QUOTE_TOKEN_MINT")
130
+ )
131
+
132
+ # Build creation instructions
133
+ instructions = sdk.create_pool_instructions(
134
+ create_pool_state=create_state,
135
+ base_in=1_000_000_000, # Initial base tokens
136
+ quote_in=1_000_000_000 # Initial quote tokens
137
+ )
138
+ ```
139
+
140
+ ### Managing Liquidity
141
+
142
+ ```python
143
+ # Get liquidity state
144
+ liquidity_state = sdk.liquidity_solana_state(pool_address, user_address)
145
+
146
+ # Add liquidity with base token amount
147
+ deposit_result = sdk.deposit_base_input(
148
+ liquidity_state=liquidity_state,
149
+ base=1_000_000, # Base token amount
150
+ slippage=100 # 1% slippage
151
+ )
152
+
153
+ print(f"Quote needed: {deposit_result.quote}")
154
+ print(f"LP tokens to receive: {deposit_result.lp_token}")
155
+
156
+ # Remove liquidity
157
+ withdraw_result = sdk.withdraw_inputs(
158
+ liquidity_state=liquidity_state,
159
+ lp_amount=500_000, # LP tokens to burn
160
+ slippage=100 # 1% slippage
161
+ )
162
+
163
+ print(f"Base tokens to receive: {withdraw_result.base}")
164
+ print(f"Quote tokens to receive: {withdraw_result.quote}")
165
+ ```
166
+
167
+ ## Advanced Usage
168
+
169
+ ### Offline SDK (No Blockchain Connection)
170
+
171
+ ```python
172
+ from pump_swap_sdk import PumpAmmSdk
173
+
174
+ # Use offline SDK for calculations without network calls
175
+ offline_sdk = PumpAmmSdk()
176
+
177
+ # You'll need to provide the state data manually
178
+ from pump_swap_sdk.types import SwapSolanaState, Pool, GlobalConfig
179
+
180
+ # Build state with your data
181
+ swap_state = SwapSolanaState(
182
+ pool=your_pool_data,
183
+ global_config=your_global_config,
184
+ pool_base_amount=base_reserve,
185
+ pool_quote_amount=quote_reserve,
186
+ # ... other required fields
187
+ )
188
+
189
+ # Perform calculations
190
+ result = offline_sdk.buy_base_input(swap_state, 1_000_000, 100)
191
+ ```
192
+
193
+ ### Fee Calculations
194
+
195
+ ```python
196
+ from pump_swap_sdk.sdk.fees import compute_fees_bps
197
+
198
+ # Calculate current fee rates
199
+ fees = compute_fees_bps(
200
+ global_config=global_config,
201
+ fee_config=fee_config,
202
+ market_cap=current_market_cap
203
+ )
204
+
205
+ print(f"LP Fee: {fees.lp_fee_bps} bps")
206
+ print(f"Protocol Fee: {fees.protocol_fee_bps} bps")
207
+ print(f"Creator Fee: {fees.creator_fee_bps} bps")
208
+ print(f"Total Fee: {fees.total_fee_bps} bps")
209
+ ```
210
+
211
+ ### Token Incentives
212
+
213
+ ```python
214
+ # Check unclaimed rewards
215
+ unclaimed = sdk.get_total_unclaimed_tokens(user_address)
216
+ print(f"Unclaimed tokens: {unclaimed}")
217
+
218
+ # Check today's earned rewards
219
+ today_rewards = sdk.get_current_day_tokens(user_address)
220
+ print(f"Today's rewards: {today_rewards}")
221
+ ```
222
+
223
+ ## SDK Architecture
224
+
225
+ ### Core Classes
226
+
227
+ - **`PumpAmmSdk`**: High-level offline SDK for calculations
228
+ - **`OnlinePumpAmmSdk`**: Online SDK with blockchain connectivity
229
+ - **`PumpAmmAdminSdk`**: Admin SDK for protocol management
230
+
231
+ ### Key Modules
232
+
233
+ - **`swap_operations`**: Buy/sell token calculations
234
+ - **`liquidity_operations`**: Deposit/withdraw liquidity calculations
235
+ - **`fees`**: Fee calculation utilities
236
+ - **`token_incentives`**: Trading reward calculations
237
+ - **`pda`**: Program Derived Address utilities
238
+ - **`utils`**: Mathematical and utility functions
239
+
240
+ ## Mathematical Formulas
241
+
242
+ The SDK implements a **constant product AMM** using the formula `x * y = k`:
243
+
244
+ ### Swap Calculations
245
+
246
+ **Buy tokens (base input)**:
247
+ ```
248
+ quote_needed = ceil_div(quote_reserve * base_amount, base_reserve - base_amount)
249
+ ```
250
+
251
+ **Sell tokens (base input)**:
252
+ ```
253
+ quote_received = floor_div(quote_reserve * base_amount, base_reserve + base_amount)
254
+ ```
255
+
256
+ ### Liquidity Calculations
257
+
258
+ **LP tokens for deposit**:
259
+ ```
260
+ lp_tokens = min(
261
+ base_amount * total_lp_supply / base_reserve,
262
+ quote_amount * total_lp_supply / quote_reserve
263
+ )
264
+ ```
265
+
266
+ **Tokens from LP withdrawal**:
267
+ ```
268
+ base_amount = lp_amount * base_reserve / total_lp_supply
269
+ quote_amount = lp_amount * quote_reserve / total_lp_supply
270
+ ```
271
+
272
+ ## Error Handling
273
+
274
+ The SDK provides comprehensive error handling:
275
+
276
+ ```python
277
+ from pump_swap_sdk.exceptions import (
278
+ InsufficientLiquidityError,
279
+ SlippageExceededError,
280
+ ValidationError
281
+ )
282
+
283
+ try:
284
+ result = sdk.buy_base_input(swap_state, amount, slippage)
285
+ except InsufficientLiquidityError:
286
+ print("Not enough liquidity for this trade")
287
+ except SlippageExceededError:
288
+ print("Price moved beyond slippage tolerance")
289
+ except ValidationError as e:
290
+ print(f"Invalid input: {e}")
291
+ ```
292
+
293
+ ## Configuration
294
+
295
+ ### Environment Variables
296
+
297
+ ```bash
298
+ # Solana RPC endpoint
299
+ SOLANA_RPC_URL=https://api.mainnet-beta.solana.com
300
+
301
+ # Program ID (optional, defaults to mainnet)
302
+ PUMP_AMM_PROGRAM_ID=6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P
303
+ ```
304
+
305
+ ### Custom Configuration
306
+
307
+ ```python
308
+ from pump_swap_sdk import OnlinePumpAmmSdk
309
+ from solders.pubkey import Pubkey
310
+
311
+ # Use custom program ID
312
+ custom_program_id = Pubkey.from_string("YOUR_PROGRAM_ID")
313
+ sdk = OnlinePumpAmmSdk(
314
+ connection=connection,
315
+ program_id=custom_program_id
316
+ )
317
+ ```
318
+
319
+ ## Testing
320
+
321
+ Run the test suite:
322
+
323
+ ```bash
324
+ # Install development dependencies
325
+ pip install -e .[dev]
326
+
327
+ # Run tests
328
+ pytest
329
+
330
+ # Run with coverage
331
+ pytest --cov=pump_swap_sdk --cov-report=html
332
+
333
+ # Run specific test categories
334
+ pytest -m unit # Unit tests only
335
+ pytest -m integration # Integration tests only
336
+ ```
337
+
338
+ ## Examples
339
+
340
+ Check out the `examples/` directory for complete working examples:
341
+
342
+ - **`basic_swap.py`**: Simple token swap
343
+ - **`liquidity_management.py`**: Add/remove liquidity
344
+ - **`pool_creation.py`**: Create a new AMM pool
345
+ - **`fee_calculation.py`**: Calculate trading fees
346
+ - **`admin_operations.py`**: Protocol administration
347
+
348
+ ## Contributing
349
+
350
+ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
351
+
352
+ ### Development Setup
353
+
354
+ ```bash
355
+ # Clone the repository
356
+ git clone https://github.com/pump-fun/pump-swap-sdk-python.git
357
+ cd pump-swap-sdk-python
358
+
359
+ # Create virtual environment
360
+ python -m venv venv
361
+ source venv/bin/activate # Linux/Mac
362
+ # or
363
+ venv\\Scripts\\activate # Windows
364
+
365
+ # Install in development mode
366
+ pip install -e .[dev]
367
+
368
+ # Install pre-commit hooks
369
+ pre-commit install
370
+
371
+ # Run tests
372
+ pytest
373
+ ```
374
+
375
+ ## Documentation
376
+
377
+ - [Full API Documentation](https://docs.pump.fun/sdk/python)
378
+ - [Protocol Documentation](https://docs.pump.fun)
379
+ - [Examples](./examples/)
380
+
381
+ ## Support
382
+
383
+ - [GitHub Issues](https://github.com/pump-fun/pump-swap-sdk-python/issues)
384
+ - [Discord Community](https://discord.gg/pump)
385
+ - [Documentation](https://docs.pump.fun)
386
+
387
+ ## License
388
+
389
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
390
+
391
+ ## Related Projects
392
+
393
+ - [Pump Swap TypeScript SDK](https://github.com/pump-fun/pump-swap-sdk)
394
+ - [Pump.fun Frontend](https://pump.fun)
395
+ - [Solana Program](https://github.com/pump-fun/pump-swap-program)
396
+
397
+ ---
398
+
399
+ Built with ❤️ by the Pump.fun team