Using EVM Trackers Standalone
While the Pulsar suite offers a full stack solution with a UI (@tuwaio/nova-transactions) and a Zustand-based state store (@tuwaio/pulsar-core), its architecture remains modular and flexible. This means you can utilize the low-level trackers (evmTracker, gelatoFetcher, safeFetcher) from @tuwaio/pulsar-evm directly, without installing or configuring the complete state management store.
This flexibility is ideal if:
- You already have your own state management solution (Redux, MobX, Valtio, etc.) and want to integrate only the transaction tracking logic.
- You require tracking on the server-side, where a client-centric store isn’t necessary.
- You desire granular control over each stage of a transaction’s lifecycle for custom workflows.
Why Use evmTracker?
Some might ask, “Why should I use evmTracker when I can just call waitForTransactionReceipt from viem?” While waitForTransactionReceipt is effective, it only addresses part of the problem. evmTracker provides a more robust, comprehensive, and ready-to-use solution.
| Feature | waitForTransactionReceipt (viem) | evmTracker (Pulsar) |
|---|---|---|
| Handles RPC Lags | ❌ No. If called immediately after submission, the RPC node might not have indexed the transaction yet, causing errors. | ✅ Yes. Built-in retry mechanism to wait for the transaction to appear in the mempool, mitigating RPC delays. |
| Full Lifecycle Support | 🤷♂️ Limited. Mainly reacts once the transaction is confirmed or replaced. | ✅ Yes. Provides callbacks for each stage: initialization, details fetched, mined, replaced, failed, etc. |
| Fetches Full Tx Details | ❌ No. Doesn’t return complete info such as nonce, value, etc. | ✅ Yes. Calls getTransaction internally, passing all transaction details to callbacks. |
| Abstraction Level | Low. You must manage the tracking states manually. | High. Encapsulates the entire process into a single, convenient async function, simplifying implementation. |
In essence, evmTracker is a reliable wrapper around viem functions, addressing common edge cases and significantly reducing manual effort.
Trackers Overview
1. EVM Tracker
This is the primary tracker for monitoring standard transactions on EVM-compatible chains, identified via transaction hash.
How It Works
evmTracker initially fetches transaction details using getTransaction. If unavailable (due to RPC indexing delay), it retries several times. Once the details are available, it actively waits for the transaction receipt using waitForTransactionReceipt.
Example Usage
import { evmTracker } from '@tuwaio/pulsar-evm';
import { mainnet } from 'viem/chains';
async function trackMyTransaction(txHash) {
console.log(`Starting to track transaction: ${txHash}`);
await evmTracker({
chains: [mainnet], // Chains array for internal client creation
tx: {
txKey: txHash, // Transaction hash
chainId: 1, // Chain ID (e.g., 1 for Ethereum Mainnet)
},
onTxDetailsFetched: (txDetails) => {
console.log('Transaction details received:', txDetails);
// Update your UI/state with nonce, gas, etc.
},
onSuccess: (txDetails, receipt) => {
console.log('Transaction mined!', receipt);
if (receipt.status === 'success') {
// Update status as successful
} else {
// Update status as failed
}
},
onReplaced: (replacement) => {
console.log('Transaction was replaced:', replacement);
// Handle replacement logic
},
onFailure: (error) => {
console.error('Tracking failed:', error);
// Handle errors
},
});
}2. Gelato & Safe Fetchers
For polling-based tracking, especially for Gelato and Safe multisig transactions, we expose fetcher functions. You can integrate these directly with Pulsar’s initializePollingTracker or your custom polling setup.
Example for Handling Gelato & Safe Transactions
import { initializePollingTracker } from '@tuwaio/pulsar-core';
import { gelatoFetcher, safeFetcher } from '@tuwaio/pulsar-evm';
// Tracking a Gelato relay task
async function trackGelatoTask(taskId) {
await initializePollingTracker({
tx: { txKey: taskId },
fetcher: gelatoFetcher,
onSuccess: (status) => {
console.log('Gelato task succeeded:', status);
},
onFailure: (status) => {
console.error('Gelato task failed:', status);
},
});
}
// Tracking a Safe multisig transaction
async function trackSafeTx(safeTxHash, chainId, fromAddress) {
await initializePollingTracker({
tx: { txKey: safeTxHash, chainId, from: fromAddress },
fetcher: safeFetcher,
onSuccess: (status) => {
console.log('Safe transaction succeeded:', status);
},
onFailure: (status) => {
console.error('Safe transaction failed:', status);
},
onReplaced: (replacement) => {
console.warn('Transaction was replaced:', replacement);
},
});
}Helper Functions
Additionally, the package provides several utilities for managing transaction states and chain interactions:
checkTransactionsTracker
Determines which tracker is suitable based on a transaction key.
import { checkTransactionsTracker } from '@tuwaio/pulsar-evm';
const { tracker, txKey } = checkTransactionsTracker('0xabc...', 'injected');
// tracker -> 'ethereum' or relevant tracker type
// txKey -> same as input or derived keycheckChainForTx
Verifies if the user is connected to the correct network and prompts for a switch if necessary.
import { checkChainForTx } from '@tuwaio/pulsar-evm';
import { wagmiConfig } from './config';
async function ensureCorrectNetwork(chainId: number | string) {
try {
await checkChainForTx(chainId, wagmiConfig);
console.log('Network is correct, proceeding...');
// Proceed with your transaction or other logic here
} catch (error) {
console.error(error.message);
// Handle error, e.g., showing a message to the user about network mismatch
}
}Feel free to ask for further customization examples or clarifications on implementing specific trackers or utilities!