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 key
checkChainForTx
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!