Skip to Content
API Reference@txfence/react

@txfence/react

React hooks for building frontends on top of txfence agents. Chain-agnostic — no dependency on @txfence/evm or viem; inject your own adapters.

Installation

npm install @txfence/react @txfence/core @txfence/evm

useAgent

Creates a stable agent instance memoized for the component lifetime.

The agent is memoized with an empty dependency array — config changes after mount do not propagate. If your config, policy, adapters, or rpcUrls need to change at runtime, pass a key prop to the component and let React remount it.

type UseAgentOptions = { config: AgentConfig adapters: AdapterMap rpcUrls: Partial<Record<ChainId, string>> executor?: ( action: Action, chainId: ChainId, rpcUrl: string, evaluation: PolicyEvaluation, simulation: SimulationResult, ) => Promise<SuccessReceipt> capLockProvider?: CapLockProvider metadataVerifier?: MetadataVerifier } function useAgent(options: UseAgentOptions): Agent
import { useAgent } from '@txfence/react' import { simulateEvmAction, executeEvmAction, privateKeySigner } from '@txfence/evm' const signer = privateKeySigner(import.meta.env.VITE_PRIVATE_KEY) function MyComponent() { const agent = useAgent({ config: { chains: ['ethereum'], policies: policy, signer, }, adapters: { ethereum: { simulate: simulateEvmAction } }, rpcUrls: { ethereum: 'https://ethereum.publicnode.com' }, executor: (action, chainId, rpcUrl, evaluation, simulation) => executeEvmAction(action, chainId, rpcUrl, signer, evaluation, simulation), }) // use agent.submit(), agent.dryRun(), agent.executeIntent() }

useSubmit

Wraps the full pipeline with loading, result, and error state.

function useSubmit(agent: Agent): { submit: (action: Action, policy: Policy) => Promise<void> reset: () => void result: ExecutionResult | null loading: boolean error: string | null }
import { useAgent, useSubmit } from '@txfence/react' function TransferButton() { const agent = useAgent({ config, adapters, rpcUrls, executor }) const { result, loading, submit } = useSubmit(agent) return ( <button onClick={() => submit(action, policy)} disabled={loading}> {loading ? 'submitting...' : 'send'} </button> ) }

result.status is one of: success, policy_rejected, simulation_failed, approval_timeout, execution_failed, simulation_stale.

useSimulate

Wraps adapter simulation with loading, result, and error state. Takes adapters and rpcUrls directly — does not take an Agent, since simulation does not need the full pipeline.

function useSimulate( adapters: AdapterMap, rpcUrls: Partial<Record<ChainId, string>>, ): { simulate: (action: Action, rpcUrl?: string) => Promise<void> // rpcUrl optional — overrides rpcUrls[action.chain] reset: () => void result: SimulationResult | null loading: boolean error: string | null // error messages are strings, not Error instances }
function SimulationPreview({ action }: { action: Action }) { const { simulate, result, loading } = useSimulate( { ethereum: { simulate: simulateEvmAction } }, { ethereum: 'https://ethereum.publicnode.com' }, ) useEffect(() => { simulate(action) }, [action]) if (loading) return <span>simulating...</span> if (result?.wouldRevert) return <span>would revert: {result.revertReason}</span> return <span>gas estimate: {result?.gasEstimate?.toLocaleString()}</span> }

useReceipt

Fetches a transaction receipt using a caller-supplied chain-specific fetcher. The hook runs once when all four arguments are non-null and supports cancellation if the inputs change while a fetch is in flight.

function useReceipt( txHash: string | null, chain: ChainId | null, rpcUrl: string | null, fetcher: ((txHash: string, chain: ChainId, rpcUrl: string) => Promise<SuccessReceipt>) | null, ): { receipt: SuccessReceipt | null loading: boolean error: string | null }

The fetcher receives (txHash, chain, rpcUrl) — keep it chain-agnostic so the same hook works for EVM, Solana, and Cosmos receipts.

function ReceiptStatus({ txHash, chain, rpcUrl }: { txHash: string chain: ChainId rpcUrl: string }) { const { receipt, loading } = useReceipt(txHash, chain, rpcUrl, fetchEvmReceipt) if (loading) return <span>waiting for confirmation...</span> if (receipt) return <span>confirmed in block {receipt.confirmedAtBlock}</span> return null }

useDryRun

Wraps agent.dryRun() with loading and result state.

function useDryRun(agent: Agent): { dryRun: (action: Action, policy: Policy) => Promise<void> reset: () => void result: DryRunResult | null loading: boolean error: string | null }
function PreflightCheck({ action, policy }: { action: Action; policy: Policy }) { const agent = useAgent({ config, adapters, rpcUrls, executor }) const { dryRun, result, loading } = useDryRun(agent) useEffect(() => { dryRun(action, policy) }, [action]) if (loading) return <span>checking...</span> if (!result) return null if (result.wouldProceed) return <span>✓ ready to submit</span> return <span>blocked: {result.blockers.map(b => b.kind).join(', ')}</span> }

useIntentSubmit

Wraps agent.executeIntent() with loading, result, and error state.

function useIntentSubmit(agent: Agent): { executeIntent: (intent: Intent) => Promise<void> reset: () => void result: IntentExecutionResult | null loading: boolean error: string | null }
function IntentButton({ intent }: { intent: Intent }) { const agent = useAgent({ config, adapters, rpcUrls, executor }) const { executeIntent, result, loading } = useIntentSubmit(agent) return ( <button onClick={() => executeIntent(intent)} disabled={loading}> {loading ? 'executing...' : 'execute intent'} </button> ) }

useAgentHealth

Polls agent.health() at a configurable interval.

function useAgentHealth( agent: Agent, options?: { pollIntervalMs?: number } ): AgentHealth
function HealthIndicator() { const agent = useAgent({ config, adapters, rpcUrls, executor }) const health = useAgentHealth(agent, { pollIntervalMs: 5000 }) return ( <span> {health.status} · {health.inFlight} in-flight · uptime {Math.floor(health.uptime / 1000)}s </span> ) }

AgentHealth fields: status ('healthy' | 'shutting_down'), inFlight, uptime.

useAgentHealth is useful for Kubernetes readiness UI — show a degraded state in your frontend when the agent is draining.

Last updated on