Core Features
Multi-Provider Support
Integrated support for Jupiter aggregation, Raydium pools, and PumpSwap custom routing
Intelligent Routing
Automatic route optimization to find the best prices and lowest fees across all DEXs
Real-time Pricing
Live price updates, slippage calculations, and fee estimates for informed trading decisions
Advanced Controls
Customizable slippage, provider selection, and transaction parameters for power users
Installation & Setup
1
Import Module
Import the swap components and hooks:
import {
SwapScreen,
useSwapLogic,
TradeService,
SelectTokenModal
} from '@/modules/swap';
2
Wallet Integration
Ensure Embedded Wallets are configured:
import { useWallet } from '@/modules/wallet-providers';
3
DEX Configuration
Configure DEX provider settings (optional):
const swapConfig = {
defaultProvider: 'jupiter',
slippageTolerance: 0.5,
enableCustomPools: true
};
Module Architecture
The swap module is built with a modular, provider-agnostic architecture:src/modules/swap/
├── components/ # UI components
│ ├── SwapScreen.tsx # Main swap interface
│ ├── SelectTokenModal.tsx
│ ├── SwapComponents/ # Specialized components
│ │ ├── Shimmer.tsx
│ │ ├── ProviderSelector.tsx
│ │ ├── PumpSwapControls.tsx
│ │ ├── SwapInfo.tsx
│ │ ├── StatusDisplay.tsx
│ │ └── Keypad.tsx
├── hooks/ # Custom React hooks
│ └── useSwapLogic.ts
├── services/ # DEX integrations
│ ├── TradeService.ts
│ └── JupiterService.ts
└── index.ts # Public API exports
DEX Providers
- Jupiter
- Raydium
- PumpSwap
Primary DEX Aggregator - Best price discovery across all Solana DEXsFeatures:
import { JupiterService } from '@/modules/swap';
// Get optimal swap quote
const quote = await JupiterService.getQuote({
inputMint: 'So11111111111111111111111111111111111111112', // SOL
outputMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC
amount: 1000000000, // 1 SOL in lamports
slippageBps: 50 // 0.5% slippage
});
// Execute swap with Jupiter
const result = await JupiterService.executeSwap({
quote,
userPublicKey: wallet.publicKey,
sendTransaction
});
- Route optimization across 20+ DEXs
- Price impact calculation
- Multi-hop swaps
- Minimal slippage
- Best execution prices
Direct Pool Access - Concentrated liquidity and direct pool swapsFeatures:
// Raydium integration through Jupiter or direct calls
const raydiumSwap = await TradeService.executeSwap(
inputToken,
outputToken,
amount,
userPublicKey,
sendTransaction,
{ provider: 'raydium' }
);
- Concentrated liquidity pools
- Direct pool swaps
- Fee sharing options
- Advanced pool analytics
- Custom liquidity provision
Custom Pool Support - Direct pool address specification with custom slippageFeatures:
// PumpSwap with custom pool
const pumpSwapResult = await TradeService.executeSwap(
inputToken,
outputToken,
amount,
userPublicKey,
sendTransaction,
{
provider: 'pumpswap',
poolAddress: 'custom_pool_address',
slippageBps: 300 // 3% slippage
}
);
- Custom pool address input
- Configurable slippage (1-30%)
- High impact warnings
- Direct pool interaction
- Specialized token support
Core Components
- Swap Screen
- Token Selection
- Specialized Components
SwapScreen - Complete swap interface with all featuresimport { SwapScreen } from '@/modules/swap';
function TradingInterface() {
return (
<SwapScreen
defaultInputToken="SOL"
defaultOutputToken="USDC"
theme="dark"
showProviderSelector={true}
enableCustomSlippage={true}
/>
);
}
- Token selection with search
- Real-time price updates
- Provider selection interface
- Slippage controls
- Transaction status tracking
- Error handling and recovery
SelectTokenModal - Advanced token selection interfaceimport { SelectTokenModal } from '@/modules/swap';
function CustomTokenSelector() {
const [showModal, setShowModal] = useState(false);
return (
<View>
<Button title="Select Token" onPress={() => setShowModal(true)} />
<SelectTokenModal
visible={showModal}
onClose={() => setShowModal(false)}
onTokenSelect={(token) => {
setSelectedToken(token);
setShowModal(false);
}}
showPopularTokens={true}
enableSearch={true}
showBalances={true}
/>
</View>
);
}
- Search functionality with metadata
- Popular tokens list
- Real-time balance display
- Price information
- Custom token import
- Favorite tokens management
Granular UI Components - Building blocks for custom interfaces
import {
ProviderSelector,
SwapInfo,
StatusDisplay,
Keypad
} from '@/modules/swap/components/SwapComponents';
function CustomSwapInterface() {
return (
<View>
<ProviderSelector
selectedProvider="jupiter"
onProviderChange={setProvider}
providers={['jupiter', 'raydium', 'pumpswap']}
/>
<SwapInfo
inputAmount={amount}
outputAmount={expectedOutput}
exchangeRate={rate}
priceImpact={impact}
fees={fees}
/>
<StatusDisplay
status={transactionStatus}
message={statusMessage}
progress={progress}
/>
<Keypad
value={inputAmount}
onValueChange={setInputAmount}
maxDecimals={9}
/>
</View>
);
}
Core Hook: useSwapLogic
The primary hook for swap functionality:import { useSwapLogic } from '@/modules/swap';
import { useWallet } from '@/modules/wallet-providers';
function CustomSwapImplementation() {
const { publicKey, connected, sendTransaction } = useWallet();
const routeParams = useRoute().params || {};
const {
// Token state
inputToken,
outputToken,
setInputToken,
setOutputToken,
// Amount and pricing
inputAmount,
setInputAmount,
outputAmount,
exchangeRate,
priceImpact,
// Transaction state
isLoading,
transactionStatus,
error,
// Actions
handleSwap,
handleTokenSwitch,
refreshPrices,
// Provider state
selectedProvider,
setSelectedProvider,
availableProviders,
// Advanced options
slippage,
setSlippage,
customPoolAddress,
setCustomPoolAddress
} = useSwapLogic(routeParams, publicKey, connected, sendTransaction);
return (
<View style={styles.container}>
<TokenPairSelector
inputToken={inputToken}
outputToken={outputToken}
onInputTokenChange={setInputToken}
onOutputTokenChange={setOutputToken}
onSwitch={handleTokenSwitch}
/>
<AmountInput
value={inputAmount}
onChange={setInputAmount}
token={inputToken}
/>
<SwapDetails
outputAmount={outputAmount}
exchangeRate={exchangeRate}
priceImpact={priceImpact}
provider={selectedProvider}
/>
<SwapButton
onPress={handleSwap}
loading={isLoading}
disabled={!connected || !inputAmount}
/>
</View>
);
}
TradeService Integration
Provider-agnostic service for executing swaps:object
Show Basic Swap Execution
Show Basic Swap Execution
import { TradeService } from '@/modules/swap';
// Simple swap execution
const swapResult = await TradeService.executeSwap(
inputToken, // Token mint address or symbol
outputToken, // Token mint address or symbol
amount, // Amount in token's base units
walletPublicKey, // User's wallet public key
sendTransaction // Wallet's send transaction function
);
console.log('Swap completed:', swapResult);
// {
// signature: 'transaction_signature',
// inputAmount: 1000000000,
// outputAmount: 995000000,
// provider: 'jupiter',
// priceImpact: 0.12,
// fees: { total: 5000, breakdown: {...} }
// }
object
Show Advanced Swap Options
Show Advanced Swap Options
// Advanced swap with custom options
const advancedSwap = await TradeService.executeSwap(
inputToken,
outputToken,
amount,
walletPublicKey,
sendTransaction,
{
// Provider selection
provider: 'jupiter', // 'jupiter', 'raydium', 'pumpswap'
// Slippage control
slippageBps: 100, // 1% slippage
// PumpSwap specific
poolAddress: 'custom_pool_address',
// Fee configuration
priorityFee: 0.001,
// Status callbacks
onStatusUpdate: (status) => {
console.log('Swap status:', status);
},
// Error handling
onError: (error) => {
console.error('Swap error:', error);
}
}
);
object
Show Fee Calculation
Show Fee Calculation
// Calculate fees before execution
const feeEstimate = await TradeService.calculateFees(
inputToken,
outputToken,
amount,
selectedProvider
);
console.log('Fee breakdown:', feeEstimate);
// {
// platformFee: 1000000,
// networkFee: 5000,
// providerFee: 2000,
// total: 1007000,
// totalInSOL: 0.001007
// }
Quick Start Examples
import { SwapScreen } from '@/modules/swap';
import { useWallet } from '@/modules/wallet-providers';
function BasicTradingApp() {
const { connected } = useWallet();
if (!connected) {
return (
<View style={styles.connectPrompt}>
<Text style={styles.title}>Connect Wallet to Trade</Text>
<ConnectWalletButton />
</View>
);
}
return (
<View style={styles.container}>
<Text style={styles.title}>Token Swap</Text>
<SwapScreen
defaultInputToken="SOL"
defaultOutputToken="USDC"
showProviderSelector={true}
enableCustomSlippage={true}
theme="dark"
/>
</View>
);
}
import { useSwapLogic } from '@/modules/swap';
import { useWallet } from '@/modules/wallet-providers';
function CustomSwapInterface() {
const { publicKey, connected, sendTransaction } = useWallet();
const {
inputToken,
outputToken,
inputAmount,
outputAmount,
handleSwap,
isLoading,
error,
setInputToken,
setOutputToken,
setInputAmount
} = useSwapLogic({}, publicKey, connected, sendTransaction);
return (
<View style={styles.container}>
<View style={styles.tokenPair}>
<TokenSelector
selectedToken={inputToken}
onTokenSelect={setInputToken}
label="From"
/>
<TouchableOpacity
style={styles.switchButton}
onPress={handleTokenSwitch}
>
<Text>⇅</Text>
</TouchableOpacity>
<TokenSelector
selectedToken={outputToken}
onTokenSelect={setOutputToken}
label="To"
/>
</View>
<View style={styles.amountSection}>
<TextInput
style={styles.amountInput}
value={inputAmount}
onChangeText={setInputAmount}
placeholder="0.00"
keyboardType="numeric"
/>
<Text style={styles.outputAmount}>
≈ {outputAmount} {outputToken?.symbol}
</Text>
</View>
<Button
title={isLoading ? "Swapping..." : "Swap"}
onPress={handleSwap}
disabled={!connected || isLoading || !inputAmount}
style={styles.swapButton}
/>
{error && (
<Text style={styles.errorText}>{error}</Text>
)}
</View>
);
}
import {
SwapScreen,
TradeService,
useSwapLogic
} from '@/modules/swap';
import { useFetchTokens } from '@/modules/data-module';
function TradingDashboard() {
const { wallet } = useWallet();
const { tokens, refetch } = useFetchTokens(wallet?.address);
const [tradingHistory, setTradingHistory] = useState([]);
const [favorites, setFavorites] = useState(['SOL', 'USDC', 'BONK']);
const handleSwapComplete = async (result) => {
// Add to trading history
setTradingHistory(prev => [result, ...prev.slice(0, 9)]);
// Refresh portfolio
await refetch();
// Show success notification
Alert.alert(
'Swap Successful!',
`Swapped ${result.inputAmount} ${result.inputToken} for ${result.outputAmount} ${result.outputToken}`
);
};
const getTopTokenPairs = () => {
return [
{ input: 'SOL', output: 'USDC' },
{ input: 'SOL', output: 'BONK' },
{ input: 'USDC', output: 'SOL' },
{ input: 'BONK', output: 'SOL' }
];
};
return (
<ScrollView style={styles.dashboard}>
<View style={styles.header}>
<Text style={styles.title}>Trading Dashboard</Text>
<View style={styles.portfolioValue}>
<Text style={styles.valueLabel}>Portfolio Value</Text>
<Text style={styles.valueAmount}>
${tokens?.reduce((sum, token) => sum + (token.value || 0), 0).toFixed(2)}
</Text>
</View>
</View>
<View style={styles.quickSwaps}>
<Text style={styles.sectionTitle}>Quick Swaps</Text>
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
{getTopTokenPairs().map((pair, index) => (
<TouchableOpacity
key={index}
style={styles.quickSwapCard}
onPress={() => navigateToSwap(pair)}
>
<Text style={styles.pairText}>
{pair.input} → {pair.output}
</Text>
</TouchableOpacity>
))}
</ScrollView>
</View>
<View style={styles.mainSwap}>
<SwapScreen
onSwapComplete={handleSwapComplete}
favoriteTokens={favorites}
showAdvancedOptions={true}
/>
</View>
<View style={styles.recentTrades}>
<Text style={styles.sectionTitle}>Recent Trades</Text>
{tradingHistory.map((trade, index) => (
<TradeHistoryItem key={index} trade={trade} />
))}
</View>
</ScrollView>
);
}
Advanced Features
Real-time Price Monitoring
function useRealTimePricing(inputToken, outputToken, amount) {
const [quote, setQuote] = useState(null);
const [priceHistory, setPriceHistory] = useState([]);
useEffect(() => {
if (!inputToken || !outputToken || !amount) return;
const fetchQuote = async () => {
try {
const newQuote = await TradeService.getQuote(inputToken, outputToken, amount);
setQuote(newQuote);
// Track price history
setPriceHistory(prev => [
...prev.slice(-29), // Keep last 30 prices
{
timestamp: Date.now(),
rate: newQuote.rate,
priceImpact: newQuote.priceImpact
}
]);
} catch (error) {
console.error('Quote fetch failed:', error);
}
};
// Initial fetch
fetchQuote();
// Update every 10 seconds
const interval = setInterval(fetchQuote, 10000);
return () => clearInterval(interval);
}, [inputToken, outputToken, amount]);
return { quote, priceHistory };
}
Slippage Protection
function useSlippageProtection() {
const [slippageSettings, setSlippageSettings] = useState({
auto: true,
custom: 0.5,
maxImpact: 5.0
});
const calculateOptimalSlippage = (priceImpact, volatility) => {
if (priceImpact < 0.1) return 0.1;
if (priceImpact < 0.5) return 0.3;
if (priceImpact < 1.0) return 0.5;
if (priceImpact < 3.0) return 1.0;
return Math.min(priceImpact * 1.5, 5.0);
};
const validateSlippage = (quote, slippage) => {
if (quote.priceImpact > slippageSettings.maxImpact) {
throw new Error(`Price impact (${quote.priceImpact}%) exceeds maximum allowed (${slippageSettings.maxImpact}%)`);
}
if (slippage < quote.priceImpact) {
console.warn('Slippage tolerance may be too low for current market conditions');
}
return true;
};
return {
slippageSettings,
setSlippageSettings,
calculateOptimalSlippage,
validateSlippage
};
}
Multi-Route Comparison
function useMultiRouteComparison(inputToken, outputToken, amount) {
const [routes, setRoutes] = useState({});
const [bestRoute, setBestRoute] = useState(null);
useEffect(() => {
if (!inputToken || !outputToken || !amount) return;
const compareRoutes = async () => {
try {
const [jupiterQuote, raydiumQuote, pumpQuote] = await Promise.allSettled([
TradeService.getQuote(inputToken, outputToken, amount, { provider: 'jupiter' }),
TradeService.getQuote(inputToken, outputToken, amount, { provider: 'raydium' }),
TradeService.getQuote(inputToken, outputToken, amount, { provider: 'pumpswap' })
]);
const routeResults = {
jupiter: jupiterQuote.status === 'fulfilled' ? jupiterQuote.value : null,
raydium: raydiumQuote.status === 'fulfilled' ? raydiumQuote.value : null,
pumpswap: pumpQuote.status === 'fulfilled' ? pumpQuote.value : null
};
setRoutes(routeResults);
// Find best route by output amount
const best = Object.entries(routeResults)
.filter(([_, quote]) => quote !== null)
.reduce((best, [provider, quote]) =>
!best || quote.outputAmount > best.quote.outputAmount
? { provider, quote }
: best
, null);
setBestRoute(best);
} catch (error) {
console.error('Route comparison failed:', error);
}
};
compareRoutes();
}, [inputToken, outputToken, amount]);
return { routes, bestRoute };
}
Error Handling & Recovery
object
Show Common Swap Errors
Show Common Swap Errors
Insufficient Balance
- Check user’s token balance before swap
- Account for transaction fees
- Provide clear balance information
- Automatic slippage adjustment
- Market volatility warnings
- Retry with higher slippage options
- RPC connection failures
- Transaction timeout handling
- Automatic retry mechanisms
- Fallback to alternative DEXs
- Provider health monitoring
- User notification of issues
function RobustSwapExecution() {
const executeSwapWithRetry = async (swapParams, maxRetries = 3) => {
let lastError;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await TradeService.executeSwap(...swapParams);
} catch (error) {
lastError = error;
console.warn(`Swap attempt ${attempt} failed:`, error.message);
if (attempt === maxRetries) break;
// Handle specific errors
if (error.message.includes('slippage')) {
// Increase slippage and retry
swapParams[5] = {
...swapParams[5],
slippageBps: (swapParams[5]?.slippageBps || 50) * 1.5
};
} else if (error.message.includes('network')) {
// Wait before retry for network issues
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
}
}
}
throw lastError;
};
return { executeSwapWithRetry };
}
Performance Optimization
Quote Caching: Implement intelligent quote caching to reduce API calls and improve response times.
Rate Limiting: Be mindful of DEX API rate limits when implementing real-time price updates.
function useOptimizedQuoting() {
const [quoteCache, setQuoteCache] = useState(new Map());
const [lastQuoteTime, setLastQuoteTime] = useState(0);
const getCachedQuote = (inputToken, outputToken, amount) => {
const key = `${inputToken}-${outputToken}-${amount}`;
const cached = quoteCache.get(key);
if (cached && Date.now() - cached.timestamp < 10000) { // 10 second cache
return cached.quote;
}
return null;
};
const fetchQuoteWithCache = async (inputToken, outputToken, amount) => {
// Check cache first
const cached = getCachedQuote(inputToken, outputToken, amount);
if (cached) return cached;
// Rate limiting
const now = Date.now();
if (now - lastQuoteTime < 1000) { // 1 second minimum between requests
await new Promise(resolve => setTimeout(resolve, 1000 - (now - lastQuoteTime)));
}
const quote = await TradeService.getQuote(inputToken, outputToken, amount);
// Cache result
const key = `${inputToken}-${outputToken}-${amount}`;
setQuoteCache(prev => new Map(prev.set(key, {
quote,
timestamp: Date.now()
})));
setLastQuoteTime(Date.now());
return quote;
};
return { fetchQuoteWithCache };
}
Integration with Other Modules
Embedded Wallets
Essential for transaction signing and balance checking
Data Module
Token metadata, prices, and portfolio integration
All DEX Modules
Works alongside Pump.fun, Raydium, and Meteora modules
AI Agent Kit
AI can execute swaps through natural language commands
API Reference
For detailed API documentation, see:The Swap Module provides the foundation for all token trading activities in your Solana app, offering users the best possible prices through intelligent multi-DEX routing while maintaining a simple and intuitive interface.