Decentralized price queries from DEXes across multiple chains
Installation | Quick Start | Core Concepts | API Reference | Usage Guides
Finding the best price across multiple DEXes and chains is complex. Different protocols, different APIs, different pool structures. dexap abstracts all of this complexity into a clean, type-safe interface.
- Truly Decentralized - Query prices directly from onchain liquidity pools instead of relying on centralized price oracles. Keep your Web3 stack decentralized end-to-end.
- One API, Many DEXes - Query UniswapV3-based DEXes (Uniswap, SushiSwap, PancakeSwap) and Slipstream DEXes (Velodrome, Aerodrome) with identical syntax
- 11 Chains Supported - Ethereum, Base, Optimism, Arbitrum, Polygon, BSC, Avalanche, and more
- Best Price Discovery - Automatically find the optimal DEX for your trade
- Price Aggregation - Get average, median, min/max prices with outlier filtering
- TypeScript First - Full type definitions with IntelliSense support
- Minimal Dependencies - Only requires
viemas a peer dependency - Symbol Resolution - Use token symbols directly, no need to look up addresses
# npm
npm install dexap viem
# yarn
yarn add dexap viem
# pnpm
pnpm add dexap viemNote:
viemis a peer dependency and must be installed alongside dexap.
import { createClient, ChainId, DexType } from "dexap";
const client = createClient({
alchemyKey: "your-api-key", // Optional
});
// Get a price quote
const result = await client.getPrice(
"WETH",
"USDC",
"1",
ChainId.ETHEREUM,
DexType.UNISWAP_V3
);
console.log(result.formatted); // "1 WETH = 3,245.67 USDC"See Usage Guides for more examples.
dexap supports two types of concentrated liquidity protocols with different pool configurations:
Used by: Uniswap V3, SushiSwap V3, PancakeSwap V3
Pool Tiers: Based on fee percentages
100= 0.01% fee (stablecoin pairs like USDC/USDT)500= 0.05% fee (correlated pairs like ETH/WETH)3000= 0.30% fee (standard pairs like ETH/USDC)10000= 1.00% fee (volatile/exotic pairs)
Available on: All 11 chains
Used by: Velodrome (Optimism), Aerodrome (Base)
Pool Tiers: Based on tick spacing
1= Tightest spacing (stablecoin pairs)50= Tight spacing (correlated assets)100= Standard spacing (common pairs)200= Wide spacing (volatile pairs)2000= Widest spacing (exotic pairs)
Key Difference: Slipstream is Velodrome's concentrated liquidity implementation optimized for OP Stack chains. It's NOT a UniswapV3 fork - it uses tick spacing parameters instead of fee tiers for pool configuration.
Available on: Optimism (Velodrome), Base (Aerodrome)
Important: dexap abstracts these protocol differences completely. You use the same API methods regardless of whether you're querying UniswapV3 or Slipstream pools. The
poolTierin results will show the appropriate tier type for each protocol.
dexap automatically handles protocol selection based on your query method:
Best Price Mode (getBestPrice)
- Queries all available DEXes on the specified chain
- Returns the best price with DEX identifier
- Optimal for finding the most favorable rate
Specific DEX Mode (getPrice)
- Queries a single specified DEX
- Use when you need prices from a particular protocol
- Faster than querying all DEXes
Aggregation Mode (getAggregatedPrice)
- Queries all DEXes and provides statistical analysis
- Returns average, median, min, max prices
- Includes outlier filtering (IQR method)
- Best for market overview and price discovery
Check availability: Use getSupportedDexTypes(chainId) to find which DEXes are available on a specific chain.
dexap includes a built-in token registry that simplifies token handling:
- Automatic resolution - Use symbols like
"WETH","USDC"instead of addresses - Multi-chain support - Same symbol works across all supported chains
- Decimal handling - Automatically handles tokens with different decimals per chain
- Common tokens - WETH, WBTC, USDC, USDT, DAI, and more pre-configured
Creates a dexap client instance for querying DEX prices.
Parameters:
interface ClientConfig {
alchemyKey?: string; // Alchemy API key
infuraKey?: string; // Infura API key
rpcUrls?: Partial<Record<ChainKey, string>>; // Custom RPC URLs
}Returns: Client instance
Example:
const client = createClient({
alchemyKey: "your-key",
});Get a price quote from a specific DEX.
Parameters:
| Parameter | Type | Description |
|---|---|---|
tokenIn |
string | TokenInfo |
Input token symbol or address |
tokenOut |
string | TokenInfo |
Output token symbol or address |
amount |
string |
Amount in human-readable format (e.g., "1.5") |
chainId |
ChainId |
Target blockchain |
dexType |
DexType |
Specific DEX to query |
Returns: Promise<PriceResult>
Find the best price across all available DEXes on a chain.
Parameters: Same as getPrice except no dexType parameter
Returns: Promise<PriceResult & { dexType: DexType }>
The result includes a dexType field indicating which DEX provided the best price.
Get price quotes from all available DEXes on a chain.
Parameters: Same as getPrice except no dexType parameter
Returns: Promise<Array<PriceResult & { dexType: DexType }>>
Returns an array of price results, one for each available DEX.
Get aggregated price statistics across all DEXes with optional outlier filtering.
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
tokenIn |
string | TokenInfo |
- | Input token |
tokenOut |
string | TokenInfo |
- | Output token |
amount |
string |
- | Amount to query |
chainId |
ChainId |
- | Target chain |
filterOutliers |
boolean |
true |
Apply IQR outlier filtering |
Returns: Promise<AggregatedPrice>
Utility functions for working with tokens across chains.
Resolve a token symbol to chain-specific address and decimals.
function resolveToken(symbol: string, chainId: ChainId): TokenInfo;Get token metadata without specifying a chain.
function getTokenMetadata(symbol: string): TokenMetadata | undefined;Returns name, symbol, and decimals (chain-agnostic).
Find which chains support a specific token.
function getSupportedChains(symbol: string): ChainId[];Get all tokens available on a specific chain.
function getChainTokens(chainId: ChainId): TokenInfo[];Get the wrapped native token for a chain (WETH, WBNB, WMATIC, etc.).
function getWrappedNativeToken(chainId: ChainId): TokenInfo | undefined;Utility functions for discovering and checking DEX availability.
Get all DEX types available on a specific chain.
function getSupportedDexTypes(chainId: ChainId): DexType[];
// Example
const baseDexes = getSupportedDexTypes(ChainId.BASE);
// => [DexType.UNISWAP_V3, DexType.SUSHISWAP_V3,
// DexType.PANCAKESWAP_V3, DexType.AERODROME]Get protocol metadata for a DEX type.
function getDexProtocol(dexType: DexType): DexProtocol;
// Example
const protocol = getDexProtocol(DexType.VELODROME);
// => { type: "velodrome", name: "Velodrome", website: "https://velodrome.finance" }Check if a specific DEX is available on a chain.
function isDexSupported(chainId: ChainId, dexType: DexType): boolean;
// Example
const supported = isDexSupported(ChainId.BASE, DexType.VELODROME);
// => false (Velodrome is only on Optimism)The result of a price query.
interface PriceResult {
tokenIn: TokenInfo; // Input token details
tokenOut: TokenInfo; // Output token details
amountIn: string; // Input amount in wei
amountOut: string; // Output amount in wei
price: number; // Numeric price (tokenOut per tokenIn)
formatted: string; // Human-readable: "1 WETH = 3,245.67 USDC"
poolTier: PoolTier; // Pool tier information
chainId: ChainId; // Chain identifier
gasEstimate: string; // Estimated gas in wei
priceImpact: number; // Price impact percentage
}Aggregated price statistics across multiple DEXes.
interface AggregatedPrice {
average: number; // Average price
median: number; // Median price
min: number; // Minimum price
max: number; // Maximum price
best: PriceResult & { dexType: DexType }; // Best price with DEX
all: Array<PriceResult & { dexType: DexType }>; // All prices
tokenIn: TokenInfo;
tokenOut: TokenInfo;
chainId: ChainId;
timestamp: number; // Query timestamp
}Pool tier information (fee or tick spacing).
interface PoolTier {
type: "fee" | "tickSpacing"; // Tier type
value: number; // Fee (e.g., 3000) or tick spacing (e.g., 100)
display: string; // Human-readable: "0.30% fee" or "100 tick spacing"
}Token information including address and decimals.
interface TokenInfo {
symbol: string; // Token symbol (e.g., "WETH")
address: `0x${string}`; // Contract address
decimals: number; // Token decimals
}enum ChainId {
ETHEREUM = 1,
BSC = 56,
POLYGON = 137,
ARBITRUM = 42161,
AVALANCHE = 43114,
OPTIMISM = 10,
BASE = 8453,
ZORA = 7777777,
UNICHAIN = 130,
WORLD_CHAIN = 480,
SONEIUM = 1868,
}
enum DexType {
UNISWAP_V3 = "uniswap-v3",
SUSHISWAP_V3 = "sushiswap-v3",
PANCAKESWAP_V3 = "pancakeswap-v3",
VELODROME = "velodrome",
AERODROME = "aerodrome",
}Scenario: Check the current WETH/USDC price on Ethereum.
import { createClient, ChainId, DexType } from "dexap";
const client = createClient({
alchemyKey: "your-api-key",
});
const result = await client.getPrice(
"WETH",
"USDC",
"1",
ChainId.ETHEREUM,
DexType.UNISWAP_V3
);
console.log(result.formatted); // "1 WETH = 3,245.67 USDC"
console.log(result.poolTier.display); // "0.30% fee"
console.log(result.price); // 3245.67
console.log(result.priceImpact); // 0.0234
console.log(result.gasEstimate); // "150000"Scenario: Find which DEX offers the best WETH price on Ethereum.
import { createClient, ChainId } from "dexap";
const client = createClient({ alchemyKey: "your-api-key" });
// Option 1: Get best price directly
const best = await client.getBestPrice("WETH", "USDC", "1", ChainId.ETHEREUM);
console.log(`Best: ${best.formatted}`);
console.log(`DEX: ${best.dexType}`);
console.log(`Pool: ${best.poolTier.display}`);
// Option 2: Compare all DEXes manually
const allPrices = await client.getPricesFromAllDexes(
"WETH",
"USDC",
"1",
ChainId.ETHEREUM
);
allPrices.forEach((quote) => {
console.log(
`${quote.dexType}: ${quote.formatted} (impact: ${quote.priceImpact.toFixed(
4
)}%)`
);
});Scenario: Check WETH/USDC price across multiple chains.
import { createClient, ChainId, DexType } from "dexap";
const client = createClient({ alchemyKey: "your-api-key" });
const chains = [
ChainId.ETHEREUM,
ChainId.BASE,
ChainId.ARBITRUM,
ChainId.OPTIMISM,
ChainId.POLYGON,
];
const prices = await Promise.allSettled(
chains.map((chainId) =>
client.getPrice("WETH", "USDC", "1", chainId, DexType.UNISWAP_V3)
)
);
prices.forEach((result, i) => {
if (result.status === "fulfilled") {
const chainName = ChainId[chains[i]];
console.log(`${chainName}: $${result.value.price.toFixed(2)}`);
} else {
console.log(`${ChainId[chains[i]]}: Error - ${result.reason}`);
}
});Scenario: Get statistical analysis of WETH price across all DEXes on Base.
import { createClient, ChainId } from "dexap";
const client = createClient({ alchemyKey: "your-api-key" });
const agg = await client.getAggregatedPrice(
"WETH",
"USDC",
"1",
ChainId.BASE,
true // Filter outliers
);
console.log(`Average: $${agg.average.toFixed(2)}`);
console.log(`Median: $${agg.median.toFixed(2)}`);
console.log(`Range: $${agg.min.toFixed(2)} - $${agg.max.toFixed(2)}`);
console.log(`Best: ${agg.best.formatted} (${agg.best.dexType})`);
console.log(`Spread: ${(((agg.max - agg.min) / agg.min) * 100).toFixed(2)}%`);
// Analyze individual DEX prices
agg.all.forEach((quote) => {
const spread = ((quote.price - agg.min) / agg.min) * 100;
console.log(
`${quote.dexType}: $${quote.price.toFixed(2)} (+${spread.toFixed(
2
)}% above min)`
);
});Scenario: Discover token availability and resolve addresses.
import {
resolveToken,
getTokenMetadata,
getSupportedChains,
getChainTokens,
getWrappedNativeToken,
ChainId,
} from "dexap";
// Get token metadata (chain-agnostic)
const meta = getTokenMetadata("WETH");
console.log(`${meta.name} - ${meta.decimals} decimals`);
// Find which chains support USDC
const usdcChains = getSupportedChains("USDC");
console.log(`USDC available on ${usdcChains.length} chains`);
// Get token address for specific chain
const weth = resolveToken("WETH", ChainId.BASE);
console.log(`WETH on Base: ${weth.address}`);
// Get wrapped native token
const wrapped = getWrappedNativeToken(ChainId.BSC);
console.log(`BSC wrapped native: ${wrapped.symbol}`); // "WBNB"
// List all tokens on a chain
const baseTokens = getChainTokens(ChainId.BASE);
console.log(`Tokens on Base: ${baseTokens.map((t) => t.symbol).join(", ")}`);Scenario: Configure RPC providers for optimal performance.
import { createClient } from "dexap";
// Use Alchemy
const alchemyClient = createClient({
alchemyKey: "your-alchemy-key",
});
// Use Infura
const infuraClient = createClient({
infuraKey: "your-infura-key",
});
// Mix custom RPCs with API keys
const customClient = createClient({
alchemyKey: "your-key", // Use Alchemy for most chains
rpcUrls: {
mainnet: "https://your-private-eth-rpc.com", // Override Ethereum
base: "https://your-private-base-rpc.com", // Override Base
},
});
// Public RPCs only (rate limited, not recommended for production)
const publicClient = createClient();Scenario: Handle common errors gracefully.
import { createClient, ChainId, DexType } from "dexap";
const client = createClient({ alchemyKey: "your-api-key" });
try {
const result = await client.getPrice(
"WETH",
"USDC",
"1",
ChainId.ETHEREUM,
DexType.UNISWAP_V3
);
console.log("Price:", result.formatted);
} catch (error) {
if (error instanceof Error) {
if (
error.message.includes("Token") &&
error.message.includes("not found")
) {
console.log("Token not supported on this chain");
} else if (error.message.includes("not configured")) {
console.log("DEX not available on this chain");
} else if (error.message.includes("No liquidity found")) {
console.log("No liquidity available for this pair");
} else {
console.error("Unexpected error:", error.message);
}
}
}Common error patterns:
Token "${symbol}" not found on chain ${chainId}- Token not deployed on this chainDEX ${dexType} not configured for chain ${chainId}- DEX not available on this chainNo liquidity found for ${tokenIn}/${tokenOut} on ${dexType}- Insufficient liquidity
| Chain | Chain ID | DEXes Available |
|---|---|---|
| Ethereum | 1 |
UniswapV3, SushiSwapV3, PancakeSwapV3 |
| Base | 8453 |
UniswapV3, SushiSwapV3, PancakeSwapV3, Aerodrome |
| Optimism | 10 |
UniswapV3, SushiSwapV3, Velodrome |
| Arbitrum | 42161 |
UniswapV3, SushiSwapV3, PancakeSwapV3 |
| Polygon | 137 |
UniswapV3, SushiSwapV3 |
| BNB Smart Chain | 56 |
UniswapV3, SushiSwapV3, PancakeSwapV3 |
| Avalanche | 43114 |
UniswapV3, SushiSwapV3 |
| Zora | 7777777 |
UniswapV3 |
| Unichain | 130 |
UniswapV3 |
| World Chain | 480 |
UniswapV3 |
| Soneium | 1868 |
UniswapV3 |
| DEX | Protocol | Pool Tiers | Chains |
|---|---|---|---|
| Uniswap V3 | UniswapV3 | Fee-based (0.01% - 1.00%) | All 11 chains |
| SushiSwap V3 | UniswapV3 | Fee-based (0.01% - 1.00%) | Ethereum, Base, Optimism, Arbitrum, Polygon, BSC, Avalanche |
| PancakeSwap V3 | UniswapV3 | Fee-based (0.01% - 1.00%) | Ethereum, Base, Arbitrum, BSC |
| Velodrome | Slipstream | Tick Spacing (1 - 2000) | Optimism |
| Aerodrome | Slipstream | Tick Spacing (1 - 2000) | Base |