# txKit - Complete API Reference
> Safe bridge between AI agents and Web3 transactions - open protocol (`@txkit/tx-protocol`) + reference implementation (React components + hooks). _OWS signs. txKit decides what's safe to sign._
txKit is two things:
1. **`@txkit/tx-protocol`** - open MIT spec for `PreparedEnvelope` (the shape between AI / MCP producers and wallet / signer / preview consumers). Zero React or wagmi deps; runtime validation via Zod.
2. **`@txkit/react` + `@txkit/core` + `@txkit/themes`** - React components with built-in Web3 logic. Drop in a component, get a production-ready UI with wallet connection, token balances, and multi-step transaction handling. Works standalone or alongside RainbowKit, AppKit, ConnectKit, or any wagmi-based setup.
Composes with: OWS (key custody + signing), x402 (HTTP payments for agents, Linux Foundation since 2 Apr 2026), MCP servers (AI agent tool-use), Etherspot TransactionKit and other AA hook libraries (UserOp builders).
- Docs: https://txkit.dev
- GitHub: https://github.com/txkit/mono
- npm: https://www.npmjs.com/package/@txkit/react
- Version: 0.1.0 (`@txkit/tx-protocol@0.1.0-alpha.1` shipped 24 Apr 2026 on `alpha` tag)
## Quick Start
```bash
npm install @txkit/react @txkit/themes
```
Peer dependencies: `react >= 18`, `wagmi >= 3`, `viem >= 2`, `@tanstack/react-query >= 5`.
### Standalone Mode
txKit creates and manages WagmiProvider + QueryClientProvider:
```tsx
import { TxKitProvider, ConnectWallet } from '@txkit/react'
import '@txkit/themes'
import { mainnet } from 'viem/chains'
import { http } from 'viem'
function App() {
return (
)
}
```
### Embedded Mode
Use inside an existing WagmiProvider (RainbowKit, AppKit, etc.):
```tsx
import { TxKitProvider, TransactionButton, txStep } from '@txkit/react'
import { parseEther } from 'viem'
import '@txkit/themes'
function SendButton() {
return (
)
}
```
### Subpath Imports (Tree-Shaking)
```tsx
import { ConnectWallet } from '@txkit/react/connect'
import { TokenBalance } from '@txkit/react/balance'
import { TransactionButton } from '@txkit/react/transaction'
import { ContractForm } from '@txkit/react/contract'
```
### Three Customization Tiers
Every component supports three levels:
```tsx
// Tier 1: Zero-config
// Tier 2: Custom render via children
{({ balance, symbol, fiatFormatted }) => (
{symbol}: {fiatFormatted}
)}
// Tier 3: Headless hook
const { balance, decimals, symbol } = useTokenBalance({ token: '0xA0b8...' })
```
---
## @txkit/core
Framework-agnostic utilities, types, constants, and error classes.
```bash
npm install @txkit/core
```
### Utility Functions
```ts
// Conditional className builder (like clsx)
cx(...args: CxValue[]): string
// Truncate address: "0x1234...5678"
shortenAddress(address: string, chars?: number): string
// Progressive token formatting with dust threshold
// 0-9: 5 decimals, 10-99: 4, 100-999: 3, 1k-9k: 2, 10k+: k/m/b suffixes
formatTokenAmount(value: bigint, decimals: number, options?: {
dustThreshold?: number // default: 0.0001
locale?: string
}): string
// Split into integer/fraction parts for styling
formatTokenAmountSplit(value: bigint, decimals: number, options?: {
dustThreshold?: number
locale?: string
}): { integer: string; fraction: string; full: string }
// Fiat currency formatting
formatFiatAmount(value: number, currency?: string, locale?: string): string
// Block explorer URL for tx or address
getExplorerUrl(chainId: number, hash: string, type?: 'tx' | 'address'): string | undefined
// Classify Web3 errors by type
classifyError(error: unknown): TransactionErrorCode
// Human-readable error message
getErrorMessage(code: TransactionErrorCode): string
// Check for MAX_UINT256 approval
isMaxApproval(amount: bigint): boolean
// Format decoded calldata for display
formatDecodedCalldata(decoded: DecodedCalldata): string
// Deep equality comparison
deepEqual(a: unknown, b: unknown): boolean
// Poll until truthy value or timeout
type PollUntilOptions = {
interval?: number // default: 2000
timeout?: number // default: 120000
signal?: AbortSignal
}
pollUntil(fn: () => T | Promise, options?: PollUntilOptions): Promise>
```
### Constants
```ts
// DeFiLlama price API
DEFILLAMA_PRICE_URL: 'https://coins.llama.fi/prices/current'
// Frankfurter forex API
FRANKFURTER_API_URL: 'https://api.frankfurter.dev/latest'
// Chain ID to DeFiLlama platform name
CHAIN_TO_DEFILLAMA: Record
// { 1: 'ethereum', 10: 'optimism', 137: 'polygon', 8453: 'base', 42161: 'arbitrum' }
// Native token price identifiers per chain
NATIVE_PRICE_IDS: Record
// { 1: 'coingecko:ethereum', 10: 'coingecko:ethereum', ... }
```
### Error Classes
```ts
// Base error with documentation link
class TxKitError extends Error {
shortMessage: string
docsPath?: string
details?: string
}
// Invalid TxKitProvider configuration
class InvalidConfigError extends TxKitError
// Hook used outside TxKitProvider
class ProviderNotFoundError extends TxKitError
// Embedded mode without WagmiProvider
class MissingWagmiProviderError extends TxKitError
```
### Types
```ts
// ERC-20 token metadata
type TokenInfo = {
address: `0x${string}`
symbol: string
name: string
decimals: number
chainId: number
logoURI?: string
}
// Transaction button state machine (10 states)
type TransactionButtonState =
| 'idle' | 'simulating' | 'confirming-risk' | 'simulation-failed'
| 'approving' | 'awaiting-signature' | 'pending' | 'success' | 'error' | 'rejected'
// Categorized error codes
type TransactionErrorCode =
| 'USER_REJECTED' | 'INSUFFICIENT_FUNDS' | 'SIMULATION_FAILED' | 'EXECUTION_REVERTED'
| 'GAS_ESTIMATION_FAILED' | 'NETWORK_ERROR' | 'TIMEOUT' | 'CHAIN_MISMATCH'
| 'APPROVAL_FAILED' | 'RISK_BLOCKED' | 'UNKNOWN'
// Structured transaction error
type TransactionError = {
code: TransactionErrorCode
message: string
cause?: Error
}
// Minimal transaction receipt
type TransactionReceipt = {
blockNumber: bigint
transactionHash: `0x${string}`
status: 'success' | 'reverted'
gasUsed?: bigint
effectiveGasPrice?: bigint
}
// Decoded contract function call
type DecodedCalldata = {
functionName: string
args: DecodedArg[]
}
type DecodedArg = {
name: string
type: string
value: unknown
}
// Risk assessment result
type RiskResult = {
level: 'low' | 'medium' | 'high' | 'critical'
warnings: string[]
blocked?: boolean
}
// Per-step status (12 states)
type StepStatus =
| 'pending' | 'skipped' | 'simulating' | 'confirming-risk' | 'simulation-failed'
| 'signing' | 'tx-pending' | 'waiting' | 'completed' | 'error' | 'rejected' | 'canceled'
// Overall flow status (7 states)
type FlowStatus = 'idle' | 'simulating-all' | 'running' | 'paused' | 'completed' | 'error' | 'rejected'
```
---
## @txkit/react
React components and headless hooks for Web3.
### TxKitProvider
Root provider. Two modes: standalone (manages wagmi) and embedded (uses existing wagmi).
```ts
// Standalone props
type TxKitProviderStandaloneProps = {
children: ReactNode
config: TxKit.Config
embedded?: false
}
// Embedded props
type TxKitProviderEmbeddedProps = {
children: ReactNode
config?: TxKit.EmbeddedConfig
embedded: true
}
// Config for standalone mode
type TxKit.Config = {
chains: [Chain, ...Chain[]]
transports: Record
wallets?: WalletConfig[]
walletConnectProjectId?: string
theme?: 'light' | 'dark' | 'auto'
variant?: 'default' | 'soft' | 'sharp' | 'rounded'
autoConnect?: boolean
pollingInterval?: number
blockWatching?: { enabled?: boolean; throttleMs?: number }
licenseKey?: string
}
// Config for embedded mode (all optional)
type TxKit.EmbeddedConfig = Pick
// Context hook
const useTxKit = (): TxKit.Context
// Returns: { config, theme, setTheme, isProEnabled }
```
### ConnectWallet
Multi-wallet connection with ENS, balance display, chain switching. State machine: `disconnected` | `connecting` | `connected` | `wrong-chain` | `error`.
```ts
type ConnectWalletProps = {
className?: string
children?: (data: ConnectWalletRenderData) => ReactNode
'data-testid'?: string
label?: string // default: "Connect Wallet"
chainId?: number // force specific chain
labels?: Partial
showEns?: boolean // default: true
showAvatar?: boolean // default: true
showBalance?: boolean // default: true
onError?: (error: Error) => void
onConnect?: (data: { address: string; connector: string }) => void
onDisconnect?: () => void
formatAddress?: (address: string, ensName?: string) => string
}
type ConnectWalletRenderData = {
state: 'disconnected' | 'connecting' | 'connected' | 'wrong-chain' | 'error'
address: `0x${string}` | undefined
displayAddress: string | undefined
ensName: string | null | undefined
ensAvatar: string | null | undefined
formattedBalance: string | undefined
chain: Chain | undefined
connectors: readonly Connector[]
connect: (connector: Connector) => void
disconnect: () => void
switchChain: (chainId: number) => void
openModal: () => void
closePanel: () => void
error: Error | null
isPending: boolean
}
// Headless hook
const useWalletState = (options?: {
chainId?: number
showBalance?: boolean
showEns?: boolean
}): UseWalletStateReturn
```
### TokenBalance
Native + ERC-20 balance display with fiat pricing and auto-formatting.
```ts
type TokenBalanceProps = {
className?: string
children?: (data: TokenBalanceRenderData) => ReactNode
'data-testid'?: string
variant?: 'inline' | 'row'
icon?: string
name?: string
token?: `0x${string}` // omit for native (ETH)
address?: `0x${string}` // defaults to connected wallet
fiatCurrency?: string // default: 'USD'
price?: number // override auto-fetched price
chainId?: number
refetchInterval?: number
labels?: Partial
formatOptions?: { dustThreshold?: number; locale?: string }
showFiat?: boolean
showIcon?: boolean
showSymbol?: boolean
onError?: (error: Error) => void
onBalanceChange?: (balance: bigint, prevBalance: bigint | undefined) => void
}
type TokenBalanceRenderData = {
icon: string | undefined
balance: bigint | undefined
decimals: number | undefined
symbol: string | undefined
formatted: string | undefined
integerPart: string | undefined
fractionPart: string | undefined
isZero: boolean
fiatValue: number | undefined
fiatFormatted: string | undefined
isLoading: boolean
isError: boolean
error: Error | null
refetch: () => void
}
// Headless hooks
const useTokenBalance = (options?: {
token?: `0x${string}`
address?: `0x${string}`
chainId?: number
refetchInterval?: number
enabled?: boolean
}): {
balance: bigint | undefined
decimals: number | undefined
symbol: string | undefined
isLoading: boolean
isError: boolean
error: Error | null
refetch: () => void
}
const useTokenBalances = (options: {
tokens: Array<`0x${string}` | 'native'>
address?: `0x${string}`
chainId?: number
refetchInterval?: number
enabled?: boolean
}): {
balances: TokenBalanceItem[]
isLoading: boolean
isError: boolean
refetch: () => void
}
const useTokenPrice = (options?: {
token?: `0x${string}`
chainId?: number
fiatCurrency?: string
enabled?: boolean
}): { price: number | undefined; isLoading: boolean; isError: boolean }
// Balance invalidation (for custom refresh logic)
const useBalanceInvalidation = (): {
invalidateAffected: (logs: LogEntry[], senderAddress: `0x${string}`) => void
invalidateAll: () => void
}
```
### TransactionButton
Multi-step transaction flow with simulation, approval, confirmation delays, and anti-phishing.
```ts
type TransactionButtonProps = {
className?: string
children?: (data: TransactionButtonRenderData) => ReactNode
'data-testid'?: string
steps: FlowStep[] // transaction steps to execute
flowId?: string // default: '__default__'
safety?: Partial
chainId?: number
label?: string
labels?: Partial
confirmations?: number // default: 1
resetDelay?: number // default: 0
disabled?: boolean
showExplorerLink?: boolean // default: true
onFlowComplete?: (results: Record) => void
onStepComplete?: (stepId: string, result: StepResult) => void
onError?: (error: TransactionError, stepId: string) => void
onFlowStatusChange?: (status: FlowStatus) => void
}
type TransactionButtonRenderData = {
flow: FlowState
currentStep: StepState | undefined
steps: FlowStep[]
explorerUrl: string | undefined
start: () => void
confirm: () => void
cancel: () => void
retry: () => void
retryFrom: (stepId: string) => void
forceSubmit: () => void
reset: () => void
skipStep: () => void
}
```
#### Flow Step Types
```ts
// Step transaction params (raw or dynamic)
type StepTx = TxParams | ((context: StepContext) => TxParams | Promise)
// Transaction step (on-chain)
type FlowStepTx = {
type: 'tx'
id: string
label: string
tx: StepTx
safety?: Partial
gas?: bigint
shouldSkip?: (context: StepContext) => boolean | Promise
waitAfterMs?: number
waitForCondition?: (context: StepContext, signal: AbortSignal) => Promise
optional?: boolean
onStart?: (context: StepContext) => void
onComplete?: (context: StepContext & { hash: `0x${string}`; receipt: TransactionReceipt }) => void
onError?: (context: StepContext & { error: TransactionError }) => void
onCancel?: (context: StepContext) => Promise | void
}
// Signature step (off-chain - EIP-712, personal_sign)
type FlowStepSign = {
type: 'sign'
id: string
label: string
sign: {
signData: SignData | ((context: StepContext) => SignData | Promise)
onSign: (signature: `0x${string}`, context: StepContext) => Promise<{ data?: unknown }>
}
shouldSkip?: (context: StepContext) => boolean | Promise
waitForCondition?: (context: StepContext, signal: AbortSignal) => Promise
optional?: boolean
onStart?: (context: StepContext) => void
onComplete?: (context: StepContext & { signature: `0x${string}` }) => void
onError?: (context: StepContext & { error: TransactionError }) => void
onCancel?: (context: StepContext) => Promise | void
}
type FlowStep = FlowStepTx | FlowStepSign
// Transaction params
type TxParams =
| { to: `0x${string}`; value?: bigint; data?: `0x${string}` } // raw
| { address: `0x${string}`; abi: Abi; functionName: string; args?: unknown[]; value?: bigint } // typed
// Signature data
type SignData =
| { method: 'eth_signTypedData_v4'; domain: TypedDataDomain; types: Record; value: Record }
| { method: 'personal_sign'; message: string }
// Step context (passed to callbacks)
type StepContext = {
results: Record
previousResult: StepResult | undefined
address: `0x${string}`
chainId: number
publicClient: PublicClient
}
type StepResult =
| { type: 'tx'; hash: `0x${string}`; receipt: TransactionReceipt }
| { type: 'sign'; signature: `0x${string}`; data?: unknown }
// Result returned from sign step's onSign callback
type SignResult = { data?: unknown }
// Safety configuration
type SafetyConfig = {
simulate: boolean // simulate before sending
delayMs: number // confirmation countdown (ms)
warnMaxApproval: boolean // warn on MAX_UINT256 approve
riskProvider: TransactionRiskProvider | null // Blowfish/Blockaid integration
}
// Runtime state
type FlowState = {
status: FlowStatus
currentStepIndex: number
totalSteps: number
steps: StepState[]
}
type StepState = {
id: string
status: StepStatus
hash?: `0x${string}`
receipt?: TransactionReceipt
signature?: `0x${string}`
error?: TransactionError
gasEstimate?: bigint
riskResult?: RiskResult
decodedCalldata?: DecodedCalldata
confirmCountdown: number
}
```
#### Flow Helper Functions
```ts
// Create a single transaction step
txStep(
id: string,
label: string,
tx: StepTx,
options?: Partial>
): FlowStepTx
// ERC-20 approve + execute (auto-handles USDT approve-to-zero)
approveAndExecute(params: {
token: `0x${string}`
spender: `0x${string}`
amount: bigint
tx: StepTx
label?: string
safety?: Partial
gas?: bigint
}): FlowStep[]
// Multiple ERC-20 approvals + execute
multiApproveAndExecute(params: {
approvals: Array<{ token: `0x${string}`; spender: `0x${string}`; amount: bigint; label?: string }>
tx: StepTx
label?: string
safety?: Partial
}): FlowStep[]
// Off-chain signature + submission (COW Protocol, Permit2, etc.)
signAndSubmit(params: {
id: string
label: string
signData: SignData | ((context: StepContext) => SignData | Promise)
onSign: (signature: `0x${string}`, context: StepContext) => Promise<{ data?: unknown }>
waitForCondition?: (context: StepContext, signal: AbortSignal) => Promise
onCancel?: (context: StepContext) => Promise | void
}): FlowStepSign
```
#### Headless Hook
```ts
const useTransactionFlow = (options: {
steps: FlowStep[]
flowId?: string
safety?: Partial
chainId?: number
confirmations?: number
resetDelay?: number
onFlowStatusChange?: (status: FlowStatus) => void
onFlowComplete?: (results: Record) => void
onStepError?: (error: TransactionError, stepId: string) => void
onStepComplete?: (stepId: string, result: StepResult) => void
}): {
flow: FlowState
steps: FlowStep[]
start: () => void
confirm: () => void
cancel: () => void
retry: () => void
retryFrom: (stepId: string) => void
forceSubmit: () => void
reset: () => void
skipStep: () => void
}
// Read flow state from any component (compound pattern)
const useFlowState = (flowId?: string): {
flow: FlowState
steps: FlowStep[]
actions: FlowActions
} | undefined
```
### Compound Flow Components
These components auto-connect to TransactionButton's flow state via TxKitProvider context. Place them anywhere in the component tree.
```ts
// Step indicator
type FlowStepsProps = {
className?: string
children?: (data: FlowStepsRenderData) => ReactNode
'data-testid'?: string
flowId?: string
orientation?: 'horizontal' | 'vertical' // default: 'horizontal'
showCompleted?: boolean // default: true
}
type FlowStepsRenderData = {
steps: Array<{ id: string; label: string; status: StepStatus; isCurrent: boolean }>
currentStepIndex: number
totalSteps: number
completedCount: number
}
// Progress bar
type FlowProgressProps = {
className?: string
children?: (data: FlowProgressRenderData) => ReactNode
'data-testid'?: string
flowId?: string
}
type FlowProgressRenderData = {
progress: number
status: FlowStatus
currentStepLabel: string | undefined
}
// Toast notification (renders via portal)
type FlowToastProps = {
className?: string
children?: (data: FlowToastRenderData) => ReactNode
'data-testid'?: string
flowId?: string
autoDismiss?: number // default: 5000 (ms)
position?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'
}
type FlowToastRenderData = {
visible: boolean
message: string
type: 'success' | 'error' | 'info'
stepId: string | undefined
dismiss: () => void
}
```
### ContractForm
ABI-driven form generation with validation, security warnings, and calldata preview.
```ts
type ContractFormProps = {
className?: string
children?: (data: ContractFormRenderData) => ReactNode
'data-testid'?: string
address: `0x${string}`
abi: Abi
functionName: string
chainId?: number
label?: string
labels?: Partial
safety?: Partial
disabled?: boolean
onSuccess?: (receipt: TransactionReceipt) => void
onError?: (error: TransactionError) => void
}
type ContractFormRenderData = {
fields: FieldDescriptor[]
values: Record
errors: Record
touched: Record
warnings: SecurityWarning[]
calldataPreview: string | undefined
isValid: boolean
isPayable: boolean
formError: string | undefined
setFieldValue: (name: string, value: string) => void
setFieldTouched: (name: string) => void
}
type FieldDescriptor = {
name: string
solidityType: string
fieldType: 'address' | 'uint' | 'int' | 'bool' | 'string' | 'bytes' | 'bytesN' | 'array' | 'tuple' | 'tupleArray' | 'unsupported'
bitSize?: number
byteLength?: number
isPayableValue?: boolean
internalType?: string
arrayElementType?: string
arrayFixedLength?: number
components?: FieldDescriptor[]
}
type SecurityWarningLevel = 'warning' | 'danger'
type SecurityWarning = {
level: SecurityWarningLevel
message: string
}
// Headless hook (with extra options for widget integration)
const useContractForm = (options: ContractFormProps & {
computedParams?: Record unknown>
defaultParams?: Record
hiddenParams?: string[]
}): ContractFormRenderData & {
abiFunction: AbiFunction | undefined
steps: FlowStep[]
txParams: ContractTransactionProps | undefined
}
```
Security warnings are shown for 19 dangerous functions: `approve`, `setApprovalForAll`, `transfer`, `transferFrom`, `delegateCall`, `selfdestruct`, `burn`, `renounceOwnership`, `upgradeTo`, `permit`, `multicall`, etc. MAX_UINT256 detection on approve amounts.
---
## @txkit/themes
CSS themes with custom properties. No JavaScript - pure CSS.
```bash
npm install @txkit/themes
```
```tsx
import '@txkit/themes' // applies light theme by default
```
### Theme Classes
- `.tx-light` - Light theme (default, set on `:root`)
- `.tx-dark` - Dark theme
- `.tx-root` - Sets font-family and color
### Visual Variants
- `.tx-soft` - Softer shadows and larger radii
- `.tx-sharp` - Sharp corners, minimal shadows
- `.tx-rounded` - Extra rounded corners
Set via `config.variant` on TxKitProvider.
### CSS Custom Properties
All properties use the `--tx-` prefix and can be overridden:
```css
/* Colors */
--tx-color-primary: #4338CA
--tx-color-primary-hover: #3730A3
--tx-color-primary-active: #312E81
--tx-color-primary-text: #FFFFFF
--tx-color-primary-alpha: rgba(67, 56, 202, 0.1)
/* Semantic colors */
--tx-color-success: #059669
--tx-color-success-text: #FFFFFF
--tx-color-warning: #D97706
--tx-color-warning-text: #FFFFFF
--tx-color-error: #DC2626
--tx-color-error-text: #FFFFFF
--tx-color-info: #2563EB
/* Surfaces */
--tx-color-bg: #FFFFFF
--tx-color-bg-secondary: #F8FAFC
--tx-color-bg-tertiary: #F1F5F9
/* Text */
--tx-color-text: #0F172A
--tx-color-text-secondary: #475569
--tx-color-text-tertiary: #64748B
/* Borders */
--tx-color-border: #E2E8F0
--tx-color-border-hover: #CBD5E1
/* Typography */
--tx-font-family: system-ui, -apple-system, sans-serif
--tx-font-mono: 'IBM Plex Mono', ui-monospace, monospace
--tx-font-size-xs: 0.75rem
--tx-font-size-sm: 0.875rem
--tx-font-size-md: 1rem
--tx-font-size-base: 1rem /* alias for font-size-md */
--tx-font-size-lg: 1.125rem
/* Spacing */
--tx-space-xs: 0.25rem
--tx-space-sm: 0.5rem
--tx-space-md: 0.75rem
--tx-space-lg: 1rem
--tx-space-xl: 1.5rem
/* Border radius */
--tx-radius-sm: 6px
--tx-radius-md: 8px
--tx-radius-lg: 12px
--tx-radius-xl: 20px
--tx-radius-full: 9999px
/* Shadows */
--tx-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05)
--tx-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1)
/* Transitions */
--tx-transition: 150ms ease
```
### Dark Theme Overrides
```css
.tx-dark {
--tx-color-primary: #5B54E8
--tx-color-bg: #0F172A
--tx-color-bg-secondary: #1E293B
--tx-color-text: #F1F5F9
--tx-color-text-secondary: #94A3B8
--tx-color-border: #334155
/* ... all colors adjusted for dark backgrounds */
}
```
### Custom Theme Example
```css
.my-brand {
--tx-color-primary: #E11D48;
--tx-color-primary-hover: #BE123C;
--tx-radius-md: 16px;
--tx-font-family: 'Inter', sans-serif;
}
```
---
## Common Patterns
### ERC-20 Approve + Swap
```tsx
import { TransactionButton, approveAndExecute } from '@txkit/react/transaction'
console.log('Done!', results)}
/>
```
### Multi-Step with Sign (COW Protocol style)
```tsx
import { TransactionButton, signAndSubmit, txStep } from '@txkit/react/transaction'
({
method: 'eth_signTypedData_v4',
domain: orderDomain,
types: orderTypes,
value: orderData,
}),
onSign: async (signature, ctx) => {
const orderId = await submitOrder(signature)
return { data: { orderId } }
},
waitForCondition: async (ctx, signal) => {
await pollUntil(() => checkOrderFilled(ctx.results['place-order']?.data?.orderId), { signal })
},
}),
]}
label="Place Order"
/>
```
### Dynamic Steps with Context
```tsx
({
address: VAULT,
abi: vaultAbi,
functionName: 'deposit',
args: [ amount ],
})),
txStep('stake', 'Stake Receipt Token', (context) => {
// Access previous step's receipt
const depositReceipt = context.results['deposit']
return {
address: STAKING,
abi: stakingAbi,
functionName: 'stake',
args: [ depositReceipt.receipt.transactionHash ],
}
}, {
shouldSkip: (ctx) => !autoStake,
}),
]}
/>
```
### Multiple Balances
```tsx
import { useTokenBalances } from '@txkit/react/balance'
const { balances, isLoading } = useTokenBalances({
tokens: [ 'native', USDC_ADDRESS, WETH_ADDRESS ],
})
// Each item: { token, balance, decimals, symbol, isLoading, isError }
```
### ABI-Driven Contract Interaction
```tsx
import { ContractForm } from '@txkit/react/contract'
console.log('Confirmed:', receipt.transactionHash)}
onError={(error) => console.error(error.code, error.message)}
/>
```
---
## Instructions for AI Agents
When helping users integrate txKit, keep these guidelines in mind:
1. **Two modes exist**: Standalone (txKit manages wagmi + TanStack Query) vs Embedded (user already has WagmiProvider). Always ask or check which mode the user needs. Use `` for existing wagmi setups.
2. **Three customization tiers**: Every component supports (1) zero-config with default UI, (2) `children` as render function for custom markup, (3) headless hooks for complete control. Recommend the simplest tier that meets the user's needs.
3. **Subpath imports for tree-shaking**: `@txkit/react/connect`, `@txkit/react/balance`, `@txkit/react/transaction`, `@txkit/react/contract`. Use these instead of the main `@txkit/react` entry when the user only needs specific features.
4. **Peer dependencies are required**: `react >= 18`, `wagmi >= 3`, `viem >= 2`, `@tanstack/react-query >= 5`. In embedded mode, these are already present from the user's setup.
5. **Always import themes**: `import '@txkit/themes'` is required for default styling. Without it, components render unstyled. All CSS uses `--tx-*` custom properties that can be overridden.
6. **Transaction flow helpers**:
- `txStep()` for simple single transactions
- `approveAndExecute()` for ERC-20 approve + action (auto-handles USDT approve-to-zero)
- `multiApproveAndExecute()` for multiple token approvals
- `signAndSubmit()` for off-chain signatures (EIP-712, personal_sign)
7. **Compound components are independent**: FlowSteps, FlowProgress, FlowToast connect to TransactionButton's flow state through TxKitProvider context. They can be placed anywhere in the component tree, not just next to TransactionButton.
8. **Anti-phishing is built-in**: TransactionButton includes calldata decoding, simulation preview, bounded approvals (warns on MAX_UINT256), and configurable confirmation delays. The `safety` prop controls this behavior.
9. **ContractForm security**: Automatically detects and warns about 19 dangerous Solidity functions. Shows calldata preview with full addresses (not shortened) to prevent address poisoning.
10. **Balance watching**: TokenBalance auto-refreshes on new blocks when `blockWatching.enabled` is true in config. Use `useBalanceInvalidation()` for manual refresh after transactions.
11. **Error handling**: All errors are classified into `TransactionErrorCode` categories. Use `classifyError()` to categorize and `getErrorMessage()` for user-friendly messages.
12. **WCAG 2.1 AA**: All components are accessible - focus traps in modals, keyboard navigation, screen reader support, reduced motion respect, 44px touch targets.