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,302 @@
1
+ """
2
+ Swap operation implementations (buy/sell) for the PumpSwap SDK
3
+ """
4
+
5
+ from typing import Dict, Any
6
+ from ..types.sdk_types import (
7
+ BuyBaseInputResult, BuyQuoteInputResult,
8
+ SellBaseInputResult, SellQuoteInputResult
9
+ )
10
+ from ..types.amm_types import PoolReserves, GlobalConfig, ComputeFeesResult
11
+ from .utils import (
12
+ ceil_div, floor_div, fee, apply_slippage,
13
+ calculate_swap_price, calculate_inverse_swap_price
14
+ )
15
+ from .fees import compute_fees_bps, calculate_swap_fees
16
+
17
+
18
+ def buy_base_input(
19
+ base: int,
20
+ slippage: int,
21
+ base_reserve: int,
22
+ quote_reserve: int,
23
+ global_config: GlobalConfig,
24
+ creator=None,
25
+ fee_config=None,
26
+ market_cap: int = 0
27
+ ) -> BuyBaseInputResult:
28
+ """
29
+ Calculate quote amount needed to buy a specific base amount
30
+
31
+ Uses the constant product formula: quote_in = ceil_div(quote_reserve * base, base_reserve - base)
32
+
33
+ Args:
34
+ base: Desired base token amount to buy
35
+ slippage: Slippage tolerance in basis points
36
+ base_reserve: Current base token reserve
37
+ quote_reserve: Current quote token reserve
38
+ global_config: Global configuration
39
+ creator: Pool creator (for fee calculation)
40
+ fee_config: Fee configuration
41
+ market_cap: Token market cap (for fee tier calculation)
42
+
43
+ Returns:
44
+ BuyBaseInputResult with quote amounts and slippage protection
45
+ """
46
+ if base >= base_reserve:
47
+ raise ValueError("Insufficient base token liquidity")
48
+
49
+ if base <= 0:
50
+ raise ValueError("Base amount must be positive")
51
+
52
+ # Calculate fees
53
+ fees_result = compute_fees_bps(
54
+ global_config=global_config,
55
+ fee_config=fee_config or _default_fee_config(),
56
+ creator=creator,
57
+ market_cap=market_cap
58
+ )
59
+
60
+ # Calculate quote amount needed using constant product formula
61
+ # x * y = k, where x is base reserve, y is quote reserve
62
+ # When buying base tokens: new_base = base_reserve - base
63
+ # new_quote * new_base = k = quote_reserve * base_reserve
64
+ # new_quote = (quote_reserve * base_reserve) / (base_reserve - base)
65
+ # quote_in = new_quote - quote_reserve
66
+ numerator = quote_reserve * base
67
+ denominator = base_reserve - base
68
+ internal_quote_amount = ceil_div(numerator, denominator)
69
+
70
+ # Calculate fees on the quote input
71
+ fee_amounts = calculate_swap_fees(internal_quote_amount, fees_result)
72
+
73
+ # Total quote needed including fees
74
+ ui_quote = internal_quote_amount + fee_amounts["total_fee_amount"]
75
+
76
+ # Apply slippage protection for maximum quote
77
+ max_quote = apply_slippage(ui_quote, slippage, is_maximum=True)
78
+
79
+ return BuyBaseInputResult(
80
+ internal_quote_amount=internal_quote_amount,
81
+ ui_quote=ui_quote,
82
+ max_quote=max_quote
83
+ )
84
+
85
+
86
+ def buy_quote_input(
87
+ quote: int,
88
+ slippage: int,
89
+ base_reserve: int,
90
+ quote_reserve: int,
91
+ global_config: GlobalConfig,
92
+ creator=None,
93
+ fee_config=None,
94
+ market_cap: int = 0
95
+ ) -> BuyQuoteInputResult:
96
+ """
97
+ Calculate base amount received when spending a specific quote amount
98
+
99
+ Args:
100
+ quote: Quote token amount to spend
101
+ slippage: Slippage tolerance in basis points
102
+ base_reserve: Current base token reserve
103
+ quote_reserve: Current quote token reserve
104
+ global_config: Global configuration
105
+ creator: Pool creator (for fee calculation)
106
+ fee_config: Fee configuration
107
+ market_cap: Token market cap (for fee tier calculation)
108
+
109
+ Returns:
110
+ BuyQuoteInputResult with base amount and fee details
111
+ """
112
+ if quote <= 0:
113
+ raise ValueError("Quote amount must be positive")
114
+
115
+ # Calculate fees
116
+ fees_result = compute_fees_bps(
117
+ global_config=global_config,
118
+ fee_config=fee_config or _default_fee_config(),
119
+ creator=creator,
120
+ market_cap=market_cap
121
+ )
122
+
123
+ # Calculate fee amounts
124
+ fee_amounts = calculate_swap_fees(quote, fees_result)
125
+ internal_quote_without_fees = fee_amounts["amount_after_fees"]
126
+
127
+ if internal_quote_without_fees <= 0:
128
+ raise ValueError("Quote amount too small after fees")
129
+
130
+ # Calculate base amount using constant product formula
131
+ # base_out = (base_reserve * internal_quote_without_fees) / (quote_reserve + internal_quote_without_fees)
132
+ numerator = base_reserve * internal_quote_without_fees
133
+ denominator = quote_reserve + internal_quote_without_fees
134
+ base = floor_div(numerator, denominator)
135
+
136
+ if base <= 0:
137
+ raise ValueError("Insufficient output amount")
138
+
139
+ # Apply slippage protection for maximum quote (in case of price movement)
140
+ max_quote = apply_slippage(quote, slippage, is_maximum=True)
141
+
142
+ return BuyQuoteInputResult(
143
+ base=base,
144
+ internal_quote_without_fees=internal_quote_without_fees,
145
+ max_quote=max_quote
146
+ )
147
+
148
+
149
+ def sell_base_input(
150
+ base: int,
151
+ slippage: int,
152
+ base_reserve: int,
153
+ quote_reserve: int,
154
+ global_config: GlobalConfig,
155
+ creator=None,
156
+ fee_config=None,
157
+ market_cap: int = 0
158
+ ) -> SellBaseInputResult:
159
+ """
160
+ Calculate quote amount received when selling a specific base amount
161
+
162
+ Uses the constant product formula: quote_out = (quote_reserve * base) / (base_reserve + base)
163
+
164
+ Args:
165
+ base: Base token amount to sell
166
+ slippage: Slippage tolerance in basis points
167
+ base_reserve: Current base token reserve
168
+ quote_reserve: Current quote token reserve
169
+ global_config: Global configuration
170
+ creator: Pool creator (for fee calculation)
171
+ fee_config: Fee configuration
172
+ market_cap: Token market cap (for fee tier calculation)
173
+
174
+ Returns:
175
+ SellBaseInputResult with quote amounts and slippage protection
176
+ """
177
+ if base <= 0:
178
+ raise ValueError("Base amount must be positive")
179
+
180
+ # Calculate fees
181
+ fees_result = compute_fees_bps(
182
+ global_config=global_config,
183
+ fee_config=fee_config or _default_fee_config(),
184
+ creator=creator,
185
+ market_cap=market_cap
186
+ )
187
+
188
+ # Calculate quote output using constant product formula
189
+ # x * y = k, where x is base reserve, y is quote reserve
190
+ # When selling base tokens: new_base = base_reserve + base
191
+ # new_quote * new_base = k = quote_reserve * base_reserve
192
+ # new_quote = (quote_reserve * base_reserve) / (base_reserve + base)
193
+ # quote_out = quote_reserve - new_quote
194
+ numerator = quote_reserve * base
195
+ denominator = base_reserve + base
196
+ internal_quote_amount_out = floor_div(numerator, denominator)
197
+
198
+ # Calculate fees on the quote output
199
+ fee_amounts = calculate_swap_fees(internal_quote_amount_out, fees_result)
200
+
201
+ # Quote amount user receives after fees
202
+ ui_quote = internal_quote_amount_out - fee_amounts["total_fee_amount"]
203
+
204
+ if ui_quote <= 0:
205
+ raise ValueError("Output amount too small after fees")
206
+
207
+ # Apply slippage protection for minimum quote
208
+ min_quote = apply_slippage(ui_quote, slippage, is_maximum=False)
209
+
210
+ return SellBaseInputResult(
211
+ ui_quote=ui_quote,
212
+ min_quote=min_quote,
213
+ internal_quote_amount_out=internal_quote_amount_out
214
+ )
215
+
216
+
217
+ def sell_quote_input(
218
+ quote: int,
219
+ slippage: int,
220
+ base_reserve: int,
221
+ quote_reserve: int,
222
+ global_config: GlobalConfig,
223
+ creator=None,
224
+ fee_config=None,
225
+ market_cap: int = 0
226
+ ) -> SellQuoteInputResult:
227
+ """
228
+ Calculate base amount needed to sell to receive a specific quote amount
229
+
230
+ Args:
231
+ quote: Desired quote token amount to receive
232
+ slippage: Slippage tolerance in basis points
233
+ base_reserve: Current base token reserve
234
+ quote_reserve: Current quote token reserve
235
+ global_config: Global configuration
236
+ creator: Pool creator (for fee calculation)
237
+ fee_config: Fee configuration
238
+ market_cap: Token market cap (for fee tier calculation)
239
+
240
+ Returns:
241
+ SellQuoteInputResult with base amount and fee details
242
+ """
243
+ if quote <= 0:
244
+ raise ValueError("Quote amount must be positive")
245
+
246
+ # Calculate fees
247
+ fees_result = compute_fees_bps(
248
+ global_config=global_config,
249
+ fee_config=fee_config or _default_fee_config(),
250
+ creator=creator,
251
+ market_cap=market_cap
252
+ )
253
+
254
+ # We need to find the raw quote amount before fees that would give us the desired quote after fees
255
+ # Let raw_quote = quote amount before fees
256
+ # quote_after_fees = raw_quote - fees
257
+ # quote = raw_quote * (1 - total_fee_rate)
258
+ # raw_quote = quote / (1 - total_fee_rate)
259
+
260
+ total_fee_rate = fees_result.total_fee_bps / 10000 # Convert to decimal
261
+ if total_fee_rate >= 1.0:
262
+ raise ValueError("Fee rate too high")
263
+
264
+ # Calculate raw quote needed before fees
265
+ internal_raw_quote = ceil_div(quote * 10000, 10000 - fees_result.total_fee_bps)
266
+
267
+ if internal_raw_quote >= quote_reserve:
268
+ raise ValueError("Insufficient quote token liquidity")
269
+
270
+ # Calculate base amount needed using inverse constant product formula
271
+ # base_in = (base_reserve * internal_raw_quote) / (quote_reserve - internal_raw_quote)
272
+ numerator = base_reserve * internal_raw_quote
273
+ denominator = quote_reserve - internal_raw_quote
274
+
275
+ if denominator <= 0:
276
+ raise ValueError("Insufficient liquidity for desired quote amount")
277
+
278
+ base = ceil_div(numerator, denominator)
279
+
280
+ if base <= 0:
281
+ raise ValueError("Invalid base amount calculated")
282
+
283
+ # Apply slippage protection for minimum quote
284
+ min_quote = apply_slippage(quote, slippage, is_maximum=False)
285
+
286
+ return SellQuoteInputResult(
287
+ internal_raw_quote=internal_raw_quote,
288
+ base=base,
289
+ min_quote=min_quote
290
+ )
291
+
292
+
293
+ def _default_fee_config():
294
+ """Create a default fee configuration for testing"""
295
+ from ..types.amm_types import FeeConfig
296
+
297
+ return FeeConfig(
298
+ fee_tiers=[],
299
+ default_lp_fee_rate_bps=25, # 0.25%
300
+ default_protocol_fee_rate_bps=5, # 0.05%
301
+ default_creator_fee_rate_bps=0 # 0%
302
+ )
@@ -0,0 +1,254 @@
1
+ """
2
+ Token incentives functionality for the PumpSwap SDK
3
+ """
4
+
5
+ from typing import Optional
6
+ from solders.pubkey import Pubkey
7
+ from ..types.amm_types import UserVolumeAccumulator, GlobalVolumeAccumulator
8
+
9
+
10
+ def total_unclaimed_tokens(user_volume_accumulator: UserVolumeAccumulator) -> int:
11
+ """
12
+ Calculate total unclaimed token incentives for a user
13
+
14
+ Args:
15
+ user_volume_accumulator: User's volume accumulator data
16
+
17
+ Returns:
18
+ Total unclaimed token amount
19
+ """
20
+ return user_volume_accumulator.unclaimed_token_incentives
21
+
22
+
23
+ def current_day_tokens(
24
+ user_volume_accumulator: UserVolumeAccumulator,
25
+ global_volume_accumulator: GlobalVolumeAccumulator,
26
+ daily_incentives_pool: int,
27
+ current_day: int
28
+ ) -> int:
29
+ """
30
+ Calculate token incentives earned for the current day
31
+
32
+ Args:
33
+ user_volume_accumulator: User's volume accumulator data
34
+ global_volume_accumulator: Global volume accumulator data
35
+ daily_incentives_pool: Total daily incentives available
36
+ current_day: Current day identifier
37
+
38
+ Returns:
39
+ Token incentives earned today
40
+ """
41
+ # Check if user has any volume today
42
+ if (user_volume_accumulator.last_updated_day != current_day or
43
+ user_volume_accumulator.daily_volume == 0):
44
+ return 0
45
+
46
+ # Check if global volume exists
47
+ if (global_volume_accumulator.last_updated_day != current_day or
48
+ global_volume_accumulator.daily_volume == 0):
49
+ return 0
50
+
51
+ # Calculate user's share of daily incentives
52
+ # Formula: user_daily_volume / global_daily_volume * daily_incentives_pool
53
+ user_share = (user_volume_accumulator.daily_volume * daily_incentives_pool) // global_volume_accumulator.daily_volume
54
+
55
+ return user_share
56
+
57
+
58
+ def calculate_volume_based_incentives(
59
+ user_volume: int,
60
+ total_volume: int,
61
+ incentive_pool: int,
62
+ minimum_volume_threshold: int = 0
63
+ ) -> int:
64
+ """
65
+ Calculate incentives based on volume proportion
66
+
67
+ Args:
68
+ user_volume: User's trading volume
69
+ total_volume: Total trading volume
70
+ incentive_pool: Total incentives to distribute
71
+ minimum_volume_threshold: Minimum volume required to earn incentives
72
+
73
+ Returns:
74
+ User's share of incentives
75
+ """
76
+ if user_volume < minimum_volume_threshold:
77
+ return 0
78
+
79
+ if total_volume == 0:
80
+ return 0
81
+
82
+ # Proportional distribution
83
+ return (user_volume * incentive_pool) // total_volume
84
+
85
+
86
+ def calculate_fee_based_incentives(
87
+ user_fees_paid: int,
88
+ total_fees_collected: int,
89
+ incentive_pool: int
90
+ ) -> int:
91
+ """
92
+ Calculate incentives based on fees paid proportion
93
+
94
+ Args:
95
+ user_fees_paid: Fees paid by user
96
+ total_fees_collected: Total fees collected
97
+ incentive_pool: Total incentives to distribute
98
+
99
+ Returns:
100
+ User's share of incentives
101
+ """
102
+ if total_fees_collected == 0:
103
+ return 0
104
+
105
+ return (user_fees_paid * incentive_pool) // total_fees_collected
106
+
107
+
108
+ def calculate_time_weighted_incentives(
109
+ user_volume: int,
110
+ days_active: int,
111
+ base_incentive: int,
112
+ time_multiplier: float = 1.1
113
+ ) -> int:
114
+ """
115
+ Calculate incentives with time weighting for consistent users
116
+
117
+ Args:
118
+ user_volume: User's trading volume
119
+ days_active: Number of consecutive active days
120
+ base_incentive: Base incentive amount
121
+ time_multiplier: Multiplier per active day
122
+
123
+ Returns:
124
+ Time-weighted incentive amount
125
+ """
126
+ if user_volume == 0 or days_active == 0:
127
+ return 0
128
+
129
+ # Apply time multiplier (capped at reasonable maximum)
130
+ max_multiplier = 3.0 # 3x maximum
131
+ effective_multiplier = min(time_multiplier ** (days_active - 1), max_multiplier)
132
+
133
+ return int(base_incentive * effective_multiplier)
134
+
135
+
136
+ def get_current_day() -> int:
137
+ """
138
+ Get current day identifier (days since epoch)
139
+
140
+ Returns:
141
+ Current day as integer
142
+ """
143
+ import time
144
+ return int(time.time()) // (24 * 60 * 60) # Days since Unix epoch
145
+
146
+
147
+ def is_eligible_for_incentives(
148
+ user_volume_accumulator: UserVolumeAccumulator,
149
+ minimum_volume: int = 0,
150
+ minimum_fees: int = 0
151
+ ) -> bool:
152
+ """
153
+ Check if user is eligible for token incentives
154
+
155
+ Args:
156
+ user_volume_accumulator: User's volume data
157
+ minimum_volume: Minimum volume requirement
158
+ minimum_fees: Minimum fees requirement
159
+
160
+ Returns:
161
+ True if eligible for incentives
162
+ """
163
+ return (user_volume_accumulator.daily_volume >= minimum_volume and
164
+ user_volume_accumulator.daily_fees >= minimum_fees)
165
+
166
+
167
+ def estimate_daily_incentives(
168
+ projected_volume: int,
169
+ estimated_total_volume: int,
170
+ daily_incentive_pool: int
171
+ ) -> int:
172
+ """
173
+ Estimate daily incentives based on projected trading volume
174
+
175
+ Args:
176
+ projected_volume: User's projected daily volume
177
+ estimated_total_volume: Estimated total daily volume
178
+ daily_incentive_pool: Total daily incentives available
179
+
180
+ Returns:
181
+ Estimated daily incentive amount
182
+ """
183
+ if estimated_total_volume == 0:
184
+ return 0
185
+
186
+ return (projected_volume * daily_incentive_pool) // estimated_total_volume
187
+
188
+
189
+ def calculate_claimable_amount(
190
+ user_volume_accumulator: UserVolumeAccumulator,
191
+ current_day: int
192
+ ) -> int:
193
+ """
194
+ Calculate total claimable token amount for a user
195
+
196
+ Args:
197
+ user_volume_accumulator: User's volume accumulator
198
+ current_day: Current day identifier
199
+
200
+ Returns:
201
+ Total claimable token amount
202
+ """
203
+ base_claimable = user_volume_accumulator.unclaimed_token_incentives
204
+
205
+ # If user has activity today and hasn't claimed today's rewards yet
206
+ if (user_volume_accumulator.last_updated_day == current_day and
207
+ user_volume_accumulator.last_claim_day < current_day):
208
+ # Add today's estimated rewards
209
+ # This would need global data to calculate precisely
210
+ # For now, return base amount
211
+ pass
212
+
213
+ return base_claimable
214
+
215
+
216
+ def update_user_volume_accumulator(
217
+ user_volume_accumulator: UserVolumeAccumulator,
218
+ volume_increase: int,
219
+ fees_increase: int,
220
+ current_day: int
221
+ ) -> UserVolumeAccumulator:
222
+ """
223
+ Update user volume accumulator with new trading activity
224
+
225
+ Args:
226
+ user_volume_accumulator: Current user volume data
227
+ volume_increase: Additional volume to add
228
+ fees_increase: Additional fees to add
229
+ current_day: Current day identifier
230
+
231
+ Returns:
232
+ Updated user volume accumulator
233
+ """
234
+ # Create a copy to avoid mutating the original
235
+ updated = UserVolumeAccumulator(
236
+ user=user_volume_accumulator.user,
237
+ total_volume=user_volume_accumulator.total_volume + volume_increase,
238
+ total_fees=user_volume_accumulator.total_fees + fees_increase,
239
+ daily_volume=user_volume_accumulator.daily_volume,
240
+ daily_fees=user_volume_accumulator.daily_fees,
241
+ last_updated_day=current_day,
242
+ unclaimed_token_incentives=user_volume_accumulator.unclaimed_token_incentives,
243
+ last_claim_day=user_volume_accumulator.last_claim_day
244
+ )
245
+
246
+ # Reset daily counters if it's a new day
247
+ if user_volume_accumulator.last_updated_day != current_day:
248
+ updated.daily_volume = volume_increase
249
+ updated.daily_fees = fees_increase
250
+ else:
251
+ updated.daily_volume += volume_increase
252
+ updated.daily_fees += fees_increase
253
+
254
+ return updated