ChainPortalChainPortal Docs
SDK Reference
SDK Reference

State Stores

Zustand stores in @chainportal/sdk -- transaction history and multi-chain wallet state, with selector hooks.

State Stores

The SDK ships two Zustand stores. Import them from the stores sub-path (or the package root):

import {
  useTransactionStore,
  useMultiChainStore,
  usePendingTransactions,
  useActiveChainId,
} from '@chainportal/sdk/stores';

Writing your own derived selector? Under zustand v5's strict useSyncExternalStore, a selector that returns a new reference every call (e.g. state.transactions.filter(...)) makes React re-render on every commit and throws "Maximum update depth exceeded" (React #185). Wrap derived selectors in useShallow so an unchanged result keeps a stable reference:

import { useShallow } from 'zustand/react/shallow';
 
const pending = useTransactionStore(
  useShallow((s) => s.transactions.filter((t) => t.status === 'pending'))
);

The built-in selector hooks below already do this for you.

Transaction store

useTransactionStore persists transaction history to IndexedDB (key chainportal-transactions), capped at the 1000 most recent entries. State is shared across tabs on the same origin.

State

FieldTypeDescription
transactionsTransaction[]All recorded transactions, newest first
isLoadingbooleanLoading flag for async hydration
errorError | nullLast error, if any

The Transaction shape

FieldTypeDescription
idstringGenerated unique id
hashstringOn-chain transaction hash
typeTransactionTypetoken_create | nft_create | nft_collection_create | nft_mint | token_transfer | nft_transfer | token_burn | nft_burn
chainTypeChainTypeevm | solana | cosmos | aptos | sui | near
chainIdnumber | stringChain ID
chainNamestringDisplay name of the chain
namestringAsset name
symbol?stringAsset symbol
contractAddress?stringDeployed contract address
creatorAddressstringAddress that created the transaction
timestampnumberCreation time (ms epoch)
statusTransactionStatuspending | confirming | success | failed
explorerUrl?stringBlock explorer URL
metadata?Record<string, unknown>Arbitrary extra data

Actions

Read these off the store hook (useTransactionStore((s) => s.addTransaction)):

ActionSignatureDescription
addTransaction(tx: Omit<Transaction,'id'|'timestamp'>) => stringAdd a tx (returns the new id)
updateTransaction(id, updates: Partial<Transaction>) => voidUpdate by id
updateTransactionByHash(hash, updates) => voidUpdate by hash
removeTransaction(id) => voidRemove by id
clearTransactions() => voidClear all history
getTransactionById(id) => Transaction | undefinedLookup by id
getTransactionByHash(hash) => Transaction | undefinedLookup by hash
getTransactionsByCreator(address) => Transaction[]Filter by creator
getFilteredTransactions(filter: TransactionFilter) => Transaction[]Filter by type/chain/status/creator
getPendingTransactions() => Transaction[]Pending + confirming
setLoading / setError(value) => voidSet loading / error flags

Derived selector hooks

These return stable references (useShallow-wrapped) and are safe to use directly:

HookReturns
useTransactions()All transactions
usePendingTransactions()Transactions that are pending or confirming
useTransactionsByType(type)Transactions of a given TransactionType
useTransactionsByChain(chainId)Transactions on a given chain
useRecentTransactions(limit = 10)The most recent limit transactions
import { usePendingTransactions } from '@chainportal/sdk/stores';
 
function PendingBadge() {
  const pending = usePendingTransactions();
  return pending.length ? <span>{pending.length} pending</span> : null;
}

Multi-chain store

useMultiChainStore tracks the active ecosystem, the active chain per ecosystem, and per-ecosystem wallet connections. It persists activeEcosystem + activeChainIds to localStorage (chainportal-multichain-storage) and uses skipHydration (hydrate on the client to avoid SSR mismatch).

State

FieldTypeDescription
activeEcosystemChainEcosystemCurrently active ecosystem
activeChainIdsRecord<ChainEcosystem, number | string | null>Active chain per ecosystem
connectionsRecord<ChainEcosystem, WalletConnection | null>Connection per ecosystem
isWalletModalOpenbooleanWallet connect modal open state
isChainSelectorOpenbooleanChain selector dropdown open state

Actions

ActionSignature
setActiveEcosystem(ecosystem) => void
setActiveChain(ecosystem, chainId) => void
setConnection(ecosystem, connection | null) => void
disconnect / disconnectAll(ecosystem) => void / () => void
getActiveConnection() => WalletConnection | null
getActiveChainId() => number | string | null
setWalletModalOpen / setChainSelectorOpen(open: boolean) => void

Selector hooks

HookReturns
useActiveEcosystem()The active ChainEcosystem
useActiveChainId()The active chain ID for the active ecosystem
useActiveConnection()The active WalletConnection, or null
useEVMConnection()The EVM WalletConnection, or null
useSolanaConnection()The Solana WalletConnection, or null
import { useActiveEcosystem, useMultiChainStore } from '@chainportal/sdk/stores';
 
function EcosystemSwitcher() {
  const ecosystem = useActiveEcosystem();
  const setEcosystem = useMultiChainStore((s) => s.setActiveEcosystem);
  return <button onClick={() => setEcosystem('solana')}>Active: {ecosystem}</button>;
}

See also

  • Types ReferenceWalletConnection, TransactionResult, and more
  • Hooks — higher-level hooks that write to these stores

On this page

Edit on GitHub