A comprehensive, production-ready TypeScript SDK for the Zerion API. Build powerful DeFi applications with easy access to portfolio data, token information, swap functionality, NFT data, gas monitoring, and cross-chain analytics.
- Complete API Coverage: Full support for all Zerion API endpoints
- TypeScript First: Built with TypeScript for excellent developer experience and type safety
- Production Ready: Robust error handling, automatic retries, intelligent caching
- Modern Architecture: Clean, modular design with service-based architecture
- Advanced Features: Batch operations, real-time monitoring, cross-chain analysis
- Zero Dependencies: Only requires axios for HTTP requests
- Comprehensive Documentation: Detailed docs, examples, and API reference
npm install zerion-sdk-tsyarn add zerion-sdk-tspnpm add zerion-sdk-tsimport { ZerionSDK } from 'zerion-sdk-ts';
// Initialize the SDK
const zerion = new ZerionSDK({
apiKey: 'zk_prod_your_api_key_here', // Get your API key from Zerion
timeout: 30000,
retries: 3
});
// Get wallet portfolio
async function getPortfolio() {
try {
const address = '0x742d35Cc6634C0532925a3b8D3Ac2FF2c6CEF9C9';
const portfolio = await zerion.wallets.getPortfolio(address);
console.log('Portfolio:', portfolio.data);
} catch (error) {
console.error('Error:', error.message);
}
}
getPortfolio();The SDK is organized into focused services for better maintainability and ease of use:
ZerionSDK
βββ wallets - Portfolio, transactions, positions, P&L, charts
βββ fungibles - Token data, charts, search, market data
βββ chains - Blockchain information and metadata
βββ swap - DEX aggregation and cross-chain bridges
βββ nfts - NFT data, metadata, and collections
βββ gas - Gas price monitoring and estimation
interface ZerionConfig {
apiKey: string; // Your Zerion API key (required)
baseURL?: string; // API base URL (default: 'https://api.zerion.io')
timeout?: number; // Request timeout in ms (default: 30000)
retries?: number; // Number of retries (default: 3)
retryDelay?: number; // Delay between retries in ms (default: 1000)
}
const zerion = new ZerionSDK({
apiKey: 'zk_prod_your_api_key',
timeout: 45000,
retries: 5,
retryDelay: 2000
});// Switch between production and testnet
zerion.setEnvironment('testnet'); // Use testnet
zerion.setEnvironment('production'); // Use production (default)
// Adjust configuration at runtime
zerion.setTimeout(60000); // Set timeout to 60 seconds
zerion.setRetries(3, 1500); // 3 retries with 1.5s delayThe wallet service provides comprehensive portfolio and transaction analysis.
// Get basic portfolio
const portfolio = await zerion.wallets.getPortfolio(address, {
positions?: 'only_simple' | 'only_complex' | 'no_filter'
});
// Get comprehensive portfolio analysis
const analysis = await zerion.getPortfolioAnalysis(address);
// Returns: { summary, positions, nftPortfolio, pnl, recentActivity, chainDistribution, topAssets }
// Get wallet summary (one-call overview)
const summary = await zerion.wallets.getWalletSummary(address);
// Returns: { portfolio, pnl, nftPortfolio, topPositions, recentTransactions }// Get positions with filtering
const positions = await zerion.wallets.getPositions(address, {
filter: {
positions?: 'only_simple' | 'only_complex' | 'no_filter';
position_types?: ('wallet' | 'deposited' | 'borrowed' | 'locked' | 'staked' | 'claimable' | 'liquidity')[];
chain_ids?: string[];
fungible_ids?: string[];
dapp_ids?: string[];
trash?: 'only_trash' | 'only_non_trash' | 'no_filter';
};
sort?: 'value,desc' | 'value,asc' | 'relative_changes.24h,desc' | 'relative_changes.24h,asc';
page?: {
after?: string;
before?: string;
size?: number; // Max 100
};
});
// Get all positions with auto-pagination
const allPositions = await zerion.wallets.getAllPositions(address, {
filter?: { /* same as above */ }
});// Get transactions with filtering
const transactions = await zerion.wallets.getTransactions(address, {
filter?: {
search_query?: string;
operation_types?: ('trade' | 'send' | 'receive' | 'deposit' | 'withdraw' | 'borrow' | 'repay' | 'stake' | 'unstake' | 'claim')[];
asset_types?: ('fungible' | 'nft')[];
chain_ids?: string[];
fungible_ids?: string[];
min_mined_at?: number; // Unix timestamp
max_mined_at?: number; // Unix timestamp
trash?: 'only_trash' | 'only_non_trash' | 'no_filter';
fungible_implementations?: string[]; // Format: 'chain:address'
};
page?: { /* pagination options */ };
});
// Get all transactions with auto-pagination
const allTransactions = await zerion.wallets.getAllTransactions(address);
// Analyze transaction patterns
const patterns = await zerion.analyzeTransactionPatterns(address, {
start: Date.now() - (30 * 24 * 60 * 60 * 1000), // 30 days ago
end: Date.now()
});
// Returns: { totalTransactions, averageDaily, commonOperations, gasSpent, preferredChains }// Get Profit & Loss data
const pnl = await zerion.wallets.getPnL(address, {
filter?: {
chain_ids?: string[];
fungible_ids?: string[];
}
});
// Returns unrealized_gain, realized_gain, net_invested, total_fee, etc.
// Get balance chart
const chart = await zerion.wallets.getChart(
address,
'hour' | 'day' | 'week' | 'month' | 'year' | 'max',
{
filter?: {
chain_ids?: string[];
fungible_ids?: string[];
}
}
);// Get NFT positions
const nftPositions = await zerion.wallets.getNFTPositions(address, {
filter?: {
chain_ids?: string[];
collections_ids?: string[];
};
sort?: 'last_acquired_at,desc' | 'last_acquired_at,asc' | 'last_price,desc' | 'last_price,asc';
include?: string[];
page?: { /* pagination */ };
});
// Get all NFT positions with auto-pagination
const allNFTPositions = await zerion.wallets.getAllNFTPositions(address);
// Get NFT collections
const collections = await zerion.wallets.getNFTCollections(address, {
filter?: {
chain_ids?: string[];
};
include?: string[];
page?: { /* pagination */ };
});
// Get NFT portfolio overview
const nftPortfolio = await zerion.wallets.getNFTPortfolio(address);The fungibles service handles token data, market information, and price charts.
// Search tokens by name/symbol
const tokens = await zerion.fungibles.searchFungibles('USDC', {
limit?: number;
chainId?: string;
});
// Get fungibles with advanced filtering
const fungibles = await zerion.fungibles.getFungibles({
filter?: {
search_query?: string;
implementation_chain_id?: string;
implementation_address?: string;
fungible_ids?: string[];
};
sort?: 'market_data.market_cap,desc' | 'market_data.market_cap,asc' | 'market_data.percent_change_24h,desc' | 'market_data.percent_change_24h,asc';
page?: { /* pagination */ };
});
// Get all fungibles with auto-pagination
const allFungibles = await zerion.fungibles.getAllFungibles();// Get specific token details
const token = await zerion.fungibles.getFungible(fungibleId);
// Get multiple tokens by IDs
const tokens = await zerion.fungibles.getFungiblesByIds(['id1', 'id2', 'id3']);
// Get token by contract address
const token = await zerion.fungibles.getFungibleByAddress(chainId, contractAddress);// Get top tokens by market cap
const topTokens = await zerion.fungibles.getTopFungibles(limit?: number);
// Get trending tokens
const trending = await zerion.fungibles.getTrendingFungibles(
period?: '1h' | '24h' | '7d' | '30d',
limit?: number
);
// Get tokens by specific chain
const chainTokens = await zerion.fungibles.getFungiblesByChain(
chainId: string,
options?: {
limit?: number;
sort?: 'market_data.market_cap,desc' | 'market_data.market_cap,asc';
}
);// Get price chart for token
const chart = await zerion.fungibles.getFungibleChart(
fungibleId: string,
period: 'hour' | 'day' | 'week' | 'month' | 'year' | 'max'
);
// Get price history for multiple periods
const priceHistory = await zerion.fungibles.getFungiblePriceHistory(
fungibleId: string,
periods?: ('day' | 'week' | 'month')[]
);
// Returns: Record<ChartPeriod, WalletChartData | null>// Compare multiple tokens
const comparison = await zerion.fungibles.compareFungibles(['id1', 'id2', 'id3']);
// Returns: { fungibles, comparison: { marketCaps, percentChanges24h, prices } }
// Cross-chain asset analysis
const analysis = await zerion.getCrossChainAssetAnalysis('USDC');
// Returns: { implementations, priceVariance, liquidityAnalysis, bridgeOptions }The chains service provides blockchain network information and metadata.
// Get all supported chains
const chains = await zerion.chains.getAllChains(useCache?: boolean);
// Get specific chain by ID
const chain = await zerion.chains.getChain(chainId: string);
// Get chain by external ID (e.g., Ethereum chain ID)
const chain = await zerion.chains.getChainByExternalId(externalId: string);// Get popular/major chains
const popularChains = await zerion.chains.getPopularChains();
// Get Layer 2 chains
const l2Chains = await zerion.chains.getL2Chains();
// Get mainnet chains (exclude testnets)
const mainnetChains = await zerion.chains.getMainnetChains();
// Get trading-enabled chains
const tradingChains = await zerion.chains.getTradingChains();
// Get EVM compatible chains
const evmChains = await zerion.chains.getEVMChains();// Search chains by name
const ethereumChains = await zerion.chains.findChainsByName('ethereum');
// Validate chain ID
const isValid = await zerion.chains.isValidChainId(chainId);
// Get chain display information
const displayInfo = await zerion.chains.getChainDisplayInfo(chainId);
// Returns: { name, icon?, externalId, supportsTrading }// Get comprehensive chain statistics
const stats = await zerion.chains.getChainStats();
// Returns: { total, mainnet, testnet, trading, l2 }
// Refresh chain cache
await zerion.chains.refreshCache();The swap service handles DEX aggregation, cross-chain bridges, and swap routing.
// Get available tokens for swapping
const swapTokens = await zerion.swap.getSwapFungibles({
input?: { chain_id: string; address?: string };
output?: { chain_id: string; address?: string };
direction?: 'input' | 'output' | 'both';
});
// Get input tokens for specific output chain
const inputTokens = await zerion.swap.getInputFungibles(outputChainId);
// Get output tokens for specific input chain
const outputTokens = await zerion.swap.getOutputFungibles(inputChainId);
// Get bridge tokens (cross-chain)
const bridgeTokens = await zerion.swap.getBridgeFungibles(inputChainId, outputChainId);// Get swap offers
const offers = await zerion.swap.getSwapOffers({
input: {
chain_id: string;
address?: string;
amount: string;
};
output: {
chain_id: string;
address?: string;
};
gas_price?: string;
liquidity_source_id?: string;
sort?: 'amount' | 'gas_fee';
slippage_percent?: number;
integrator?: {
id?: string;
fee_recipient?: string;
fee_percent?: number;
};
});
// Get best swap offer
const bestOffer = await zerion.swap.getBestSwapOffer(swapParams);
// Get gas-optimized offers
const gasOptimizedOffers = await zerion.swap.getGasOptimizedOffers(swapParams);// Compare swap rates across providers
const rateComparison = await zerion.swap.compareSwapRates(swapParams);
// Returns: { offers, bestRate, worstRate, averageRate, rateSpread }
// Test different slippage tolerances
const slippageTests = await zerion.swap.estimateSwapWithSlippage(swapParams);
// Returns: Record<string, SwapOffer | null> for different slippage %
// Get cross-chain swap options
const crossChainOptions = await zerion.swap.getCrossChainSwapOptions(
fromChainId: string,
toChainId: string,
tokenSymbol?: string
);
// Returns: { availableTokens, sampleOffers }
// Calculate swap impact and fees
const impact = await zerion.swap.calculateSwapImpact(swapParams);
// Returns: { offer, priceImpact, gasFee, protocolFee, totalCost }// Get quote for exact input amount
const quote = await zerion.swap.getQuoteExactIn(
inputToken: { chainId: string; address?: string },
outputToken: { chainId: string; address?: string },
inputAmount: string,
options?: { slippage?: number; gasPrice?: string }
);
// Check if tokens can be swapped
const canSwap = await zerion.swap.canSwap(inputToken, outputToken);
// Get swap route information
const route = await zerion.swap.getSwapRoute(swapParams);
// Returns: { route, hops, protocols, estimatedGas }
// Validate swap parameters
const validation = zerion.swap.validateSwapParams(swapParams);
// Returns: { isValid, errors }
// Find best swap route (high-level helper)
const bestRoute = await zerion.findBestSwapRoute(fromToken, toToken, amount, options);
// Returns: { bestOffer, allOffers, route, gasEstimate }The NFT service handles NFT data, metadata, and collection information.
// Get NFT by ID
const nft = await zerion.nfts.getNFT(nftId, { include?: string[] });
// Get NFTs by references
const nfts = await zerion.nfts.getNFTs({
filter: {
references: string[]; // Format: 'chain:contract:tokenId'
};
include?: string[];
});
// Get NFT by chain, contract, and token ID
const nft = await zerion.nfts.getNFTByReference(
chainId: string,
contractAddress: string,
tokenId: string,
options?: { include?: string[] }
);
// Get multiple NFTs by references
const nfts = await zerion.nfts.getNFTsByReferences(
references: string[],
options?: { include?: string[] }
);// Batch get NFTs with chunking
const nfts = await zerion.nfts.batchGetNFTs(
references: string[],
options?: {
include?: string[];
chunkSize?: number; // Default: 50
}
);
// Get NFTs from specific collection
const collectionNFTs = await zerion.nfts.getNFTsFromCollection(
chainId: string,
contractAddress: string,
tokenIds: string[],
options?: { include?: string[] }
);
// Safe NFT retrieval with error handling
const { success, failed } = await zerion.nfts.getNFTsSafely(references);// Get NFT with enhanced metadata
const nftWithMetadata = await zerion.nfts.getNFTWithMetadata(nftId);
// Returns: { nft, metadata: { hasImage, hasVideo, hasAudio, contentUrls, estimatedValue } }
// Get collection summary
const collectionSummary = await zerion.nfts.getCollectionSummary(
chainId: string,
contractAddress: string,
sampleTokenIds: string[]
);
// Returns: { collectionInfo, sampleNFTs, totalSampled }
// Check if NFT exists
const exists = await zerion.nfts.nftExists(chainId, contractAddress, tokenId);// Create NFT reference
const reference = zerion.nfts.createReference(chainId, contractAddress, tokenId);
// Parse NFT reference
const { chainId, contractAddress, tokenId } = zerion.nfts.parseReference(reference);
// Validate NFT reference format
const validation = zerion.nfts.validateNFTReference(reference);
// Returns: { isValid, parsed?, error? }The gas service provides gas price monitoring, estimation, and optimization.
// Get gas prices for all chains
const gasPrices = await zerion.gas.getGasPrices({
filter?: {
chain_ids?: string[];
gas_types?: ('classic' | 'fast' | 'instant')[];
}
});
// Get gas prices for specific chain
const chainGasPrices = await zerion.gas.getChainGasPrices(
chainId: string,
useCache?: boolean
);
// Get specific gas type for chain
const gasPrice = await zerion.gas.getChainGasPrice(
chainId: string,
gasType?: 'classic' | 'fast' | 'instant'
);
// Get all gas types as object
const gasPricesByType = await zerion.gas.getChainGasPricesByType(chainId);
// Returns: { classic?, fast?, instant? }// Get gas prices for multiple chains
const multiChainGas = await zerion.gas.getMultiChainGasPrices(chainIds);
// Compare gas prices across chains
const comparison = await zerion.gas.compareGasPricesAcrossChains(
chainIds: string[],
gasType?: 'classic' | 'fast' | 'instant'
);
// Returns: { prices, cheapest, fastest }// Get gas recommendations
const recommendations = await zerion.gas.getGasRecommendations(chainId);
// Returns: { recommended, options: { classic, fast, instant } }
// Get optimal gas price for urgency
const optimalGas = await zerion.gas.getOptimalGasPrice(
chainId: string,
urgency?: 'low' | 'medium' | 'high'
);
// Get gas price trends (mock implementation)
const trends = await zerion.gas.getGasPriceTrends(chainId);
// Returns: { current, trend, recommendation }// Estimate transaction cost
const cost = await zerion.gas.estimateTransactionCost(
chainId: string,
gasLimit: string,
gasType?: 'classic' | 'fast' | 'instant'
);
// Returns: { gasPrice, gasLimit, totalCostWei, estimatedTimeMinutes }
// Calculate gas fee in different units
const feeUnits = await zerion.gas.calculateGasFeeInNativeToken(
chainId: string,
gasLimit: string,
gasType?: 'classic' | 'fast' | 'instant'
);
// Returns: { feeWei, feeEther, feeGwei }// Monitor gas prices (async generator)
for await (const gasPrices of zerion.gas.monitorGasPrices(chainId, intervalMs)) {
console.log('Current gas prices:', gasPrices);
// Break when needed
}
// Get gas price alerts
const alerts = await zerion.gas.getGasPriceAlerts(chainId, {
low?: string; // Wei threshold
high?: string; // Wei threshold
});
// Returns: { currentPrice, alerts: [{ type, threshold, triggered, message }] }
// Cache management
zerion.gas.clearCache();
const cacheStats = zerion.gas.getCacheStats();
// Returns: { size, keys, oldestEntry?, newestEntry? }// Batch get wallet summaries
const summaries = await zerion.batchGetWalletSummaries([
'0x742d35Cc6634C0532925a3b8D3Ac2FF2c6CEF9C9',
'0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'
]);
// Multi-wallet aggregation
const aggregation = await zerion.getMultiWalletAggregation(addresses);
// Returns: { totalValue, totalPnL, combinedPositions, topChains, topAssets }// Monitor wallet activity
const monitor = zerion.monitorWalletActivity(address, intervalMs);
for await (const activity of monitor) {
console.log('New transactions:', activity.newTransactions.length);
console.log('Portfolio changes:', activity.portfolioChange);
console.log('Alerts:', activity.alerts);
// Break when needed
if (someCondition) break;
}// Get comprehensive market overview
const market = await zerion.getMarketOverview();
// Returns: { topAssets, trending, chains, totalChains }
// Cross-chain asset analysis
const analysis = await zerion.getCrossChainAssetAnalysis('USDC');
// Returns: { implementations, priceVariance, liquidityAnalysis, bridgeOptions }
// Transaction pattern analysis
const patterns = await zerion.analyzeTransactionPatterns(address, timeRange);
// Returns: { totalTransactions, averageDaily, mostActiveDay, commonOperations, gasSpent, preferredChains }// Get SDK health status
const health = await zerion.getHealthStatus();
// Returns: { status: 'healthy' | 'degraded' | 'unhealthy', services, responseTime }The SDK includes comprehensive utility functions for common operations:
import {
isValidAddress,
isValidApiKey,
normalizeAddress,
buildQueryParams,
createNFTReference,
parseNFTReference,
formatNumber,
formatTimestamp,
parseTimestamp,
validateRequired,
chunk,
debounce,
calculatePercentageChange,
safeJsonParse
} from '@zerion/sdk';
// Address validation and normalization
const isValid = isValidAddress('0x742d35Cc6634C0532925a3b8D3Ac2FF2c6CEF9C9');
const normalized = normalizeAddress('0x742D35CC6634C0532925A3B8D3AC2FF2C6CEF9C9');
// API key validation
const isValidKey = isValidApiKey('zk_prod_123456789');
// Query parameter building
const query = buildQueryParams({
filter: { chain_ids: ['ethereum', 'polygon'] },
page: { size: 10 }
});
// NFT reference handling
const ref = createNFTReference('ethereum', contractAddress, tokenId);
const { chainId, contractAddress, tokenId } = parseNFTReference(ref);
// Number and date formatting
const formatted = formatNumber(1234567.89, 2); // "1.23M"
const timestamp = formatTimestamp(Date.now() / 1000);
const parsed = parseTimestamp('2023-01-01T00:00:00Z');
// Array utilities
const chunks = chunk([1, 2, 3, 4, 5], 2); // [[1, 2], [3, 4], [5]]
// Percentage calculation
const change = calculatePercentageChange(100, 120); // 20The SDK provides comprehensive error handling with detailed error information:
import { ZerionAPIError } from '@zerion/sdk';
try {
const portfolio = await zerion.wallets.getPortfolio(address);
} catch (error) {
if (error instanceof ZerionAPIError) {
console.log('Error code:', error.code);
console.log('HTTP status:', error.status);
console.log('Error details:', error.details);
} else {
console.log('Unexpected error:', error.message);
}
}- ZerionAPIError: API-related errors with HTTP status codes
- Network errors: Connection and timeout issues
- Validation errors: Invalid parameters or configuration
- Rate limiting: When API limits are exceeded
async function analyzePortfolio(address: string) {
const analysis = await zerion.getPortfolioAnalysis(address);
console.log('Portfolio Analysis:');
console.log(`Total Value: $${analysis.summary.total_value}`);
console.log(`Positions: ${analysis.positions.length}`);
console.log(`Top Assets:`, analysis.topAssets.slice(0, 5));
console.log(`Chain Distribution:`, analysis.chainDistribution);
console.log(`P&L: $${analysis.pnl.unrealized_gain}`);
return analysis;
}async function researchToken(symbol: string) {
// Search for token
const tokens = await zerion.fungibles.searchFungibles(symbol);
if (tokens.length === 0) {
console.log(`No tokens found for ${symbol}`);
return;
}
const token = tokens[0];
// Get detailed information
const details = await zerion.fungibles.getFungible(token.id);
const chart = await zerion.fungibles.getFungibleChart(token.id, 'week');
const crossChain = await zerion.getCrossChainAssetAnalysis(symbol);
console.log('Token Research:', {
name: details.data.attributes.name,
symbol: details.data.attributes.symbol,
price: details.data.attributes.market_data?.price,
marketCap: details.data.attributes.market_data?.market_cap,
change24h: details.data.attributes.market_data?.percent_change_24h,
implementations: crossChain.implementations.length,
priceVariance: crossChain.priceVariance
});
}async function findBestCrossChainSwap(
fromToken: { chainId: string; address: string },
toToken: { chainId: string; address: string },
amount: string
) {
// Check if cross-chain swap is possible
const canSwap = await zerion.swap.canSwap(fromToken, toToken);
if (!canSwap) {
console.log('Cross-chain swap not available');
return;
}
// Get best route
const route = await zerion.findBestSwapRoute(fromToken, toToken, amount, {
slippage: 1
});
if (route) {
console.log('Best Cross-Chain Swap:');
console.log(`Input: ${route.bestOffer.attributes.send_quantity.float}`);
console.log(`Output: ${route.bestOffer.attributes.receive_quantity.float}`);
console.log(`Rate: ${route.bestOffer.attributes.receive_quantity.float / route.bestOffer.attributes.send_quantity.float}`);
console.log(`Gas: ${route.gasEstimate?.totalCostWei} wei`);
console.log(`Route:`, route.route);
}
return route;
}async function optimizeGasUsage(chainId: string, gasLimit: string) {
// Get current gas prices
const gasPrices = await zerion.gas.getChainGasPricesByType(chainId);
// Get recommendations
const recommendations = await zerion.gas.getGasRecommendations(chainId);
// Calculate costs for different speeds
const costs = await Promise.all([
zerion.gas.estimateTransactionCost(chainId, gasLimit, 'classic'),
zerion.gas.estimateTransactionCost(chainId, gasLimit, 'fast'),
zerion.gas.estimateTransactionCost(chainId, gasLimit, 'instant')
]);
console.log('Gas Optimization Analysis:');
costs.forEach((cost, i) => {
const types = ['classic', 'fast', 'instant'];
if (cost) {
console.log(`${types[i]}: ${cost.totalCostWei} wei (${cost.estimatedTimeMinutes} min)`);
}
});
console.log(`Recommended: ${recommendations?.recommended}`);
return {
gasPrices,
recommendations,
costs: costs.filter(Boolean)
};
}async function analyzeNFTCollection(
chainId: string,
contractAddress: string,
sampleTokenIds: string[]
) {
// Get collection summary
const summary = await zerion.nfts.getCollectionSummary(
chainId,
contractAddress,
sampleTokenIds
);
// Get detailed NFT data
const nfts = await zerion.nfts.getNFTsFromCollection(
chainId,
contractAddress,
sampleTokenIds
);
// Analyze metadata
const metadataAnalysis = await Promise.all(
nfts.map(nft => zerion.nfts.getNFTWithMetadata(nft.id))
);
const hasImages = metadataAnalysis.filter(m => m?.metadata.hasImage).length;
const hasVideos = metadataAnalysis.filter(m => m?.metadata.hasVideo).length;
console.log('NFT Collection Analysis:', {
name: summary.collectionInfo?.name,
totalSampled: summary.totalSampled,
hasImages: `${hasImages}/${nfts.length}`,
hasVideos: `${hasVideos}/${nfts.length}`,
avgContentUrls: metadataAnalysis.reduce((sum, m) =>
sum + (m?.metadata.contentUrls.length || 0), 0) / nfts.length
});
return {
summary,
nfts,
metadataAnalysis: metadataAnalysis.filter(Boolean)
};
}async function monitorDeFiActivity(addresses: string[], duration: number = 300000) {
console.log(`Starting DeFi monitoring for ${duration/1000} seconds...`);
const monitors = addresses.map(address => ({
address,
monitor: zerion.monitorWalletActivity(address, 30000) // Check every 30s
}));
const startTime = Date.now();
const activities: Record<string, any[]> = {};
// Initialize activity arrays
addresses.forEach(addr => activities[addr] = []);
try {
await Promise.race([
// Monitor each wallet
...monitors.map(async ({ address, monitor }) => {
for await (const activity of monitor) {
activities[address].push({
timestamp: activity.timestamp,
newTransactions: activity.newTransactions.length,
alerts: activity.alerts
});
console.log(`[${new Date().toLocaleTimeString()}] ${address.slice(0, 8)}... - ${activity.newTransactions.length} new tx, ${activity.alerts.length} alerts`);
if (Date.now() - startTime > duration) break;
}
}),
// Timeout after specified duration
new Promise(resolve => setTimeout(resolve, duration))
]);
} catch (error) {
console.error('Monitoring error:', error);
}
// Generate summary
console.log('\nMonitoring Summary:');
Object.entries(activities).forEach(([address, activity]) => {
const totalTx = activity.reduce((sum, a) => sum + a.newTransactions, 0);
const totalAlerts = activity.reduce((sum, a) => sum + a.alerts.length, 0);
console.log(`${address.slice(0, 8)}...: ${totalTx} transactions, ${totalAlerts} alerts`);
});
return activities;
}The SDK automatically handles pagination for large datasets:
// These methods automatically paginate through all results
const allPositions = await zerion.wallets.getAllPositions(address);
const allTransactions = await zerion.wallets.getAllTransactions(address);
const allNFTPositions = await zerion.wallets.getAllNFTPositions(address);
const allFungibles = await zerion.fungibles.getAllFungibles();
// Manual pagination with control
let cursor: string | undefined;
const allResults: any[] = [];
do {
const response = await zerion.wallets.getPositions(address, {
page: { after: cursor, size: 100 }
});
allResults.push(...response.data);
cursor = response.links.next ?
new URL(response.links.next).searchParams.get('page[after]') || undefined :
undefined;
} while (cursor);// Batch wallet analysis
const walletAddresses = [
'0x742d35Cc6634C0532925a3b8D3Ac2FF2c6CEF9C9',
'0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
'0x47ac0Fb4F2D84898e4D9E7b4DaB3C24507a6D503'
];
const batchResults = await zerion.batchGetWalletSummaries(walletAddresses);
// Batch NFT retrieval with error handling
const nftReferences = [
'ethereum:0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d:1',
'ethereum:0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d:2',
'ethereum:0x60e4d786628fea6478f785a6d7e704777c86a7c6:1'
];
const { success, failed } = await zerion.nfts.getNFTsSafely(nftReferences);
console.log(`Successfully retrieved: ${success.length}, Failed: ${failed.length}`);
// Batch token comparison
const tokenIds = ['ethereum-usdc', 'ethereum-usdt', 'ethereum-dai'];
const comparison = await zerion.fungibles.compareFungibles(tokenIds);// Chains service has built-in caching
const chains1 = await zerion.chains.getAllChains(); // API call
const chains2 = await zerion.chains.getAllChains(); // From cache (faster)
// Force refresh cache
await zerion.chains.refreshCache();
// Gas service caching
const gasStats = zerion.gas.getCacheStats();
console.log('Cache stats:', gasStats);
// Clear gas cache when needed
zerion.gas.clearCache();// Execute multiple independent requests in parallel
const [portfolio, positions, transactions, gasPrices] = await Promise.all([
zerion.wallets.getPortfolio(address),
zerion.wallets.getPositions(address, { page: { size: 20 } }),
zerion.wallets.getTransactions(address, { page: { size: 10 } }),
zerion.gas.getChainGasPricesByType('ethereum')
]);
// Parallel token analysis
const tokenPromises = ['USDC', 'USDT', 'DAI'].map(symbol =>
zerion.fungibles.searchFungibles(symbol, { limit: 1 })
);
const tokenResults = await Promise.all(tokenPromises);// Use specific filters to reduce data transfer
const positions = await zerion.wallets.getPositions(address, {
filter: {
position_types: ['wallet'], // Only wallet positions
chain_ids: ['ethereum'], // Only Ethereum
trash: 'only_non_trash' // Exclude spam
},
page: { size: 50 } // Reasonable page size
});
// Use include parameter for NFTs when you need related data
const nfts = await zerion.wallets.getNFTPositions(address, {
include: ['collection', 'chain']
});// β
Good: Use environment variables
const zerion = new ZerionSDK({
apiKey: process.env.ZERION_API_KEY!
});
// β Bad: Hardcode API keys
const zerion = new ZerionSDK({
apiKey: 'zk_prod_your_actual_key_here'
});
// β
Good: Validate API key format
import { isValidApiKey } from '@zerion/sdk';
const apiKey = process.env.ZERION_API_KEY;
if (!apiKey || !isValidApiKey(apiKey)) {
throw new Error('Invalid or missing Zerion API key');
}import { isValidAddress, normalizeAddress } from '@zerion/sdk';
function safeAddressHandler(address: string) {
// Always validate addresses
if (!isValidAddress(address)) {
throw new Error(`Invalid Ethereum address: ${address}`);
}
// Normalize for consistency
return normalizeAddress(address);
}import { ZerionAPIError } from '@zerion/sdk';
async function safeApiCall<T>(apiCall: () => Promise<T>): Promise<T | null> {
try {
return await apiCall();
} catch (error) {
if (error instanceof ZerionAPIError) {
// Handle API errors appropriately
if (error.status === 429) {
console.log('Rate limited, waiting before retry...');
await new Promise(resolve => setTimeout(resolve, 5000));
return safeApiCall(apiCall); // Retry once
} else if (error.status === 404) {
console.log('Resource not found');
return null;
} else {
console.error('API Error:', error.message);
throw error;
}
} else {
console.error('Unexpected error:', error);
throw error;
}
}
}// test/wallet.service.test.ts
import { ZerionSDK } from '@zerion/sdk';
describe('Wallet Service', () => {
let zerion: ZerionSDK;
beforeEach(() => {
zerion = new ZerionSDK({
apiKey: process.env.ZERION_TEST_API_KEY!
});
});
test('should get wallet portfolio', async () => {
const address = '0x742d35Cc6634C0532925a3b8D3Ac2FF2c6CEF9C9';
const portfolio = await zerion.wallets.getPortfolio(address);
expect(portfolio.data).toBeDefined();
expect(portfolio.data.type).toBe('portfolio');
expect(portfolio.links.self).toContain(address);
});
test('should handle invalid address', async () => {
await expect(
zerion.wallets.getPortfolio('invalid_address')
).rejects.toThrow();
});
});// test/integration.test.ts
describe('Integration Tests', () => {
test('should perform complete portfolio analysis', async () => {
const address = process.env.TEST_WALLET_ADDRESS!;
const analysis = await zerion.getPortfolioAnalysis(address);
expect(analysis.summary).toBeDefined();
expect(analysis.positions).toBeInstanceOf(Array);
expect(analysis.chainDistribution).toBeInstanceOf(Object);
expect(analysis.topAssets).toBeInstanceOf(Array);
});
});// hooks/useZerion.ts
import { useState, useEffect } from 'react';
import { ZerionSDK } from '@zerion/sdk';
const zerion = new ZerionSDK({
apiKey: process.env.REACT_APP_ZERION_API_KEY!
});
export function useWalletPortfolio(address: string) {
const [portfolio, setPortfolio] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
if (!address) return;
const fetchPortfolio = async () => {
try {
setLoading(true);
const result = await zerion.getPortfolioAnalysis(address);
setPortfolio(result);
setError(null);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
fetchPortfolio();
}, [address]);
return { portfolio, loading, error };
}
// Component usage
function WalletDashboard({ address }: { address: string }) {
const { portfolio, loading, error } = useWalletPortfolio(address);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h1>Portfolio Value: ${portfolio?.summary?.total_value}</h1>
<p>Positions: {portfolio?.positions?.length}</p>
</div>
);
}// routes/portfolio.ts
import express from 'express';
import { ZerionSDK } from '@zerion/sdk';
const router = express.Router();
const zerion = new ZerionSDK({
apiKey: process.env.ZERION_API_KEY!
});
router.get('/portfolio/:address', async (req, res) => {
try {
const { address } = req.params;
const portfolio = await zerion.getPortfolioAnalysis(address);
res.json(portfolio);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
router.get('/tokens/search', async (req, res) => {
try {
const { q, limit = 10 } = req.query;
const tokens = await zerion.fungibles.searchFungibles(q as string, {
limit: parseInt(limit as string)
});
res.json(tokens);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
export default router;// pages/api/wallet/[address]/portfolio.ts
import type { NextApiRequest, NextApiResponse } from 'next';
import { ZerionSDK } from '@zerion/sdk';
const zerion = new ZerionSDK({
apiKey: process.env.ZERION_API_KEY!
});
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== 'GET') {
return res.status(405).json({ message: 'Method not allowed' });
}
const { address } = req.query;
if (!address || typeof address !== 'string') {
return res.status(400).json({ message: 'Invalid address parameter' });
}
try {
const portfolio = await zerion.getPortfolioAnalysis(address);
// Cache for 5 minutes
res.setHeader('Cache-Control', 's-maxage=300, stale-while-revalidate');
res.status(200).json(portfolio);
} catch (error) {
console.error('Portfolio API error:', error);
res.status(500).json({ message: 'Internal server error' });
}
}# .env (Production)
ZERION_API_KEY=zk_prod_your_production_key_here
ZERION_TIMEOUT=30000
ZERION_RETRIES=3
# .env.development
ZERION_API_KEY=zk_dev_your_development_key_here
ZERION_TIMEOUT=10000
ZERION_RETRIES=1# Dockerfile
FROM node:18-alpine
WORKDIR /app
# Copy package files
COPY package*.json ./
RUN npm ci --only=production
# Copy application code
COPY . .
# Build application
RUN npm run build
# Set environment
ENV NODE_ENV=production
# Expose port
EXPOSE 3000
# Start application
CMD ["npm", "start"]// config/zerion.ts
import { ZerionSDK } from '@zerion/sdk';
export const createZerionClient = () => {
return new ZerionSDK({
apiKey: process.env.ZERION_API_KEY!,
timeout: parseInt(process.env.ZERION_TIMEOUT || '30000'),
retries: parseInt(process.env.ZERION_RETRIES || '3'),
retryDelay: parseInt(process.env.ZERION_RETRY_DELAY || '1000')
});
};
// Use connection pooling for high-traffic applications
let zerionInstance: ZerionSDK | null = null;
export const getZerionClient = (): ZerionSDK => {
if (!zerionInstance) {
zerionInstance = createZerionClient();
}
return zerionInstance;
};// health-check.ts
import { ZerionSDK } from '@zerion/sdk';
export async function healthCheck(): Promise<{
status: 'healthy' | 'unhealthy';
timestamp: string;
services: Record<string, boolean>;
}> {
const zerion = new ZerionSDK({
apiKey: process.env.ZERION_API_KEY!,
timeout: 5000 // Short timeout for health checks
});
try {
const health = await zerion.getHealthStatus();
return {
status: health.status === 'healthy' ? 'healthy' : 'unhealthy',
timestamp: new Date().toISOString(),
services: health.services
};
} catch (error) {
return {
status: 'unhealthy',
timestamp: new Date().toISOString(),
services: {}
};
}
}// metrics.ts
import { ZerionSDK } from '@zerion/sdk';
class ZerionMetrics {
private requestCount = 0;
private errorCount = 0;
private responseTimeSum = 0;
async trackRequest<T>(operation: () => Promise<T>): Promise<T> {
const startTime = Date.now();
this.requestCount++;
try {
const result = await operation();
this.responseTimeSum += Date.now() - startTime;
return result;
} catch (error) {
this.errorCount++;
throw error;
}
}
getMetrics() {
return {
totalRequests: this.requestCount,
totalErrors: this.errorCount,
errorRate: this.requestCount > 0 ? this.errorCount / this.requestCount : 0,
averageResponseTime: this.requestCount > 0 ? this.responseTimeSum / this.requestCount : 0
};
}
reset() {
this.requestCount = 0;
this.errorCount = 0;
this.responseTimeSum = 0;
}
}
export const metrics = new ZerionMetrics();// Problem: Invalid API key format
const zerion = new ZerionSDK({
apiKey: 'invalid_key' // β Wrong format
});
// Solution: Use correct format
import { isValidApiKey } from '@zerion/sdk';
const apiKey = process.env.ZERION_API_KEY;
if (!isValidApiKey(apiKey)) {
throw new Error('Invalid API key format. Must be zk_prod_... or zk_dev_...');
}// Problem: Getting 429 errors
try {
const portfolio = await zerion.wallets.getPortfolio(address);
} catch (error) {
if (error instanceof ZerionAPIError && error.status === 429) {
// Solution: Implement exponential backoff
await new Promise(resolve => setTimeout(resolve, 5000));
return zerion.wallets.getPortfolio(address); // Retry
}
}// Problem: Requests timing out
const zerion = new ZerionSDK({
apiKey: process.env.ZERION_API_KEY!,
timeout: 5000, // β Too short for complex requests
retries: 1 // β Too few retries
});
// Solution: Increase timeout and retries
const zerion = new ZerionSDK({
apiKey: process.env.ZERION_API_KEY!,
timeout: 30000, // β
30 seconds
retries: 3, // β
3 retries
retryDelay: 2000 // β
2 second delay between retries
});// Problem: Loading too much data at once
const allPositions = await zerion.wallets.getAllPositions(address); // β Might be huge
// Solution: Use pagination
const positions = await zerion.wallets.getPositions(address, {
page: { size: 50 }, // β
Reasonable page size
filter: {
trash: 'only_non_trash' // β
Filter out spam
}
});// Enable debug logging
process.env.NODE_ENV = 'development';
// The SDK will log requests in development mode
const portfolio = await zerion.wallets.getPortfolio(address);
// Output: [Zerion SDK] β GET /v1/wallets/.../portfolio
// Output: [Zerion SDK] β 200 GET /v1/wallets/.../portfolioMIT License - see LICENSE file for details.
Contributions are welcome! Please read our Contributing Guide for details on our code of conduct and the process for submitting pull requests.
Built with β€οΈ by the @noFAYZ