This is a demonstration project showing how to use XState v5 to model a cryptocurrency wallet connection lifecycle.
The WalletMachine demonstrates:
- Connection Management: Handle wallet connection/disconnection
- Hierarchical States: Parent
connectedstate with child states for chain validation - Guards: Conditional transitions using
isChainSupportedguard - Actions & Context: Managing state data (wallet address, chain ID, balance)
- Actors/Invocations: Handling asynchronous operations (connect, fetch balance, switch chain)
- Global Transitions:
SWITCH_CHAINevent can interrupt any child state
src/
__tests__/
utils.ts # Test utilities for state machine testing
services/
wallet/
controllers/
walletController.ts # Mocked wallet business logic
xstate/
wallet/
walletMachine.ts # XState v5 state machine definition
walletMachine.test.ts # Comprehensive test suite
index.ts # Main entry point with demonstration
npm installRun the demonstration:
npm run devOr build and run:
npm run build
npm startThe project includes comprehensive tests using Vitest. The test suite uses a transition-based testing approach that tracks state transitions rather than relying on timeouts, making tests more reliable and maintainable.
Run tests:
npm testRun tests in watch mode:
npm run test:watchTests are located in src/xstate/wallet/walletMachine.test.ts and cover:
- State Transitions: Verifying correct state transitions for all events
- Hierarchical States: Testing nested states within the
connectedparent state - Global Transitions: Testing that
SWITCH_CHAINcan interrupt any child state - Error Handling: Testing error states and recovery
- Context Updates: Verifying that context (address, chainId, balance) is updated correctly
The test utilities in src/__tests__/utils.ts provide helpers for:
- Creating and tracking state machine actors
- Managing controllable promises for async operations
- Extracting nested state values
- disconnected: Initial state, wallet not connected
- connecting: Asynchronously connecting to wallet
- connected: Wallet connected (parent state)
- checkingChain: Validating if chain is supported
- chainReady: Chain is supported, ready for operations
- unsupportedChain: Chain is not supported
- fetchingBalance: Fetching wallet balance
- switchingChain: Switching to a different chain
- error: Error state accessible from any async operation
CONNECT: Connect wallet with addressDISCONNECT: Disconnect walletFETCH_BALANCE: Fetch wallet balanceSWITCH_CHAIN: Switch to a different chain (global transition)RETRY: Retry from error state
- Hierarchical States: The
connectedstate contains child states - Guards:
isChainSupportedguard determines chain validation - Global Transitions:
SWITCH_CHAINat theconnectedlevel can interrupt any child state - Async Operations: Using
fromPromiseactors for async wallet operations - Context Management: Storing wallet address, chain ID, balance, and errors