diff --git a/app/src/hooks/useTx.ts b/app/src/hooks/useTx.ts new file mode 100644 index 0000000..9080460 --- /dev/null +++ b/app/src/hooks/useTx.ts @@ -0,0 +1,52 @@ +import { + pipe, + createTransactionMessage, + setTransactionMessageFeePayerSigner, + setTransactionMessageLifetimeUsingBlockhash, + appendTransactionMessageInstructions, + signTransactionMessageWithSigners, + sendAndConfirmTransactionFactory, + assertIsTransactionWithBlockhashLifetime, +} from '@solana/kit' +import { useKitTransactionSigner, useSolanaClient } from '@solana/connector/react' +import { useQueryClient } from '@tanstack/react-query' +import { useToast } from '@/components/ui/Toast' + +type AnyInstruction = Parameters[0][number] + +export function useTx() { + const { signer } = useKitTransactionSigner() + const { client } = useSolanaClient() + const queryClient = useQueryClient() + const toast = useToast() + + return async ( + instructions: AnyInstruction[], + invalidateKeys: string[][], + label: string, + ): Promise => { + if (!signer || !client) throw new Error('Wallet not connected') + const { rpc, rpcSubscriptions } = client + + const { value: latestBlockhash } = await rpc.getLatestBlockhash().send() + + const txMsg = pipe( + createTransactionMessage({ version: 0 }), + (tx) => setTransactionMessageFeePayerSigner(signer, tx), + (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx), + (tx) => appendTransactionMessageInstructions(instructions, tx), + ) + + const signed = await signTransactionMessageWithSigners(txMsg) + assertIsTransactionWithBlockhashLifetime(signed) + await sendAndConfirmTransactionFactory({ rpc: rpc as never, rpcSubscriptions: rpcSubscriptions as never })( + signed as never, + { commitment: 'confirmed' }, + ) + + toast(`Tx confirmed — ${label}`) + for (const key of invalidateKeys) { + queryClient.invalidateQueries({ queryKey: key }) + } + } +}