repos — cfx-ui
@cfxdevkit/defi-react
Opinionated DeFi widgets for Conflux eSpace applications. Provides reusable React components for common DeFi operations including token swapping, liquidity provision, portfolio tracking, and transaction status display.
Overview
The defi-react package is a Tier 0c React UI surface (per ADR-0003) that delivers:
- DeFi-specific widgets (swap, liquidity, token picker)
- Headless UI primitives built on CSS custom properties
- Hooks for blockchain data fetching (balances, pool info, prices)
- Integration with Conflux CDK and viem for wallet interactions
- Swappi V2 DEX adapter for quote generation and transaction building
All components use CSS variables from @cfxdevkit/theme/css and require no additional styling dependencies.
Installation
pnpm add @cfxdevkit/defi-reactPeer Dependencies
Ensure these are installed in your project:
{
"peerDependencies": {
"@cfxdevkit/theme": "workspace:^",
"@tanstack/react-query": "^5.100.9",
"react": "^19.2.5",
"viem": "^2.21.0"
}
}Required Setup
Import the theme CSS in your application entry point:
import '@cfxdevkit/theme/css';Core Concepts
CSS Custom Properties
All components rely on CSS variables defined in @cfxdevkit/theme/css for theming. Key variable categories:
- Colors:
--cfx-color-* - Spacing:
--cfx-space-* - Typography:
--cfx-text-*,--cfx-font-* - Radius:
--cfx-radius-* - Shadows:
--cfx-shadow-*
Hooks Architecture
Data-fetching hooks follow React Query patterns:
- Automatic caching and refetching
- Configurable refresh intervals
- Loading/error states
- Chain-aware querying via
@cfxdevkit/react/context
DEX Abstraction
Swap functionality uses a DexAdapter interface, enabling:
- Protocol-agnostic swap hooks
- Easy integration with different DEXes
- Current implementation: Swappi V2 adapter
Key Components
Balance
Displays token holdings and values.
PortfolioTable (src/balance/PortfolioTable.tsx)
<PortfolioTable
tokens={[
{ address: '0x...', symbol: 'USDT', name: 'Tether', decimals: 6, logoURI: '...' }
]}
address="0xUserAddress"
/>- Shows formatted balances for ERC-20 tokens plus native CFX
- Handles wrapped native token aggregation (WCFX → CFX)
- Requires address connection state
- Custom row rendering via
renderRowprop
usePortfolio (src/balance/usePortfolio.ts)
const { rows, isLoading, error } = usePortfolio({
tokens: tokenList,
address: userAddress,
refreshMs: 15000
});- Fetches balances for token list + native CFX
- Returns sorted rows by balance (descending)
- Handles token address normalization for wrapped natives
- Parallel queries for ERC-20 balances
Pool
Provides Swappi V2 pair data and pricing.
usePoolTokens (src/pool/usePoolTokens.ts)
const { pool, isLoading, error } = usePoolTokens({
service: swapService,
pairAddress: '0xPairAddress'
});- Fetches token0/token1 and reserves from pair contract
- Returns structured pool data
- Configurable refetch interval
useTokenPrice (src/pool/useTokenPrice.ts)
const { price } = useTokenPrice({
service: swapService,
tokenAddress: '0xToken',
quoteToken: WCFX_ADDRESS
});- Gets price of token in quote token units
- Returns raw bigint (1e18 input token basis)
- Requires manual decimal adjustment for display
Primitives
Headless UI building blocks in @cfxdevkit/defi-react/primitives.
Core Components
Button: Primary/secondary/ghost/danger variants with loading statesCard: Container with elevation and padding optionsInput: Styled text input with validation statesSelectMenu: Accessible native select dropdownSegmentedControl: Toggle group for mutually exclusive optionsBadge: Status indicators with color variantsNetworkBadge: Network identification (testnet/mainnet)
Layout Components
SectionHeader: Title with subtitle and action slotMetricCard: KPI display with value and deltaStatusBanner: Full-width alert stripAppShell: Full-page layout with nav barMainGrid: Two-column layout with optional sidebarPanel: Container with header (title/icon/actions)
Feedback Components
AppToaster: Fixed-position toast containerSelectableListItem: Highlightable list item for pickersCopyButton: Clipboard copy button with state
Specialized Components
TradeTokenField: Swap input with token displayTradeActionBar: Swap/provide toggle + slippage inputTradeSummaryGrid: Trade confirmation detailsFaucetWidget: Devnet funding interfaceDevkitStatus: Node/keystore health dashboardNavWalletActions: Wallet connect/sign-in buttons
Service
Swappi V2 DEX integration.
SwapService (src/service/SwapService.ts)
const service = new SwapService({
chainId: 1030, // or 71 for testnet
client: espaceClient
});- Implements
DexAdapterinterface for swap operations - Provides:
getQuote(): Calculate swap output with slippagebuildCalldata(): Encode transaction datagetPoolTokens(): Fetch pair reservesgetTokenPrice(): Get token price in quote tokengetPair(): Find pair address for two tokens
- Handles WCFX wrapping/unwrapping logic
- Multi-hop routing via WCFX when direct pair missing
Swap
Token exchange interface.
SwapWidget (src/swap/SwapWidget.tsx)
<SwapWidget
adapter={createSwappiAdapter({ chainId, client })}
tokens={tokenList}
defaultTokenIn="0xCFX"
defaultTokenOut="0xUSDT"
onSwapSubmitted={handleSwapTx}
/>- Headless swap interface requiring token selection
- Shows input/output amounts with balance display
- Handles approval workflow for ERC-20 tokens
- Displays quote, price impact, and transaction states
- Token picker modal for selection
- Automatic token inversion when swapping directions
useSwap (src/swap/useSwap.ts)
const {
quote,
isQuoting,
approveAsync,
needsApproval,
swapAsync,
isSwapping
} = useSwap({
adapter: swapService,
tokenIn: inputToken,
tokenOut: outputToken,
amountIn: parsedAmount
});- Manages swap quote fetching
- Handles token approval requirements
- Provides async functions for approval and swap
- Tracks approval and swap transaction states
- Resettable state for reuse
Token Picker
Token search and selection interface.
TokenPicker (src/token-picker/TokenPicker.tsx)
<TokenPicker
registry={createTokenRegistry(tokenList)}
chainId={1030}
selected={currentToken}
onSelect={handleTokenSelect}
/>- Pure client-side token search
- Registry-based for efficient filtering
- Visual selection indicators
- Logo display with error fallback
- Keyboard accessible
Transaction Status
Live transaction tracking.
TxStatusList (src/tx-status/TxStatusList.tsx)
<TxStatusList recent={3} onConfirm={handleConfirmation} />- Displays recent transactions with status indicators
- Requires
TxListProviderhigher in tree - Tracks transactions via
useTxList().track(hash) - Automatic polling for confirmation status
- Configurable display limit
TxStatusToast (src/tx-status/TxStatusToast.tsx)
<TxStatusToast hash={txHash} onConfirm={handleConfirmation} />- Individual transaction status display
- Pending (warning dot) → Success/Failure (color-coded)
- Transaction hash display with mono font
- Automatic confirmation handling via callback
Liquidity Provision
Swappi V2 liquidity management.
AddLiquidityWidget (src/lp/AddLiquidityWidget.tsx)
<AddLiquidityWidget
service={swapService}
tokenA="0xTokenA"
tokenB="0xWCFX"
poolData={poolData} // optional from usePoolTokens
onAddLiquidity={handleAddLiquidity}
/>- Two-token input for liquidity provision
- Displays price ratio and estimated pool share
- Auto-fills second amount based on reserves
- Share percentage badge shown when amountA > 0
- Form validation and submission handling
PoolShareBadge (src/lp/AddLiquidityWidget.tsx)
<PoolShareBadge sharePercent={12.5} />- Displays liquidity provider share percentage
- Formats small shares as “<0.01%”
- Subtle styling for inline display
Usage Examples
Basic Swap Interface
import { SwapWidget, createSwappiAdapter } from '@cfxdevkit/defi-react/swap';
import { useAccount, useClient } from '@cfxdevkit/react';
function SwapInterface() {
const { address } = useAccount();
const client = useClient();
const adapter = createSwappiAdapter({ chainId: 1030, client });
if (!address) return <p>Connect wallet</p>;
return (
<SwapWidget
adapter={adapter}
tokens=[/* token list */]
defaultTokenIn="0xCFX"
defaultTokenOut="0xUSDT"
onSwapSubmitted={({ hash }) => console.log('Swap tx:', hash)}
/>
);
}Portfolio Display
import { PortfolioTable } from '@cfxdevkit/defi-react/balance';
import { useAccount } from '@cfxdevkit/react';
function Portfolio() {
const { address } = useAccount();
const tokens = [/* token metadata */];
return (
<div>
<h2>My Portfolio</h2>
{address ? (
<PortfolioTable tokens={tokens} address={address} />
) : (
<p>Connect wallet to view portfolio</p>
)}
</div>
);
}Liquidity Position Add
import { AddLiquidityWidget, usePoolTokens } from '@cfxdevkit/defi-react';
import { useClient } from '@cfxdevkit/react/context';
import { createSwapService } from '@cfxdevkit/defi-react/service';
function AddLiquidity({ pairAddress }) {
const client = useClient();
const service = createSwapService({ chainId: 1030, client });
const { pool } = usePoolTokens({ service, pairAddress });
return (
<AddLiquidityWidget
service={service}
tokenA="0xTokenA"
tokenB="0xTokenB"
poolData={pool}
onAddLiquidity={({ amountA, amountB }) => {
// submit liquidity transaction
}}
/>
);
}Important Notes
Browser Support
- Requires modern browsers with CSS custom property support
- Tested in latest Chrome, Firefox, Safari, Edge
Performance
- Hooks use React Query for efficient data fetching
- Components minimize re-renders via memoization where beneficial
- Large token lists should be memoized before passing to pickers
Error Handling
- All hooks return error objects for UI display
- Network errors propagate as standard JavaScript Errors
- Transaction errors include viem error details
- Validation errors shown inline in form components
Testing
- Unit tests with Vitest
- Mock blockchain clients for deterministic tests
- Component testing via React Testing Library (not shown in source)
Internationalization
- Components use English strings internally
- Consumer applications should wrap text for translation
- No built-in i18n support in this package
Accessibility
- Semantic HTML where possible
- ARIA labels on interactive elements
- Focus management in modal dialogs
- Color contrast adheres to WCAG AA standards
- Keyboard navigation supported in all interactive components
This documentation covers the core functionality of the @cfxdevkit/defi-react package. For detailed API reference, consult the source code and TypeScript definitions. Additional usage patterns can be found in the package’s test files and example applications.