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,37 @@
1
+ """
2
+ PumpSwap SDK - Python implementation for interacting with Pump Swap AMM protocol on Solana
3
+
4
+ This SDK provides high-level and low-level interfaces for:
5
+ - Creating AMM pools
6
+ - Performing token swaps (buy/sell)
7
+ - Managing liquidity (deposit/withdraw)
8
+ - Collecting fees
9
+ - Token incentives
10
+ """
11
+
12
+ from .sdk.pump_amm_sdk import PumpAmmSdk
13
+ from .sdk.online_pump_amm_sdk import OnlinePumpAmmSdk
14
+ from .sdk.pump_amm_admin_sdk import PumpAmmAdminSdk
15
+ from .sdk.swap_operations import buy_base_input, buy_quote_input, sell_base_input, sell_quote_input
16
+ from .sdk.liquidity_operations import deposit_lp_token, withdraw
17
+ from .sdk.token_incentives import total_unclaimed_tokens, current_day_tokens
18
+ from .types.sdk_types import *
19
+ from .types.amm_types import *
20
+ from .constants import *
21
+
22
+ __version__ = "1.0.0"
23
+ __author__ = "PumpSwap Python SDK"
24
+
25
+ __all__ = [
26
+ "PumpAmmSdk",
27
+ "OnlinePumpAmmSdk",
28
+ "PumpAmmAdminSdk",
29
+ "buy_base_input",
30
+ "buy_quote_input",
31
+ "sell_base_input",
32
+ "sell_quote_input",
33
+ "deposit_lp_token",
34
+ "withdraw",
35
+ "total_unclaimed_tokens",
36
+ "current_day_tokens",
37
+ ]
@@ -0,0 +1,30 @@
1
+ """
2
+ Constants used throughout the PumpSwap SDK
3
+ """
4
+
5
+ from solders.pubkey import Pubkey
6
+ from typing import Final
7
+
8
+ # Program IDs
9
+ PUMP_AMM_PROGRAM_ID: Final[Pubkey] = Pubkey.from_string("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P")
10
+ TOKEN_PROGRAM_ID: Final[Pubkey] = Pubkey.from_string("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
11
+ TOKEN_2022_PROGRAM_ID: Final[Pubkey] = Pubkey.from_string("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb")
12
+ ASSOCIATED_TOKEN_PROGRAM_ID: Final[Pubkey] = Pubkey.from_string("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL")
13
+ NATIVE_MINT: Final[Pubkey] = Pubkey.from_string("So11111111111111111111111111111111111111112")
14
+
15
+ # Pool Constants
16
+ POOL_ACCOUNT_NEW_SIZE: Final[int] = 288
17
+
18
+ # Global PDAs
19
+ GLOBAL_CONFIG_SEED: Final[str] = "global_config"
20
+ GLOBAL_VOLUME_ACCUMULATOR_SEED: Final[str] = "global_volume_accumulator"
21
+ PUMP_AMM_EVENT_AUTHORITY_SEED: Final[str] = "event_authority"
22
+
23
+ # Fee Configuration
24
+ DEFAULT_SLIPPAGE_BPS: Final[int] = 100 # 1%
25
+ MAX_SLIPPAGE_BPS: Final[int] = 1000 # 10%
26
+ BASIS_POINTS: Final[int] = 10000 # 100%
27
+
28
+ # Precision
29
+ LAMPORTS_PER_SOL: Final[int] = 1_000_000_000
30
+ TOKEN_DECIMALS: Final[int] = 6
@@ -0,0 +1,83 @@
1
+ """
2
+ Custom exceptions for the PumpSwap SDK
3
+ """
4
+
5
+
6
+ class PumpSwapSDKError(Exception):
7
+ """Base exception class for PumpSwap SDK errors"""
8
+ pass
9
+
10
+
11
+ class ValidationError(PumpSwapSDKError):
12
+ """Raised when input validation fails"""
13
+ pass
14
+
15
+
16
+ class InsufficientLiquidityError(PumpSwapSDKError):
17
+ """Raised when there's insufficient liquidity for an operation"""
18
+ pass
19
+
20
+
21
+ class SlippageExceededError(PumpSwapSDKError):
22
+ """Raised when slippage tolerance is exceeded"""
23
+ pass
24
+
25
+
26
+ class InvalidPoolError(PumpSwapSDKError):
27
+ """Raised when pool data is invalid or corrupted"""
28
+ pass
29
+
30
+
31
+ class AccountNotFoundError(PumpSwapSDKError):
32
+ """Raised when a required account is not found on-chain"""
33
+ pass
34
+
35
+
36
+ class UnauthorizedError(PumpSwapSDKError):
37
+ """Raised when user lacks required permissions"""
38
+ pass
39
+
40
+
41
+ class ConfigurationError(PumpSwapSDKError):
42
+ """Raised when SDK configuration is invalid"""
43
+ pass
44
+
45
+
46
+ class FeeCalculationError(PumpSwapSDKError):
47
+ """Raised when fee calculation fails"""
48
+ pass
49
+
50
+
51
+ class NetworkError(PumpSwapSDKError):
52
+ """Raised when network operations fail"""
53
+ pass
54
+
55
+
56
+ class SerializationError(PumpSwapSDKError):
57
+ """Raised when data serialization/deserialization fails"""
58
+ pass
59
+
60
+
61
+ class InstructionBuildError(PumpSwapSDKError):
62
+ """Raised when instruction building fails"""
63
+ pass
64
+
65
+
66
+ class TokenAccountError(PumpSwapSDKError):
67
+ """Raised when token account operations fail"""
68
+ pass
69
+
70
+
71
+ class PoolCreationError(PumpSwapSDKError):
72
+ """Raised when pool creation fails"""
73
+ pass
74
+
75
+
76
+ class SwapError(PumpSwapSDKError):
77
+ """Raised when swap operations fail"""
78
+ pass
79
+
80
+
81
+ class LiquidityError(PumpSwapSDKError):
82
+ """Raised when liquidity operations fail"""
83
+ pass
@@ -0,0 +1,171 @@
1
+ """
2
+ Fee calculation utilities for the PumpSwap SDK
3
+ """
4
+
5
+ from typing import Optional
6
+ from solders.pubkey import Pubkey
7
+ from ..types.amm_types import FeeConfig, GlobalConfig, ComputeFeesResult
8
+ from ..constants import BASIS_POINTS
9
+ from .utils import is_pump_pool
10
+
11
+
12
+ def calculate_fee_tier(fee_tiers: list, market_cap: int) -> Optional[FeeConfig.FeeTier]:
13
+ """
14
+ Find the appropriate fee tier based on market cap
15
+
16
+ Args:
17
+ fee_tiers: List of fee tiers ordered by market cap threshold
18
+ market_cap: Current market cap
19
+
20
+ Returns:
21
+ Matching fee tier or None
22
+ """
23
+ # Tiers should be ordered by threshold (ascending)
24
+ for tier in reversed(fee_tiers):
25
+ if market_cap >= tier.market_cap_threshold:
26
+ return tier
27
+ return None
28
+
29
+
30
+ def compute_fees_bps(
31
+ global_config: GlobalConfig,
32
+ fee_config: FeeConfig,
33
+ creator: Optional[Pubkey] = None,
34
+ market_cap: int = 0,
35
+ mint: Optional[Pubkey] = None
36
+ ) -> ComputeFeesResult:
37
+ """
38
+ Compute fee rates in basis points for a swap
39
+
40
+ Args:
41
+ global_config: Global configuration
42
+ fee_config: Fee configuration with tiers
43
+ creator: Pool creator (for pump pool detection)
44
+ market_cap: Current market cap of the token
45
+ mint: Token mint (for pump pool detection)
46
+
47
+ Returns:
48
+ ComputeFeesResult with all fee rates
49
+ """
50
+ # Start with default fees
51
+ lp_fee_bps = fee_config.default_lp_fee_rate_bps
52
+ protocol_fee_bps = fee_config.default_protocol_fee_rate_bps
53
+ creator_fee_bps = fee_config.default_creator_fee_rate_bps
54
+
55
+ # Check if mayhem mode is active (higher fees)
56
+ if global_config.is_mayhem_mode:
57
+ # Apply mayhem mode multiplier (example: 2x fees)
58
+ lp_fee_bps *= 2
59
+ protocol_fee_bps *= 2
60
+ creator_fee_bps *= 2
61
+
62
+ # Find appropriate fee tier based on market cap
63
+ fee_tier = calculate_fee_tier(fee_config.fee_tiers, market_cap)
64
+ if fee_tier:
65
+ lp_fee_bps = fee_tier.lp_fee_rate_bps
66
+ protocol_fee_bps = fee_tier.protocol_fee_rate_bps
67
+ creator_fee_bps = fee_tier.creator_fee_rate_bps
68
+
69
+ # Special handling for pump pools
70
+ if mint and creator and is_pump_pool(mint, creator):
71
+ # Pump pools might have different fee structures
72
+ # This would be implemented based on specific pump.fun requirements
73
+ pass
74
+
75
+ # Calculate total fee rate
76
+ total_fee_bps = lp_fee_bps + protocol_fee_bps + creator_fee_bps
77
+
78
+ # Ensure total fees don't exceed 100%
79
+ if total_fee_bps > BASIS_POINTS:
80
+ raise ValueError(f"Total fees ({total_fee_bps} bps) exceed 100%")
81
+
82
+ return ComputeFeesResult(
83
+ lp_fee_bps=lp_fee_bps,
84
+ protocol_fee_bps=protocol_fee_bps,
85
+ creator_fee_bps=creator_fee_bps,
86
+ total_fee_bps=total_fee_bps
87
+ )
88
+
89
+
90
+ def get_fee_recipient(
91
+ global_config: GlobalConfig,
92
+ pool_creator: Optional[Pubkey] = None
93
+ ) -> Pubkey:
94
+ """
95
+ Get the fee recipient address
96
+
97
+ Args:
98
+ global_config: Global configuration
99
+ pool_creator: Pool creator (unused for now, but could be used for dynamic routing)
100
+
101
+ Returns:
102
+ Fee recipient address
103
+ """
104
+ return global_config.fee_recipient
105
+
106
+
107
+ def calculate_swap_fees(
108
+ amount_in: int,
109
+ fees_result: ComputeFeesResult
110
+ ) -> dict:
111
+ """
112
+ Calculate actual fee amounts for a swap
113
+
114
+ Args:
115
+ amount_in: Input amount
116
+ fees_result: Fee rates from compute_fees_bps
117
+
118
+ Returns:
119
+ Dictionary with fee amounts
120
+ """
121
+ from .utils import fee
122
+
123
+ lp_fee_amount = fee(amount_in, fees_result.lp_fee_bps)
124
+ protocol_fee_amount = fee(amount_in, fees_result.protocol_fee_bps)
125
+ creator_fee_amount = fee(amount_in, fees_result.creator_fee_bps)
126
+ total_fee_amount = lp_fee_amount + protocol_fee_amount + creator_fee_amount
127
+
128
+ return {
129
+ "lp_fee_amount": lp_fee_amount,
130
+ "protocol_fee_amount": protocol_fee_amount,
131
+ "creator_fee_amount": creator_fee_amount,
132
+ "total_fee_amount": total_fee_amount,
133
+ "amount_after_fees": amount_in - total_fee_amount
134
+ }
135
+
136
+
137
+ def validate_fee_rates(
138
+ lp_fee_bps: int,
139
+ protocol_fee_bps: int,
140
+ creator_fee_bps: int
141
+ ) -> None:
142
+ """
143
+ Validate that fee rates are reasonable
144
+
145
+ Args:
146
+ lp_fee_bps: LP fee rate in basis points
147
+ protocol_fee_bps: Protocol fee rate in basis points
148
+ creator_fee_bps: Creator fee rate in basis points
149
+
150
+ Raises:
151
+ ValueError: If fees are invalid
152
+ """
153
+ # Check individual fee rates
154
+ if lp_fee_bps < 0 or lp_fee_bps > BASIS_POINTS:
155
+ raise ValueError(f"Invalid LP fee rate: {lp_fee_bps} bps")
156
+
157
+ if protocol_fee_bps < 0 or protocol_fee_bps > BASIS_POINTS:
158
+ raise ValueError(f"Invalid protocol fee rate: {protocol_fee_bps} bps")
159
+
160
+ if creator_fee_bps < 0 or creator_fee_bps > BASIS_POINTS:
161
+ raise ValueError(f"Invalid creator fee rate: {creator_fee_bps} bps")
162
+
163
+ # Check total fees
164
+ total_fee_bps = lp_fee_bps + protocol_fee_bps + creator_fee_bps
165
+ if total_fee_bps > BASIS_POINTS:
166
+ raise ValueError(f"Total fees exceed 100%: {total_fee_bps} bps")
167
+
168
+ # Reasonable maximum (e.g., 10% total fees)
169
+ max_reasonable_fee = BASIS_POINTS // 10 # 10%
170
+ if total_fee_bps > max_reasonable_fee:
171
+ raise ValueError(f"Total fees too high: {total_fee_bps} bps (max reasonable: {max_reasonable_fee} bps)")
@@ -0,0 +1,328 @@
1
+ """
2
+ Liquidity operation implementations (deposit/withdraw) for the PumpSwap SDK
3
+ """
4
+
5
+ import math
6
+ from typing import Tuple
7
+ from ..types.sdk_types import (
8
+ DepositBaseResult, DepositQuoteResult, DepositLpTokenResult,
9
+ DepositBaseAndLpTokenFromQuoteResult, DepositQuoteAndLpTokenFromBaseResult,
10
+ WithdrawResult, WithdrawAutocompleteResult
11
+ )
12
+ from .utils import (
13
+ ceil_div, floor_div, apply_slippage,
14
+ calculate_lp_tokens_for_deposit, calculate_tokens_for_lp_withdrawal
15
+ )
16
+
17
+
18
+ def deposit_token0(
19
+ token0: int,
20
+ slippage: int,
21
+ token0_reserve: int,
22
+ token1_reserve: int,
23
+ total_lp_supply: int
24
+ ) -> DepositBaseResult:
25
+ """
26
+ Calculate deposit amounts when user provides token0 amount
27
+
28
+ Args:
29
+ token0: Amount of token0 to deposit
30
+ slippage: Slippage tolerance in basis points
31
+ token0_reserve: Current token0 reserve
32
+ token1_reserve: Current token1 reserve
33
+ total_lp_supply: Current total LP supply
34
+
35
+ Returns:
36
+ DepositBaseResult with calculated amounts
37
+ """
38
+ if token0 <= 0:
39
+ raise ValueError("Token0 amount must be positive")
40
+
41
+ if total_lp_supply == 0:
42
+ # Initial liquidity - special case
43
+ # For initial deposit, we can accept any ratio
44
+ # LP tokens = sqrt(token0 * token1) where token1 is provided
45
+ raise ValueError("Use deposit_lp_token for initial liquidity provision")
46
+
47
+ if token0_reserve == 0:
48
+ raise ValueError("Invalid token0 reserve")
49
+
50
+ # Calculate proportional token1 amount needed
51
+ # token1 = token0 * token1_reserve / token0_reserve
52
+ quote = ceil_div(token0 * token1_reserve, token0_reserve)
53
+
54
+ # Calculate LP tokens to be minted
55
+ # lp_tokens = token0 * total_lp_supply / token0_reserve
56
+ lp_token = floor_div(token0 * total_lp_supply, token0_reserve)
57
+
58
+ if lp_token == 0:
59
+ raise ValueError("Deposit amount too small")
60
+
61
+ # Apply slippage protection
62
+ max_base = apply_slippage(token0, slippage, is_maximum=True)
63
+ max_quote = apply_slippage(quote, slippage, is_maximum=True)
64
+
65
+ return DepositBaseResult(
66
+ quote=quote,
67
+ lp_token=lp_token,
68
+ max_base=max_base,
69
+ max_quote=max_quote
70
+ )
71
+
72
+
73
+ def deposit_quote_and_lp_token_from_base(
74
+ base: int,
75
+ slippage: int,
76
+ base_reserve: int,
77
+ quote_reserve: int,
78
+ total_lp_supply: int
79
+ ) -> DepositQuoteAndLpTokenFromBaseResult:
80
+ """
81
+ Calculate quote amount and LP tokens when user provides base amount
82
+
83
+ Args:
84
+ base: Base token amount to deposit
85
+ slippage: Slippage tolerance in basis points
86
+ base_reserve: Current base token reserve
87
+ quote_reserve: Current quote token reserve
88
+ total_lp_supply: Current total LP supply
89
+
90
+ Returns:
91
+ DepositQuoteAndLpTokenFromBaseResult
92
+ """
93
+ if base <= 0:
94
+ raise ValueError("Base amount must be positive")
95
+
96
+ if total_lp_supply == 0 or base_reserve == 0:
97
+ raise ValueError("Pool not initialized")
98
+
99
+ # Calculate proportional quote amount needed
100
+ quote = ceil_div(base * quote_reserve, base_reserve)
101
+
102
+ # Calculate LP tokens to be minted
103
+ lp_token = floor_div(base * total_lp_supply, base_reserve)
104
+
105
+ if lp_token == 0:
106
+ raise ValueError("Deposit amount too small")
107
+
108
+ return DepositQuoteAndLpTokenFromBaseResult(
109
+ quote=quote,
110
+ lp_token=lp_token
111
+ )
112
+
113
+
114
+ def deposit_base_and_lp_token_from_quote(
115
+ quote: int,
116
+ slippage: int,
117
+ base_reserve: int,
118
+ quote_reserve: int,
119
+ total_lp_supply: int
120
+ ) -> DepositBaseAndLpTokenFromQuoteResult:
121
+ """
122
+ Calculate base amount and LP tokens when user provides quote amount
123
+
124
+ Args:
125
+ quote: Quote token amount to deposit
126
+ slippage: Slippage tolerance in basis points
127
+ base_reserve: Current base token reserve
128
+ quote_reserve: Current quote token reserve
129
+ total_lp_supply: Current total LP supply
130
+
131
+ Returns:
132
+ DepositBaseAndLpTokenFromQuoteResult
133
+ """
134
+ if quote <= 0:
135
+ raise ValueError("Quote amount must be positive")
136
+
137
+ if total_lp_supply == 0 or quote_reserve == 0:
138
+ raise ValueError("Pool not initialized")
139
+
140
+ # Calculate proportional base amount needed
141
+ base = ceil_div(quote * base_reserve, quote_reserve)
142
+
143
+ # Calculate LP tokens to be minted
144
+ lp_token = floor_div(quote * total_lp_supply, quote_reserve)
145
+
146
+ if lp_token == 0:
147
+ raise ValueError("Deposit amount too small")
148
+
149
+ return DepositBaseAndLpTokenFromQuoteResult(
150
+ base=base,
151
+ lp_token=lp_token
152
+ )
153
+
154
+
155
+ def deposit_lp_token(
156
+ lp_token: int,
157
+ slippage: int,
158
+ base_reserve: int,
159
+ quote_reserve: int,
160
+ total_lp_supply: int
161
+ ) -> DepositLpTokenResult:
162
+ """
163
+ Calculate token amounts needed for a specific LP token amount
164
+
165
+ Args:
166
+ lp_token: Desired LP token amount
167
+ slippage: Slippage tolerance in basis points
168
+ base_reserve: Current base token reserve
169
+ quote_reserve: Current quote token reserve
170
+ total_lp_supply: Current total LP supply
171
+
172
+ Returns:
173
+ DepositLpTokenResult with maximum token amounts needed
174
+ """
175
+ if lp_token <= 0:
176
+ raise ValueError("LP token amount must be positive")
177
+
178
+ if total_lp_supply == 0:
179
+ # Initial liquidity case
180
+ # For initial deposit, we use the geometric mean
181
+ # lp_token = sqrt(base_amount * quote_amount)
182
+ # This means base_amount * quote_amount = lp_token^2
183
+ # We need to determine the ratio, but for now return equal amounts
184
+ estimated_base = int(math.sqrt(lp_token))
185
+ estimated_quote = int(math.sqrt(lp_token))
186
+
187
+ max_base = apply_slippage(estimated_base, slippage, is_maximum=True)
188
+ max_quote = apply_slippage(estimated_quote, slippage, is_maximum=True)
189
+
190
+ return DepositLpTokenResult(
191
+ max_base=max_base,
192
+ max_quote=max_quote
193
+ )
194
+
195
+ # Calculate required token amounts proportionally
196
+ # base_amount = lp_token * base_reserve / total_lp_supply
197
+ max_base_needed = ceil_div(lp_token * base_reserve, total_lp_supply)
198
+ max_quote_needed = ceil_div(lp_token * quote_reserve, total_lp_supply)
199
+
200
+ # Apply slippage protection
201
+ max_base = apply_slippage(max_base_needed, slippage, is_maximum=True)
202
+ max_quote = apply_slippage(max_quote_needed, slippage, is_maximum=True)
203
+
204
+ return DepositLpTokenResult(
205
+ max_base=max_base,
206
+ max_quote=max_quote
207
+ )
208
+
209
+
210
+ def withdraw(
211
+ lp_amount: int,
212
+ slippage: int,
213
+ base_reserve: int,
214
+ quote_reserve: int,
215
+ total_lp_supply: int
216
+ ) -> WithdrawResult:
217
+ """
218
+ Calculate token amounts received when withdrawing LP tokens
219
+
220
+ Args:
221
+ lp_amount: LP token amount to withdraw
222
+ slippage: Slippage tolerance in basis points
223
+ base_reserve: Current base token reserve
224
+ quote_reserve: Current quote token reserve
225
+ total_lp_supply: Current total LP supply
226
+
227
+ Returns:
228
+ WithdrawResult with token amounts and slippage protection
229
+ """
230
+ if lp_amount <= 0:
231
+ raise ValueError("LP amount must be positive")
232
+
233
+ if lp_amount > total_lp_supply:
234
+ raise ValueError("LP amount exceeds total supply")
235
+
236
+ if total_lp_supply == 0:
237
+ raise ValueError("No liquidity to withdraw")
238
+
239
+ # Calculate proportional token amounts
240
+ base, quote = calculate_tokens_for_lp_withdrawal(
241
+ lp_amount, base_reserve, quote_reserve, total_lp_supply
242
+ )
243
+
244
+ if base == 0 and quote == 0:
245
+ raise ValueError("Withdrawal amount too small")
246
+
247
+ # Apply slippage protection for minimum amounts
248
+ min_base = apply_slippage(base, slippage, is_maximum=False)
249
+ min_quote = apply_slippage(quote, slippage, is_maximum=False)
250
+
251
+ return WithdrawResult(
252
+ base=base,
253
+ quote=quote,
254
+ min_base=min_base,
255
+ min_quote=min_quote
256
+ )
257
+
258
+
259
+ def withdraw_autocomplete_base_and_quote_from_lp_token(
260
+ lp_amount: int,
261
+ slippage: int,
262
+ base_reserve: int,
263
+ quote_reserve: int,
264
+ total_lp_supply: int
265
+ ) -> WithdrawAutocompleteResult:
266
+ """
267
+ Simple autocomplete calculation for withdrawal amounts
268
+
269
+ Args:
270
+ lp_amount: LP token amount to withdraw
271
+ slippage: Slippage tolerance in basis points
272
+ base_reserve: Current base token reserve
273
+ quote_reserve: Current quote token reserve
274
+ total_lp_supply: Current total LP supply
275
+
276
+ Returns:
277
+ WithdrawAutocompleteResult with base and quote amounts
278
+ """
279
+ if lp_amount <= 0:
280
+ raise ValueError("LP amount must be positive")
281
+
282
+ if total_lp_supply == 0:
283
+ return WithdrawAutocompleteResult(base=0, quote=0)
284
+
285
+ # Calculate proportional amounts without slippage
286
+ base, quote = calculate_tokens_for_lp_withdrawal(
287
+ lp_amount, base_reserve, quote_reserve, total_lp_supply
288
+ )
289
+
290
+ return WithdrawAutocompleteResult(
291
+ base=base,
292
+ quote=quote
293
+ )
294
+
295
+
296
+ def validate_deposit_amounts(
297
+ base_amount: int,
298
+ quote_amount: int,
299
+ base_reserve: int,
300
+ quote_reserve: int,
301
+ tolerance_bps: int = 100 # 1% tolerance
302
+ ) -> bool:
303
+ """
304
+ Validate that deposit amounts maintain proper pool ratio
305
+
306
+ Args:
307
+ base_amount: Base token amount to deposit
308
+ quote_amount: Quote token amount to deposit
309
+ base_reserve: Current base token reserve
310
+ quote_reserve: Current quote token reserve
311
+ tolerance_bps: Allowed deviation in basis points
312
+
313
+ Returns:
314
+ True if amounts are within tolerance
315
+ """
316
+ if base_reserve == 0 or quote_reserve == 0:
317
+ # Initial deposit - any ratio is acceptable
318
+ return True
319
+
320
+ # Calculate expected ratio
321
+ expected_ratio = quote_reserve * 10000 // base_reserve # Scale up for precision
322
+ actual_ratio = quote_amount * 10000 // base_amount
323
+
324
+ # Check if within tolerance
325
+ deviation = abs(actual_ratio - expected_ratio)
326
+ max_deviation = expected_ratio * tolerance_bps // 10000
327
+
328
+ return deviation <= max_deviation