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,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
|