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.
- pump_swap_sdk/__init__.py +37 -0
- pump_swap_sdk/constants.py +30 -0
- pump_swap_sdk/exceptions.py +83 -0
- pump_swap_sdk/sdk/fees.py +171 -0
- pump_swap_sdk/sdk/liquidity_operations.py +328 -0
- pump_swap_sdk/sdk/online_pump_amm_sdk.py +431 -0
- pump_swap_sdk/sdk/pda.py +231 -0
- pump_swap_sdk/sdk/pump_amm_admin_sdk.py +303 -0
- pump_swap_sdk/sdk/pump_amm_sdk.py +503 -0
- pump_swap_sdk/sdk/swap_operations.py +302 -0
- pump_swap_sdk/sdk/token_incentives.py +254 -0
- pump_swap_sdk/sdk/utils.py +273 -0
- pump_swap_sdk/types/amm_types.py +153 -0
- pump_swap_sdk/types/sdk_types.py +193 -0
- pump_swap_sdk/validators.py +315 -0
- pumpswap_python_sdk-1.0.0.dist-info/METADATA +399 -0
- pumpswap_python_sdk-1.0.0.dist-info/RECORD +21 -0
- pumpswap_python_sdk-1.0.0.dist-info/WHEEL +5 -0
- pumpswap_python_sdk-1.0.0.dist-info/entry_points.txt +2 -0
- pumpswap_python_sdk-1.0.0.dist-info/licenses/LICENSE +21 -0
- pumpswap_python_sdk-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -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
|