@@ -131,22 +131,32 @@ async def execute(self, token_info: TokenInfo) -> TradeResult:
131131 if success :
132132 logger .info (f"Buy transaction confirmed: { tx_signature } " )
133133
134- # Fetch actual token amount from transaction to handle slippage
135- actual_token_balance = await self .client .get_transaction_token_balance (
136- str (tx_signature ), self .wallet .pubkey , token_info .mint
134+ # Fetch actual tokens and SOL spent from transaction
135+ # Uses preBalances/postBalances to get exact amounts
136+ sol_destination = self ._get_sol_destination (
137+ token_info , address_provider
138+ )
139+ tokens_raw , sol_spent = await self .client .get_buy_transaction_details (
140+ str (tx_signature ), token_info .mint , sol_destination
137141 )
138142
139- if actual_token_balance is not None :
140- actual_amount = actual_token_balance / 10 ** TOKEN_DECIMALS
143+ if tokens_raw is not None and sol_spent is not None :
144+ actual_amount = tokens_raw / 10 ** TOKEN_DECIMALS
145+ actual_price = (sol_spent / LAMPORTS_PER_SOL ) / actual_amount
141146 logger .info (
142147 f"Actual tokens received: { actual_amount :.6f} "
143148 f"(expected: { token_amount :.6f} )"
144149 )
150+ logger .info (
151+ f"Actual SOL spent: { sol_spent / LAMPORTS_PER_SOL :.10f} SOL"
152+ )
153+ logger .info (f"Actual price: { actual_price :.10f} SOL/token" )
145154 token_amount = actual_amount
155+ token_price_sol = actual_price
146156 else :
147- logger . warning (
148- "Could not fetch actual token balance from tx , "
149- f"using estimated: { token_amount :.6f } "
157+ raise ValueError (
158+ f"Failed to parse transaction details: tokens= { tokens_raw } , "
159+ f"sol_spent= { sol_spent } "
150160 )
151161
152162 return TradeResult (
@@ -184,6 +194,42 @@ def _get_pool_address(
184194 # Fallback to deriving the address using platform provider
185195 return address_provider .derive_pool_address (token_info .mint )
186196
197+ def _get_sol_destination (
198+ self , token_info : TokenInfo , address_provider : AddressProvider
199+ ) -> Pubkey :
200+ """Get the address where SOL is sent during a buy transaction.
201+
202+ For pump.fun: SOL goes to the bonding curve
203+ For letsbonk: SOL goes to the quote_vault (WSOL vault)
204+
205+ Args:
206+ token_info: Token information
207+ address_provider: Platform-specific address provider
208+
209+ Returns:
210+ Address where SOL is transferred during buy
211+
212+ Raises:
213+ NotImplementedError: If platform SOL destination is not implemented
214+ """
215+ if token_info .platform == Platform .PUMP_FUN :
216+ # For pump.fun, SOL goes directly to bonding curve
217+ if hasattr (token_info , "bonding_curve" ) and token_info .bonding_curve :
218+ return token_info .bonding_curve
219+ return address_provider .derive_pool_address (token_info .mint )
220+ elif token_info .platform == Platform .LETS_BONK :
221+ # For letsbonk, SOL goes to quote_vault (WSOL vault)
222+ if hasattr (token_info , "quote_vault" ) and token_info .quote_vault :
223+ return token_info .quote_vault
224+ # Derive quote_vault if not available
225+ return address_provider .derive_quote_vault (token_info .mint )
226+
227+ raise NotImplementedError (
228+ f"SOL destination not implemented for platform { token_info .platform .value } . "
229+ f"Add platform-specific logic to _get_sol_destination() to specify where "
230+ f"SOL is transferred during buy transactions for this platform."
231+ )
232+
187233 def _get_cu_override (self , operation : str , platform : Platform ) -> int | None :
188234 """Get compute unit override from configuration.
189235
@@ -246,7 +292,7 @@ async def execute(
246292 )
247293 if token_price is None or token_price <= 0 :
248294 raise ValueError (
249- "token_price is required for sell operation. "
295+ "token_price is required for sell operation and must be positive . "
250296 "Pass the price from buy result to avoid RPC delays."
251297 )
252298
@@ -274,20 +320,19 @@ async def execute(
274320 error_message = "No tokens to sell" ,
275321 )
276322
277- # Calculate expected SOL output
323+ # Calculate expected SOL output with slippage protection
278324 expected_sol_output = token_balance_decimal * token_price_sol
279-
280- # Calculate minimum SOL output with slippage protection
281- min_sol_output = int (
282- (expected_sol_output * (1 - self .slippage )) * LAMPORTS_PER_SOL
325+ min_sol_output = max (
326+ 1 ,
327+ int ((expected_sol_output * (1 - self .slippage )) * LAMPORTS_PER_SOL ),
283328 )
284-
285329 logger .info (
286330 f"Selling { token_balance_decimal } tokens on { token_info .platform .value } "
287331 )
288- logger .info (f"Expected SOL output: { expected_sol_output :.8f } SOL" )
332+ logger .info (f"Expected SOL output: { expected_sol_output :.10f } SOL" )
289333 logger .info (
290- f"Minimum SOL output (with { self .slippage * 100 :.1f} % slippage): { min_sol_output / LAMPORTS_PER_SOL :.8f} SOL"
334+ f"Minimum SOL output (with { self .slippage * 100 :.1f} % slippage): "
335+ f"{ min_sol_output / LAMPORTS_PER_SOL :.10f} SOL ({ min_sol_output } lamports)"
291336 )
292337
293338 # Build sell instructions using platform-specific builder
0 commit comments