Pulsar is the transaction tracking project of TUWA Stage 2: a headless, framework-agnostic engine that follows EVM and Solana transactions from the wallet prompt to their final status. It keeps transactions in a store outside your components, persists them to localStorage, resumes tracking after a page reload and detects EVM speed-ups and cancels made in the wallet, with no UI components and no required backend.
Pulsar is built only on modern Web3 libraries: viem and @wagmi/core for EVM, @solana/kit and the Wallet Standard for Solana, on top of Orbit Utils. It does not use ethers.js, web3.js, @solana/web3.js or gill.
π Documentation: pulsar.docs.tuwa.io
TUWA is built in stages. Pulsar sits in Stage 2 (State & Connection) next to Satellite Connect, above SIWX and Orbit Utils (Stage 1) and below Quasar (Stage 3) and Nova UI Kit (Stage 4). Nova Transactions renders Pulsar's state, and Quasar can index and sync its transactions across devices; both are optional.
Inside the monorepo, packages are split into two layers:
@tuwaio/pulsar-core: the transaction store (Zustand withpersist), the adapter contract, metadata validation, remote sync hooks, selectors, the "transaction finished" event (onTxFinished,subscribeTxFinished) and building blocks for trackers. Peer dependencies:@tuwaio/orbit-core,zustand,immer,dayjs.
@tuwaio/pulsar-evm: the EVM adapter and trackers for standard transactions, ERC-4337 UserOperations and Safe multisig transactions, plus speed-up and cancel actions;@tuwaio/pulsar-evm/safeis the Safe tracker alone, without wagmi. Peer dependencies:viem, and for the main entry@tuwaio/orbit-evmand@wagmi/core.@tuwaio/pulsar-solana: the Solana adapter, the signature tracker andsignAndSendSolanaTx. Peer dependencies:@tuwaio/orbit-solana,@solana/kit(plus the@wallet-standardpeers of@tuwaio/orbit-solana).@tuwaio/pulsar-react: theuseInitializeTransactionsPoolhook that resumes tracking after a reload and theuseTxFinishedhook that reacts to every finished transaction. Peer dependency:react.
The EVM and Solana packages have @tuwaio/pulsar-core as a peer dependency.
pulsar-core/
βββ apps/
β βββ docs/ # pulsar.docs.tuwa.io (Next.js 16 + Nextra 4)
β βββ src/content/ # Hand-written MDX pages + generated `packages/` reference
β βββ typedoc/ # TypeDoc plugins, Packages overview page and sidebar template
βββ packages/
β βββ pulsar-core/ # L3: transaction store, adapter contract, validation, selectors
β βββ pulsar-evm/ # L4: EVM adapter and trackers (viem, @wagmi/core)
β βββ pulsar-solana/ # L4: Solana adapter and tracker (@solana/kit)
β βββ pulsar-react/ # L4: React hook
βββ typedoc.json # Reference generation (TypeDoc "packages" strategy)
Install the L3 core and the L4 packages your app needs:
# L3 Core
pnpm add @tuwaio/pulsar-core @tuwaio/orbit-core zustand immer dayjs
# L4 EVM
pnpm add @tuwaio/pulsar-evm @tuwaio/orbit-evm @wagmi/core viem
# L4 Solana
pnpm add @tuwaio/pulsar-solana @tuwaio/orbit-solana @solana/kit @wallet-standard/app @wallet-standard/ui-core @wallet-standard/ui-registry
# L4 React
pnpm add @tuwaio/pulsar-react reactA React app that tracks EVM and Solana transactions in one store. The store is created once, the initializer resumes tracking after a reload, and components read the pool with a selector:
// store/pulsar.ts
'use client';
import { createBoundedUseStore, createPulsarStore, type Transaction } from '@tuwaio/pulsar-core';
import { pulsarEvmAdapter } from '@tuwaio/pulsar-evm';
import { useInitializeTransactionsPool } from '@tuwaio/pulsar-react';
import { pulsarSolanaAdapter } from '@tuwaio/pulsar-solana';
import { type Config } from '@wagmi/core';
import { mainnet } from 'viem/chains';
declare const wagmiConfig: Config; // your wagmi config
export const pulsarStore = createPulsarStore<Transaction>({
name: 'pulsar-transactions',
adapter: [
pulsarEvmAdapter(wagmiConfig, [mainnet]),
pulsarSolanaAdapter({ rpcUrls: { mainnet: 'https://api.mainnet-beta.solana.com' } }),
],
});
export const usePulsarStore = createBoundedUseStore(pulsarStore);
// Render once, next to your providers.
export function PulsarInitializer() {
useInitializeTransactionsPool({ initializeTransactionsPool: pulsarStore.getState().initializeTransactionsPool });
return null;
}
export function PendingBadge() {
const pendingCount = usePulsarStore(
(state) => Object.values(state.transactionsPool).filter((tx) => tx.pending).length,
);
return pendingCount > 0 ? <span>{pendingCount} pending</span> : null;
}Send transactions with usePulsarStore((state) => state.executeTxAction): see the React transaction tracking guide for the complete EVM and Solana flows.
pnpm install # installs dependencies and builds all packages
pnpm build # builds packages with tsup (ESM, CJS, types)
pnpm test # runs vitest in every package
pnpm lint # runs ESLint
pnpm docs:gen # regenerates the Packages reference in apps/docs
pnpm --filter @tuwaio/pulsar-core-docs dev # runs the docs site locallyThe Packages reference is generated from each package's src/index.ts, JSDoc and README, and is regenerated by the pre-commit hook. Source links point to main, so a regeneration only changes the pages whose source actually changed. The tests of the L4 packages use the built @tuwaio/pulsar-core: run pnpm build after changing it.
Please review our ecosystem Contribution Guidelines.
Licensed under the Apache-2.0 License. See the LICENSE file for details.
