-
-
Notifications
You must be signed in to change notification settings - Fork 282
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(blockchain-link): fix Solana transaction confirmation
Combine `signatureSubscribe` and `getSignatureStatus` when confirming transactions. This resolves the issue with Quicknode not sending a signature update event. Transaction will confirm if either a signature update event is received or if the periodic `getSignatureStatus` confirms the transaction as `finalized`. (cherry picked from commit 38cd7cc)
- Loading branch information
1 parent
a0fd548
commit 3a726b7
Showing
2 changed files
with
95 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
86 changes: 86 additions & 0 deletions
86
packages/blockchain-link/src/workers/solana/transactionConfirmation.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import { Connection } from '@solana/web3.js'; | ||
|
||
const COMMITMENT = 'finalized'; | ||
|
||
const tryConfirmBySignatureStatus = async ( | ||
connection: Connection, | ||
signature: string, | ||
lastValidBlockHeight: number, | ||
abortSignal: AbortSignal, | ||
) => { | ||
const getCurrentBlockHeight = async () => { | ||
try { | ||
return await connection.getBlockHeight('finalized'); | ||
} catch (_) { | ||
return -1; | ||
} | ||
}; | ||
|
||
let currentBlockHeight = await getCurrentBlockHeight(); | ||
while (currentBlockHeight <= lastValidBlockHeight) { | ||
const signatureStatus = await connection.getSignatureStatus(signature); | ||
if ( | ||
signatureStatus.value != null && | ||
signatureStatus.value.confirmationStatus === COMMITMENT | ||
) { | ||
return signature; | ||
} | ||
|
||
await new Promise(resolve => setTimeout(resolve, 5000)); | ||
if (abortSignal.aborted) { | ||
return signature; | ||
} | ||
currentBlockHeight = await getCurrentBlockHeight(); | ||
} | ||
|
||
throw new Error( | ||
`TransactionExpiredBlockheightExceededError: Signature ${signature} has expired: block height exceeded.`, | ||
); | ||
}; | ||
|
||
const tryConfirmBySignatureSubscription = (connection: Connection, signature: string) => { | ||
let subscriptionId: number | undefined; | ||
const confirmationPromise = new Promise<string>((resolve, reject) => { | ||
subscriptionId = connection.onSignature( | ||
signature, | ||
result => { | ||
if (result.err != null) { | ||
reject(result.err); | ||
} | ||
resolve(signature); | ||
}, | ||
COMMITMENT, | ||
); | ||
}); | ||
|
||
return { subscriptionId, confirmationPromise }; | ||
}; | ||
|
||
export const confirmTransaction = async ( | ||
api: Connection, | ||
signature: string, | ||
lastValidBlockHeight: number, | ||
) => { | ||
const { subscriptionId, confirmationPromise: signatureSubscriptionConfirmationPromise } = | ||
tryConfirmBySignatureSubscription(api, signature); | ||
|
||
const abortController = new AbortController(); | ||
const signatureStatusConfirmationPromise = tryConfirmBySignatureStatus( | ||
api, | ||
signature, | ||
lastValidBlockHeight, | ||
abortController.signal, | ||
); | ||
|
||
await Promise.race([ | ||
signatureSubscriptionConfirmationPromise, | ||
signatureStatusConfirmationPromise, | ||
]); | ||
|
||
abortController.abort(); | ||
if (subscriptionId != null) { | ||
api.removeSignatureListener(subscriptionId); | ||
} | ||
|
||
return signature; | ||
}; |