From fe51fb55503f3496202f428d0dee646628bab7dd Mon Sep 17 00:00:00 2001 From: yasha-meursault Date: Thu, 6 Aug 2026 15:34:52 +0400 Subject: [PATCH 1/2] fix(solana): recover completed swaps from the closed user-lock path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Anchor closes the user-lock PDA on redeem/refund, so recoverClosedUserLock returned only a status with amount 0 and empty sender/recipient/token — no dst* fields — and useRecoverSwap threw "Destination network not found" for every completed or refunded Solana swap. The lock tx still carries the full UserLocked event, and findUserDataFromLogs already parses it, so return the lock terms from that same pass and spread them in. Status and timestamp still come from the closing tx. --- .../src/client/public/getUserLockDetails.ts | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/packages/blockchains/solana/src/client/public/getUserLockDetails.ts b/packages/blockchains/solana/src/client/public/getUserLockDetails.ts index 3a07e043..ff0a5ecd 100644 --- a/packages/blockchains/solana/src/client/public/getUserLockDetails.ts +++ b/packages/blockchains/solana/src/client/public/getUserLockDetails.ts @@ -33,7 +33,7 @@ export async function getUserLockDetails( const accountInfo = await connection.getAccountInfo(userLockPda) if (!accountInfo) { - return recoverClosedUserLock(connection, id, userLockPda, program) + return recoverClosedUserLock(connection, params, id, userLockPda, program) } try { const result = await (program.account as TypedProgramAccounts).userLock.fetch(userLockPda) @@ -56,17 +56,26 @@ export async function getUserLockDetails( // ── Private Helpers ───────────────────────────────────────────────── -async function recoverClosedUserLock(connection: Connection, id: string, pda: PublicKey, program: Program): Promise { +async function recoverClosedUserLock(connection: Connection, params: LockParams, id: string, pda: PublicKey, program: Program): Promise { const sigs = await connection.getSignaturesForAddress(pda, { limit: 1 }).catch(() => []) if (!sigs.length) return null const closedTx = await connection.getTransaction(sigs[0].signature, { commitment: 'confirmed', maxSupportedTransactionVersion: 0 }) for (const event of parseLogEvents(closedTx?.meta?.logMessages ?? [], program)) { const name = event.name.toLowerCase() if (name === 'userrefunded' || name === 'userredeemed') { + const { eventData, lockTerms } = params.txId + ? await findUserDataFromLogs(connection, params.txId, id, program) + : { eventData: {}, lockTerms: undefined } + + const amountInBaseUnits = lockTerms?.amountInBaseUnits + return { hashlock: `0x${id.replace('0x', '')}`, - amount: 0, timelock: 0, secret: 0n, + amount: amountInBaseUnits ? Number(formatUnits(amountInBaseUnits, params.decimals)) : 0, + timelock: 0, secret: 0n, sender: '', recipient: '', token: '', + ...lockTerms, + ...eventData, status: name === 'userrefunded' ? LockStatus.Refunded : LockStatus.Redeemed, blockTimestamp: closedTx?.blockTime ? closedTx.blockTime * 1000 : undefined, } as UserLockDetails @@ -93,8 +102,9 @@ async function findUserDataFromLogs( txId: string, id: string, program: Program -): Promise<{ eventData: Partial; blockTimestamp?: number }> { +): Promise<{ eventData: Partial; lockTerms?: Partial; blockTimestamp?: number }> { const eventData: Partial = {} + let lockTerms: Partial | undefined try { let tx = await connection.getTransaction(txId, { commitment: 'confirmed', @@ -154,7 +164,20 @@ async function findUserDataFromLogs( : decodeBytes(rewardRecipient) } - return { eventData, blockTimestamp } + const payoutCurve = new PublicKey(data.payout_curve ?? data.payoutCurve).toBase58() + const refundTo = data.refund_to ?? data.refundTo + + lockTerms = { + amountInBaseUnits: BigInt(data.amount.toString()), + timelock: Number(data.timelock), + sender: new PublicKey(data.sender).toBase58(), + recipient: new PublicKey(data.recipient).toBase58(), + token: new PublicKey(data.token_mint ?? data.tokenMint).toBase58(), + refundTo: refundTo ? new PublicKey(refundTo).toBase58() : undefined, + payoutCurve: payoutCurve === NATIVE_SOL_ADDRESS ? null : payoutCurve, + } + + return { eventData, lockTerms, blockTimestamp } } return { eventData, blockTimestamp } From 2608539118e88e2139008dd9390e923730b2603a Mon Sep 17 00:00:00 2001 From: yasha-meursault Date: Thu, 6 Aug 2026 16:48:44 +0400 Subject: [PATCH 2/2] small fix --- .../solana/src/client/public/getUserLockDetails.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/blockchains/solana/src/client/public/getUserLockDetails.ts b/packages/blockchains/solana/src/client/public/getUserLockDetails.ts index ff0a5ecd..2e824c5a 100644 --- a/packages/blockchains/solana/src/client/public/getUserLockDetails.ts +++ b/packages/blockchains/solana/src/client/public/getUserLockDetails.ts @@ -33,7 +33,7 @@ export async function getUserLockDetails( const accountInfo = await connection.getAccountInfo(userLockPda) if (!accountInfo) { - return recoverClosedUserLock(connection, params, id, userLockPda, program) + return recoverClosedUserLock(connection, userLockPda, program, id, params.decimals, params.txId) } try { const result = await (program.account as TypedProgramAccounts).userLock.fetch(userLockPda) @@ -56,22 +56,22 @@ export async function getUserLockDetails( // ── Private Helpers ───────────────────────────────────────────────── -async function recoverClosedUserLock(connection: Connection, params: LockParams, id: string, pda: PublicKey, program: Program): Promise { +async function recoverClosedUserLock(connection: Connection, pda: PublicKey, program: Program, id: string, decimals: number, txId?: string): Promise { const sigs = await connection.getSignaturesForAddress(pda, { limit: 1 }).catch(() => []) if (!sigs.length) return null const closedTx = await connection.getTransaction(sigs[0].signature, { commitment: 'confirmed', maxSupportedTransactionVersion: 0 }) for (const event of parseLogEvents(closedTx?.meta?.logMessages ?? [], program)) { const name = event.name.toLowerCase() if (name === 'userrefunded' || name === 'userredeemed') { - const { eventData, lockTerms } = params.txId - ? await findUserDataFromLogs(connection, params.txId, id, program) + const { eventData, lockTerms } = txId + ? await findUserDataFromLogs(connection, txId, id, program) : { eventData: {}, lockTerms: undefined } const amountInBaseUnits = lockTerms?.amountInBaseUnits return { hashlock: `0x${id.replace('0x', '')}`, - amount: amountInBaseUnits ? Number(formatUnits(amountInBaseUnits, params.decimals)) : 0, + amount: amountInBaseUnits ? Number(formatUnits(amountInBaseUnits, decimals)) : 0, timelock: 0, secret: 0n, sender: '', recipient: '', token: '', ...lockTerms,