examples - saltict/Demo-Docs GitHub Wiki
Comprehensive examples and code samples for integrating the SubWallet Services SDK into your applications.
📖 Navigation
- Quick Start Examples
- Service-Specific Examples
- Platform Integration Examples
- Advanced Usage Patterns
- Error Handling Examples
- Real-World Use Cases
import subwalletApiSdk from '@subwallet-monorepos/subwallet-services-sdk';
// Configure the SDK
subwalletApiSdk.updateConfig({
platform: 'webapp'
});
// Simple price history fetch
async function getTokenPrice() {
try {
const priceHistory = await subwalletApiSdk.priceHistoryApi.getPriceHistory('DOT', '1W');
console.log('Price data points:', priceHistory.history.length);
console.log('Latest price:', priceHistory.history[priceHistory.history.length - 1]);
} catch (error) {
console.error('Failed to fetch price:', error);
}
}
getTokenPrice();
// Check SDK version and availability
function sdkHealthCheck() {
console.log('SDK Version:', subwalletApiSdk.version);
console.log('Platform Headers:', subwalletApiSdk.headers);
// Check service availability
const services = {
balanceDetection: !!subwalletApiSdk.balanceDetectionApi,
priceHistory: !!subwalletApiSdk.priceHistoryApi,
swap: !!subwalletApiSdk.swapApi,
xcm: !!subwalletApiSdk.xcmApi,
cardano: !!subwalletApiSdk.cardanoApi
};
console.log('Available Services:', services);
return services;
}
sdkHealthCheck();
Get historical price data for tokens with different timeframes.
// Get price history for different timeframes
async function priceHistoryExamples() {
try {
// 1 Day price history
const dailyPrices = await subwalletApiSdk.priceHistoryApi.getPriceHistory('DOT', '1D');
console.log('Daily prices (24h):', dailyPrices.history.length, 'data points');
// 1 Week price history
const weeklyPrices = await subwalletApiSdk.priceHistoryApi.getPriceHistory('KSM', '1W');
console.log('Weekly prices:', weeklyPrices.history.length, 'data points');
// 1 Month price history
const monthlyPrices = await subwalletApiSdk.priceHistoryApi.getPriceHistory('ACA', '1M');
console.log('Monthly prices:', monthlyPrices.history.length, 'data points');
// Year-to-date price history
const ytdPrices = await subwalletApiSdk.priceHistoryApi.getPriceHistory('GLMR', 'YTD');
console.log('YTD prices:', ytdPrices.history.length, 'data points');
} catch (error) {
console.error('Price history error:', error);
}
}
// Price change calculation
function calculatePriceChange(priceHistory: any) {
const history = priceHistory.history;
if (history.length < 2) return null;
const latest = history[history.length - 1].value;
const previous = history[0].value;
const change = ((latest - previous) / previous) * 100;
return {
latest,
previous,
change: change.toFixed(2),
direction: change > 0 ? 'up' : change < 0 ? 'down' : 'stable'
};
}
// Usage
priceHistoryExamples();
Detect token balances for EVM addresses.
// Get EVM token balance slugs
async function balanceDetectionExamples() {
try {
const address = '0x742d35Cc6565C0532F3E4db7c5F804E5D5eDeF94';
// Get token slugs for the address
const tokenSlugs = await subwalletApiSdk.balanceDetectionApi.getEvmTokenBalanceSlug(address);
console.log('Token slugs found:', tokenSlugs);
console.log('Number of tokens:', tokenSlugs.length);
return tokenSlugs;
} catch (error) {
console.error('Balance detection error:', error);
}
}
// Batch balance detection for multiple addresses
async function batchBalanceDetection() {
const addresses = [
'0x742d35Cc6565C0532F3E4db7c5F804E5D5eDeF94',
'0x8ba1f109551bD432803012645Hac136c7c5DeF95',
'0x123f567801bD432803012645Hac136c7c5Ab123'
];
try {
const balancePromises = addresses.map(address =>
subwalletApiSdk.balanceDetectionApi.getEvmTokenBalanceSlug(address)
);
const results = await Promise.allSettled(balancePromises);
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`Address ${addresses[index]}: ${result.value.length} tokens`);
} else {
console.error(`Address ${addresses[index]} failed:`, result.reason);
}
});
} catch (error) {
console.error('Batch balance detection error:', error);
}
}
balanceDetectionExamples();
Cross-chain transfer operations within the Polkadot ecosystem.
// XCM transfer example
async function xcmTransferExample() {
try {
const xcmRequest = {
address: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', // Sender address
from: 'polkadot', // Source chain
to: 'acala', // Destination chain
recipient: '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty', // Recipient address
value: '1000000000000' // Amount in smallest unit (10 DOT)
};
const xcmData = await subwalletApiSdk.xcmApi.fetchXcmData(xcmRequest);
console.log('XCM Transfer Data:', {
sender: xcmData.sender,
to: xcmData.to,
value: xcmData.value,
encodedCall: xcmData.transferEncodedCall.substring(0, 50) + '...' // Truncate for display
});
return xcmData;
} catch (error) {
console.error('XCM transfer error:', error);
}
}
// XCM fee estimation helper
async function estimateXcmFee(from: string, to: string, amount: string) {
try {
const xcmRequest = {
address: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
from,
to,
recipient: '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
value: amount
};
const xcmData = await subwalletApiSdk.xcmApi.fetchXcmData(xcmRequest);
// Extract fee information from metadata if available
const fee = xcmData.metadata?.fee || 'Fee information not available';
return {
from,
to,
amount,
estimatedFee: fee,
transferCall: xcmData.transferEncodedCall
};
} catch (error) {
console.error('XCM fee estimation error:', error);
throw error;
}
}
xcmTransferExample();
Cardano-specific transaction building.
// Cardano transaction building
async function cardanoTransactionExample() {
try {
const txRequest = {
sender: 'addr1qx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3jcu5d8ps7zex2k2xt3uqxgjqnnj0vs2qd4a6gtmydwfqq5e8a0m',
receiver: 'addr1qy93ktfeuwxm4k7k9u8xy6qxh0vlalq8a5q2kxj4rtj3v2ycu5d8ps7zex2k2xt3uqxgjqnnj0vs2qd4a6gtmydwfqq5dcd7',
unit: 'lovelace', // ADA in lovelace (1 ADA = 1,000,000 lovelace)
quantity: '2000000' // 2 ADA
};
const unsignedPayload = await subwalletApiSdk.cardanoApi.fetchUnsignedPayload(txRequest);
console.log('Cardano Unsigned Transaction:', {
sender: txRequest.sender,
receiver: txRequest.receiver,
amount: `${parseFloat(txRequest.quantity) / 1000000} ADA`,
payload: unsignedPayload.substring(0, 100) + '...' // Truncate for display
});
return unsignedPayload;
} catch (error) {
console.error('Cardano transaction error:', error);
}
}
// Cardano native token transfer
async function cardanoNativeTokenTransfer() {
try {
const nativeTokenTx = {
sender: 'addr1qx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3jcu5d8ps7zex2k2xt3uqxgjqnnj0vs2qd4a6gtmydwfqq5e8a0m',
receiver: 'addr1qy93ktfeuwxm4k7k9u8xy6qxh0vlalq8a5q2kxj4rtj3v2ycu5d8ps7zex2k2xt3uqxgjqnnj0vs2qd4a6gtmydwfqq5dcd7',
unit: 'a0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235.484f534b59', // Example native token
quantity: '1000'
};
const payload = await subwalletApiSdk.cardanoApi.fetchUnsignedPayload(nativeTokenTx);
console.log('Native Token Transfer Payload:', payload);
return payload;
} catch (error) {
console.error('Native token transfer error:', error);
}
}
cardanoTransactionExample();
Token swap operations and route finding.
// Note: Swap API implementation details may vary based on actual service endpoints
// This example shows the expected usage pattern
async function swapOperationExample() {
try {
// Example swap request (actual interface may differ based on implementation)
const swapParams = {
tokenIn: 'DOT',
tokenOut: 'USDT',
amountIn: '10',
slippage: '0.5', // 0.5% slippage tolerance
recipient: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'
};
// Note: Actual method names and parameters depend on implementation
console.log('Swap request prepared:', swapParams);
// The SwapApi is available but specific methods depend on backend implementation
console.log('Swap API available:', !!subwalletApiSdk.swapApi);
} catch (error) {
console.error('Swap operation error:', error);
}
}
swapOperationExample();
Complete React component examples with hooks and state management.
// React hook for price data
import React, { useState, useEffect } from 'react';
import subwalletApiSdk from '@subwallet-monorepos/subwallet-services-sdk';
interface PriceData {
token: string;
timeframe: string;
history: Array<{ time: number; value: number }>;
loading: boolean;
error: string | null;
}
export const usePriceHistory = (token: string, timeframe: string) => {
const [priceData, setPriceData] = useState<PriceData>({
token,
timeframe,
history: [],
loading: true,
error: null
});
useEffect(() => {
let isMounted = true;
const fetchPriceHistory = async () => {
try {
setPriceData(prev => ({ ...prev, loading: true, error: null }));
const result = await subwalletApiSdk.priceHistoryApi.getPriceHistory(token, timeframe);
if (isMounted) {
setPriceData(prev => ({
...prev,
history: result.history,
loading: false
}));
}
} catch (error) {
if (isMounted) {
setPriceData(prev => ({
...prev,
loading: false,
error: error instanceof Error ? error.message : 'Unknown error'
}));
}
}
};
fetchPriceHistory();
return () => {
isMounted = false;
};
}, [token, timeframe]);
return priceData;
};
// React component using the hook
export const PriceChart: React.FC<{ token: string; timeframe: string }> = ({ token, timeframe }) => {
const { history, loading, error } = usePriceHistory(token, timeframe);
if (loading) return <div>Loading price data...</div>;
if (error) return <div>Error: {error}</div>;
if (history.length === 0) return <div>No price data available</div>;
const currentPrice = history[history.length - 1]?.value;
const previousPrice = history[0]?.value;
const priceChange = currentPrice && previousPrice ?
((currentPrice - previousPrice) / previousPrice * 100).toFixed(2) : '0';
return (
<div className="price-chart">
<h3>{token} Price Chart</h3>
<div className="price-info">
<span className="current-price">${currentPrice?.toFixed(2)}</span>
<span className={`price-change ${parseFloat(priceChange) >= 0 ? 'positive' : 'negative'}`}>
{parseFloat(priceChange) >= 0 ? '+' : ''}{priceChange}%
</span>
</div>
<div className="chart-container">
{/* Chart implementation would go here */}
<div>Chart with {history.length} data points</div>
</div>
</div>
);
};
// Vue 3 Composition API example
import { ref, computed, onMounted, watch } from 'vue';
import subwalletApiSdk from '@subwallet-monorepos/subwallet-services-sdk';
export default {
setup() {
const balances = ref([]);
const loading = ref(false);
const error = ref(null);
const walletAddress = ref('');
const fetchBalances = async () => {
if (!walletAddress.value) return;
loading.value = true;
error.value = null;
try {
const tokenSlugs = await subwalletApiSdk.balanceDetectionApi.getEvmTokenBalanceSlug(walletAddress.value);
balances.value = tokenSlugs;
} catch (err) {
error.value = err.message;
} finally {
loading.value = false;
}
};
const totalTokens = computed(() => balances.value.length);
watch(walletAddress, fetchBalances);
onMounted(() => {
// Configure SDK for web app
subwalletApiSdk.updateConfig({ platform: 'webapp' });
});
return {
balances,
loading,
error,
walletAddress,
totalTokens,
fetchBalances
};
}
};
// Background script (service worker)
// background.js
import subwalletApiSdk from '@subwallet-monorepos/subwallet-services-sdk';
// Configure for extension environment
subwalletApiSdk.updateConfig({
platform: 'extension'
});
// Handle messages from content script or popup
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
const handleApiRequest = async () => {
try {
let result;
switch (request.action) {
case 'getPriceHistory':
result = await subwalletApiSdk.priceHistoryApi.getPriceHistory(
request.token,
request.timeframe
);
break;
case 'getBalances':
result = await subwalletApiSdk.balanceDetectionApi.getEvmTokenBalanceSlug(
request.address
);
break;
case 'buildXcmTransfer':
result = await subwalletApiSdk.xcmApi.fetchXcmData(request.xcmRequest);
break;
default:
throw new Error(`Unknown action: ${request.action}`);
}
sendResponse({ success: true, data: result });
} catch (error) {
sendResponse({ success: false, error: error.message });
}
};
handleApiRequest();
return true; // Keep message channel open for async response
});
// Popup script
// popup.js
function sendMessageToBackground(action, params) {
return new Promise((resolve, reject) => {
chrome.runtime.sendMessage({ action, ...params }, (response) => {
if (response.success) {
resolve(response.data);
} else {
reject(new Error(response.error));
}
});
});
}
// Usage in popup
async function displayTokenPrices() {
try {
const priceData = await sendMessageToBackground('getPriceHistory', {
token: 'DOT',
timeframe: '1D'
});
document.getElementById('price-display').textContent =
`Current Price: $${priceData.history[priceData.history.length - 1].value}`;
} catch (error) {
console.error('Failed to fetch price:', error);
}
}
// Advanced error handling with exponential backoff
class APIRetryHandler {
async retryWithBackoff<T>(
apiCall: () => Promise<T>,
maxRetries: number = 3,
baseDelay: number = 1000
): Promise<T> {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await apiCall();
} catch (error) {
if (attempt === maxRetries) {
throw new Error(`API call failed after ${maxRetries} attempts: ${error.message}`);
}
const delay = baseDelay * Math.pow(2, attempt - 1);
console.log(`Attempt ${attempt} failed, retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw new Error('Unexpected error in retry logic');
}
}
const retryHandler = new APIRetryHandler();
// Usage with retry logic
async function robustPriceHistoryFetch(token: string, timeframe: string) {
return retryHandler.retryWithBackoff(
() => subwalletApiSdk.priceHistoryApi.getPriceHistory(token, timeframe),
3, // Max 3 retries
1000 // Start with 1 second delay
);
}
// Batch operations with rate limiting
class BatchProcessor {
constructor(private rateLimit: number = 5, private interval: number = 1000) {}
async processBatch<T, R>(
items: T[],
processor: (item: T) => Promise<R>,
onProgress?: (completed: number, total: number) => void
): Promise<R[]> {
const results: R[] = [];
const batches = this.createBatches(items, this.rateLimit);
for (let i = 0; i < batches.length; i++) {
const batch = batches[i];
const batchPromises = batch.map(processor);
const batchResults = await Promise.allSettled(batchPromises);
batchResults.forEach((result, index) => {
if (result.status === 'fulfilled') {
results.push(result.value);
} else {
console.error(`Batch item ${index} failed:`, result.reason);
results.push(null as any); // or handle error appropriately
}
});
if (onProgress) {
onProgress(results.length, items.length);
}
// Wait before next batch (except for last batch)
if (i < batches.length - 1) {
await new Promise(resolve => setTimeout(resolve, this.interval));
}
}
return results;
}
private createBatches<T>(items: T[], batchSize: number): T[][] {
const batches: T[][] = [];
for (let i = 0; i < items.length; i += batchSize) {
batches.push(items.slice(i, i + batchSize));
}
return batches;
}
}
// Usage
const batchProcessor = new BatchProcessor(3, 2000); // 3 requests per 2 seconds
async function batchPriceHistoryFetch(tokens: string[]) {
return batchProcessor.processBatch(
tokens,
token => subwalletApiSdk.priceHistoryApi.getPriceHistory(token, '1D'),
(completed, total) => {
console.log(`Progress: ${completed}/${total} tokens processed`);
}
);
}
// Fetch price data for multiple tokens
const tokens = ['DOT', 'KSM', 'ACA', 'GLMR', 'ASTR'];
batchPriceHistoryFetch(tokens).then(results => {
console.log('All price data fetched:', results);
});
// Advanced caching with TTL and storage backends
interface CacheEntry<T> {
data: T;
timestamp: number;
ttl: number;
}
class AdvancedCache {
private memoryCache = new Map<string, CacheEntry<any>>();
constructor(
private defaultTTL: number = 60000, // 1 minute
private useLocalStorage: boolean = true
) {}
async get<T>(
key: string,
fetcher: () => Promise<T>,
customTTL?: number
): Promise<T> {
const ttl = customTTL || this.defaultTTL;
// Check memory cache first
const memoryEntry = this.memoryCache.get(key);
if (memoryEntry && this.isValid(memoryEntry)) {
return memoryEntry.data;
}
// Check localStorage cache
if (this.useLocalStorage) {
const storageEntry = this.getFromStorage<T>(key);
if (storageEntry && this.isValid(storageEntry)) {
// Restore to memory cache
this.memoryCache.set(key, storageEntry);
return storageEntry.data;
}
}
// Fetch fresh data
const data = await fetcher();
const entry: CacheEntry<T> = {
data,
timestamp: Date.now(),
ttl
};
// Store in caches
this.memoryCache.set(key, entry);
if (this.useLocalStorage) {
this.setToStorage(key, entry);
}
return data;
}
private isValid<T>(entry: CacheEntry<T>): boolean {
return Date.now() - entry.timestamp < entry.ttl;
}
private getFromStorage<T>(key: string): CacheEntry<T> | null {
try {
const item = localStorage.getItem(`subwallet-cache-${key}`);
return item ? JSON.parse(item) : null;
} catch {
return null;
}
}
private setToStorage<T>(key: string, entry: CacheEntry<T>): void {
try {
localStorage.setItem(`subwallet-cache-${key}`, JSON.stringify(entry));
} catch (error) {
console.warn('Failed to store in localStorage:', error);
}
}
clear(): void {
this.memoryCache.clear();
if (this.useLocalStorage) {
const keys = Object.keys(localStorage).filter(key =>
key.startsWith('subwallet-cache-')
);
keys.forEach(key => localStorage.removeItem(key));
}
}
}
// Create cached API wrapper
const cache = new AdvancedCache();
const cachedSDK = {
priceHistory: {
getPriceHistory: (token: string, timeframe: string) =>
cache.get(
`price-${token}-${timeframe}`,
() => subwalletApiSdk.priceHistoryApi.getPriceHistory(token, timeframe),
300000 // 5 minutes for price data
)
},
balanceDetection: {
getEvmTokenBalanceSlug: (address: string) =>
cache.get(
`balance-${address}`,
() => subwalletApiSdk.balanceDetectionApi.getEvmTokenBalanceSlug(address),
60000 // 1 minute for balance data
)
}
};
// Usage
const priceData = await cachedSDK.priceHistory.getPriceHistory('DOT', '1W');
Complete portfolio tracking application example.
// Portfolio dashboard implementation
interface TokenHolding {
symbol: string;
balance: number;
priceData: any;
value: number;
change24h: number;
}
class PortfolioDashboard {
private cache = new AdvancedCache(300000); // 5 minute cache
async getPortfolioData(walletAddress: string): Promise<{
totalValue: number;
holdings: TokenHolding[];
totalChange24h: number;
}> {
try {
// Get detected tokens for the address
const tokenSlugs = await this.cache.get(
`tokens-${walletAddress}`,
() => subwalletApiSdk.balanceDetectionApi.getEvmTokenBalanceSlug(walletAddress)
);
// For this example, we'll map token slugs to actual tokens
// In reality, you'd need additional APIs to get balance amounts
const mockHoldings = await this.getMockHoldings(tokenSlugs);
// Get price data for each holding
const holdings: TokenHolding[] = await Promise.all(
mockHoldings.map(async (holding) => {
const priceData = await this.cache.get(
`price-${holding.symbol}-1D`,
() => subwalletApiSdk.priceHistoryApi.getPriceHistory(holding.symbol, '1D')
);
const currentPrice = priceData.history[priceData.history.length - 1]?.value || 0;
const previousPrice = priceData.history[0]?.value || 0;
const change24h = previousPrice ? ((currentPrice - previousPrice) / previousPrice) * 100 : 0;
return {
...holding,
priceData,
value: holding.balance * currentPrice,
change24h
};
})
);
const totalValue = holdings.reduce((sum, holding) => sum + holding.value, 0);
const totalChange24h = holdings.length > 0 ?
holdings.reduce((sum, holding) => sum + holding.change24h, 0) / holdings.length : 0;
return {
totalValue,
holdings,
totalChange24h
};
} catch (error) {
console.error('Portfolio data fetch failed:', error);
throw error;
}
}
private async getMockHoldings(tokenSlugs: string[]) {
// Mock implementation - in reality, you'd get actual balance data
return tokenSlugs.slice(0, 5).map((slug, index) => ({
symbol: ['DOT', 'KSM', 'ACA', 'GLMR', 'ASTR'][index] || 'UNKNOWN',
balance: Math.random() * 1000 + 10 // Mock balance
}));
}
}
// Usage
const portfolio = new PortfolioDashboard();
const portfolioData = await portfolio.getPortfolioData('0x742d35Cc6565C0532F3E4db7c5F804E5D5eDeF94');
console.log('Portfolio Value:', portfolioData.totalValue);
console.log('24h Change:', portfolioData.totalChange24h);
// Cross-chain transfer utility
class CrossChainTransferHelper {
async estimateTransferCost(
from: string,
to: string,
amount: string,
token: string = 'DOT'
) {
try {
// Get current token price for USD estimation
const priceData = await subwalletApiSdk.priceHistoryApi.getPriceHistory(token, '1D');
const currentPrice = priceData.history[priceData.history.length - 1]?.value || 0;
// Build XCM transfer to get fee estimation
const xcmRequest = {
address: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', // Mock address
from,
to,
recipient: '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty', // Mock recipient
value: amount
};
const xcmData = await subwalletApiSdk.xcmApi.fetchXcmData(xcmRequest);
// Extract fee from metadata (implementation depends on actual API response)
const estimatedFee = xcmData.metadata?.fee || '0';
const feeInToken = parseFloat(estimatedFee) / Math.pow(10, 10); // Assuming 10 decimal places
const feeInUSD = feeInToken * currentPrice;
return {
from,
to,
amount,
token,
estimatedFee: feeInToken,
estimatedFeeUSD: feeInUSD,
transferCall: xcmData.transferEncodedCall,
currentTokenPrice: currentPrice
};
} catch (error) {
console.error('Transfer cost estimation failed:', error);
throw error;
}
}
async getSupportedRoutes() {
// Mock implementation - in reality, this would come from an API
return {
polkadot: ['acala', 'moonbeam', 'astar', 'parallel'],
kusama: ['karura', 'moonriver', 'shiden', 'heiko'],
acala: ['polkadot', 'moonbeam'],
moonbeam: ['polkadot', 'acala']
};
}
}
// Usage
const transferHelper = new CrossChainTransferHelper();
const cost = await transferHelper.estimateTransferCost('polkadot', 'acala', '10000000000000');
console.log('Transfer cost:', cost);
This comprehensive examples documentation provides developers with practical, real-world usage patterns for the SubWallet Services SDK across different platforms and use cases.
🔗 Related Documentation