@@ -236,7 +236,9 @@ export async function closePosition(
236236 } ) ,
237237 ) ;
238238
239- // Note: We'll extract actual fee amounts from balance changes after transaction
239+ // Use collectQuote for fee amounts (derived from on-chain position data)
240+ baseFeeAmountCollected = Number ( collectQuote . feeOwedA ) / Math . pow ( 10 , mintA . decimals ) ;
241+ quoteFeeAmountCollected = Number ( collectQuote . feeOwedB ) / Math . pow ( 10 , mintB . decimals ) ;
240242 }
241243
242244 // Step 4: Auto-unwrap WSOL to native SOL after receiving all tokens
@@ -281,52 +283,37 @@ export async function closePosition(
281283 } ) ,
282284 ) ;
283285
286+ // Calculate rent refund by querying position account balances BEFORE the TX.
287+ // These accounts will be closed by the transaction, so we must read them now.
288+ // This is more accurate than deriving from wallet SOL changes, which can be
289+ // skewed by ephemeral wSOL wrapper create/close cycles within the same TX.
290+ const LAMPORT_TO_SOL = 1e-9 ;
291+ const positionMintPubkey = position . getData ( ) . positionMint ;
292+ const positionTokenAccount = getAssociatedTokenAddressSync (
293+ positionMintPubkey ,
294+ client . getContext ( ) . wallet . publicKey ,
295+ undefined ,
296+ isToken2022 ? TOKEN_2022_PROGRAM_ID : undefined ,
297+ ) ;
298+ const [ positionMintBalance , positionDataBalance , positionAtaBalance ] = await Promise . all ( [
299+ solana . connection . getBalance ( positionMintPubkey ) ,
300+ solana . connection . getBalance ( positionPubkey ) ,
301+ solana . connection . getBalance ( positionTokenAccount ) ,
302+ ] ) ;
303+ const positionRentRefunded = ( positionMintBalance + positionDataBalance + positionAtaBalance ) * LAMPORT_TO_SOL ;
304+
305+ logger . info (
306+ `Position rent refund: mint=${ positionMintBalance } , data=${ positionDataBalance } , ata=${ positionAtaBalance } , total=${ positionRentRefunded } SOL` ,
307+ ) ;
308+
284309 // Build, simulate, and send transaction
285310 const txPayload = await builder . build ( ) ;
286311 await solana . simulateWithErrorHandling ( txPayload . transaction ) ;
287312 const { signature, fee } = await solana . sendAndConfirmTransaction ( txPayload . transaction , [ wallet ] ) ;
288313
289- // Extract actual amounts from balance changes (more accurate than quotes)
290- const tokenAAddress = whirlpool . getTokenAInfo ( ) . address . toString ( ) ;
291- const tokenBAddress = whirlpool . getTokenBInfo ( ) . address . toString ( ) ;
292- const tokenA = await solana . getToken ( tokenAAddress ) ;
293- const tokenB = await solana . getToken ( tokenBAddress ) ;
294-
295- const { balanceChanges } = await solana . extractBalanceChangesAndFee (
296- signature ,
297- client . getContext ( ) . wallet . publicKey . toString ( ) ,
298- [ tokenAAddress , tokenBAddress ] ,
299- ) ;
300-
301- // Total balance changes (positive values = received)
302- const totalBaseChange = Math . abs ( balanceChanges [ 0 ] ) ;
303- const totalQuoteChange = Math . abs ( balanceChanges [ 1 ] ) ;
304-
305- // If we removed liquidity, use the quote estimates as basis
306- // Otherwise, all balance change is from fees
307- if ( hasLiquidity ) {
308- // We have estimates from decreaseQuote, but actual amounts might differ slightly
309- // Use the estimates as reference, but ensure fees aren't negative
310- baseFeeAmountCollected = Math . max ( 0 , totalBaseChange - baseTokenAmountRemoved ) ;
311- quoteFeeAmountCollected = Math . max ( 0 , totalQuoteChange - quoteTokenAmountRemoved ) ;
312-
313- // If fees would be negative, it means the estimate was slightly high
314- // Adjust the liquidity removed to match actual total
315- if ( totalBaseChange < baseTokenAmountRemoved ) {
316- baseTokenAmountRemoved = totalBaseChange ;
317- baseFeeAmountCollected = 0 ;
318- }
319- if ( totalQuoteChange < quoteTokenAmountRemoved ) {
320- quoteTokenAmountRemoved = totalQuoteChange ;
321- quoteFeeAmountCollected = 0 ;
322- }
323- } else {
324- // No liquidity removed, all balance change is fees
325- baseFeeAmountCollected = totalBaseChange ;
326- quoteFeeAmountCollected = totalQuoteChange ;
327- }
328-
329- const positionRentRefunded = 0.00203928 ;
314+ // Token amounts are already set from quotes:
315+ // - baseTokenAmountRemoved / quoteTokenAmountRemoved from decreaseQuote (Step 2)
316+ // - baseFeeAmountCollected / quoteFeeAmountCollected from collectQuote (Step 3)
330317
331318 return {
332319 signature,
0 commit comments