Skip to Content
Solana Trackers Standalone

Solana Trackers Standalone

The Solana tracker of @tuwaio/pulsar-solana also works without the Pulsar store. Use it when you:

  • keep transactions in your own state (Redux, MobX, another Zustand store, a database);
  • track transactions on a server, where there is no localStorage and no wallet;
  • need a callback for each check instead of store updates.

Without the store nothing is persisted and nothing is resumed after a restart: you decide what to save and when to start tracking again. The tracker uses only @solana/kit RPC calls, so it runs in the browser and in Node.js.


Why solanaFetcher Instead of Manual Polling

SituationManual getSignatureStatuses pollingsolanaFetcher with initializePollingTracker
The RPC has not seen the signatureYou decide how long to wait.Keeps polling; fails one hour after localTimestamp if the signature never appears (for example, the blockhash expired).
Transient RPC errorsYou write the retry logic.Errors are retried on the next tick; tracking gives up after maxRetries consecutive errors.
Tracking resumed laterRecent statuses only, unless you pass searchTransactionHistory.Always searches the transaction history, so transactions that finished while you were away are found.
Transaction detailsA separate getTransaction call.Fetches fee, recentBlockhash and instructions once and adds them to every reported status.
Final statusYou compare commitment levels yourself.onSuccess at finalized, onFailure for an on-chain error; both stop polling.

Tracking a Signature: solanaFetcher

Pass solanaFetcher to initializePollingTracker from @tuwaio/pulsar-core. It starts polling in the background and returns immediately:

import { OrbitAdapter } from '@tuwaio/orbit-core'; import { initializePollingTracker } from '@tuwaio/pulsar-core'; import { solanaFetcher } from '@tuwaio/pulsar-solana'; export function trackSignature(signature: string) { initializePollingTracker({ tx: { adapter: OrbitAdapter.SOLANA, txKey: signature, chainId: 'solana:devnet', // used to pick the public endpoint when rpcUrl is not set rpcUrl: 'https://api.devnet.solana.com', // prefer your own RPC provider localTimestamp: Math.floor(Date.now() / 1000), // when the transaction was sent, in seconds pending: true, // polling starts only for pending transactions }, fetcher: solanaFetcher, pollingInterval: 2500, onIntervalTick: (status) => console.log(status.confirmationStatus, status.confirmations), onSuccess: (status) => console.log('Finalized in slot', status.slot, 'fee', status.fee), onFailure: (status) => console.error(status ? 'Failed on-chain' : 'Not finalized in time or too many RPC errors', status?.err), }); }

How each check works:

  1. getSignatureStatuses with searchTransactionHistory: true. If the signature is unknown, the fetcher reports nothing and waits for the next tick, until one hour after localTimestamp.
  2. getTransaction (commitment confirmed) for the fee, blockhash and instructions, only until it has returned them once for this tx object: the details are cached in memory for the tracking run, or taken from tx if it already has them. Until they are known, nothing is reported.
  3. onIntervalTick with the status; then onFailure with the status if it has an err, or onSuccess when it is finalized. A transaction that is not finalized one hour after localTimestamp calls onFailure.

RPC errors are thrown to initializePollingTracker, which retries on the next tick and calls onFailure() without arguments after maxRetries (default 10) consecutive errors.


Helpers

signAndSendSolanaTx

Builds a version 0 transaction from one or more instructions, with the signer as fee payer and the latest blockhash, and has the signer sign and send it. It returns the base58 signature, ready for solanaFetcher:

import type { Instruction, TransactionSendingSigner } from '@solana/kit'; import type { SolanaClient } from '@tuwaio/orbit-solana'; import { signAndSendSolanaTx } from '@tuwaio/pulsar-solana'; export async function send(client: SolanaClient, signer: TransactionSendingSigner, instructions: Instruction[]) { const signature = await signAndSendSolanaTx({ client, signer, instruction: instructions }); console.log('Sent', signature); return signature; }

It rejects with the RPC or signer error, for example when the user rejects the transaction in the wallet.

checkSolanaChain

Compares two cluster identifiers exactly and throws SolanaChainMismatchError when they differ. The Solana adapter uses it to compare desiredChainID with the cluster saved for the wallet connection after removing a solana: prefix from both, so devnet and solana:devnet match:

import { checkSolanaChain, SolanaChainMismatchError } from '@tuwaio/pulsar-solana'; export function isOnCluster(requiredCluster: string, walletCluster: string): boolean { try { checkSolanaChain(requiredCluster, walletCluster); return true; } catch (error) { if (error instanceof SolanaChainMismatchError) { console.warn(`Switch the wallet from ${error.currentChain} to ${error.requiredChain}.`); return false; } throw error; } }

The full list of exports is in the @tuwaio/pulsar-solana reference.

Last updated on