@openocean.finance/widget
Version:
Openocean Widget for cross-chain bridging and swapping. It will drive your multi-chain strategy and attract new users from everywhere.
190 lines (172 loc) • 5.1 kB
text/typescript
/**
* Solana Transaction Sending Service
*
* This module handles sending and confirming transactions on Solana blockchain
*/
import type {
BlockhashWithExpiryBlockHeight,
Connection,
} from '@solana/web3.js'
/**
* Delay execution helper
* @param ms Delay time in milliseconds
*/
const delay = (ms: number): Promise<void> =>
new Promise((resolve) => setTimeout(resolve, ms))
/**
* Transaction sending and confirmation parameters interface
*/
interface SolanaTransactionParams {
/** Solana RPC connection instance */
connection: Connection
/** Serialized transaction data */
serializedTransaction: Buffer
/** Blockhash with expiry block height */
blockhashWithExpiryBlockHeight?: BlockhashWithExpiryBlockHeight
/** Transaction process object */
process?: any
/** Route update callback function */
updateRouteHook?: (route: any) => void
}
/**
* Send raw transaction to Solana network
*
* @param connection Solana RPC connection instance
* @param serializedTransaction Serialized transaction data
* @param process Transaction process object
* @param updateRouteHook Route update callback function
* @returns Transaction hash
*/
async function sendSolanaTransaction(
connection: Connection,
serializedTransaction: Buffer,
process: any,
updateRouteHook?: (route: any) => void
): Promise<string> {
try {
// Send transaction to network
const txid = await connection.sendRawTransaction(serializedTransaction, {
skipPreflight: true,
})
// Update transaction status to pending
if (process && updateRouteHook) {
process.status = 'PENDING'
process.txHash = txid
process.message = 'Transaction pending'
updateRouteHook(process.route)
}
return txid
} catch (error) {
console.error('Failed to send transaction:', error)
if (process && updateRouteHook) {
process.status = 'FAILED'
process.message =
error instanceof Error ? error.message : 'Failed to send transaction'
updateRouteHook(process.route)
}
throw error
}
}
/**
* Confirm Solana transaction status
*
* @param connection Solana RPC connection instance
* @param txid Transaction hash
* @param process Transaction process object
* @param updateRouteHook Route update callback function
* @param maxRetries Maximum retry attempts
* @returns Whether transaction succeeded
*/
async function confirmSolanaTransaction(
connection: Connection,
txid: string,
process: any,
updateRouteHook?: (route: any) => void,
maxRetries = 30
): Promise<boolean> {
let retries = 0
while (retries < maxRetries) {
try {
// Get latest blockhash
const latestBlockhash = await connection.getLatestBlockhash()
// Confirm transaction status
const confirmation = await connection.confirmTransaction({
signature: txid,
blockhash: latestBlockhash.blockhash,
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
})
// Check if transaction failed
if (confirmation.value.err) {
if (process && updateRouteHook) {
process.status = 'FAILED'
process.message = 'Transaction failed on chain'
updateRouteHook(process.route)
}
return false
}
// Transaction confirmed successfully
if (process && updateRouteHook) {
process.status = 'DONE'
process.doneAt = Date.now()
process.message = 'Transaction confirmed'
updateRouteHook(process.route)
}
return true
} catch (error) {
console.warn('Confirmation attempt failed:', error)
await delay(2000) // Wait 2 seconds before retrying
retries++
}
}
// Exceeded maximum retry attempts
if (process && updateRouteHook) {
process.status = 'FAILED'
process.message = 'Transaction confirmation timeout'
updateRouteHook(process.route)
}
throw new Error('Transaction confirmation timeout')
}
/**
* Send and wait for Solana transaction confirmation
*
* @param params Transaction parameters
* @returns Transaction response and hash
*/
export async function sendAndConfirmSolanaTransaction({
connection,
serializedTransaction,
process,
updateRouteHook,
}: SolanaTransactionParams): Promise<any> {
try {
// Send transaction and get transaction hash
const txid = await sendSolanaTransaction(
connection,
serializedTransaction,
process,
updateRouteHook
)
// Wait for transaction confirmation
const success = await confirmSolanaTransaction(
connection,
txid,
process,
updateRouteHook
)
if (!success) {
throw new Error('Transaction failed during confirmation')
}
// Get transaction details
const response = await connection.getTransaction(txid, {
commitment: 'confirmed',
maxSupportedTransactionVersion: 0,
})
if (!response) {
throw new Error('Failed to fetch transaction details')
}
return { response, txid }
} catch (error) {
console.error('Transaction failed:', error)
throw error
}
}