Skip to main content
The Quote Service provides functions for fetching bridge quotes from the LI.FI API, calculating price impact, and managing quote caching.

Exports

import {
  getQuote,
  getQuotes,
  estimatePriceImpact,
  invalidateQuoteCache,
  createQuoteCache,
  QuoteCache,
  QuoteFetchError,
  InvalidQuoteParamsError,
  NoRouteFoundError,
  isQuoteFetchError,
  isInvalidQuoteParamsError,
  isNoRouteFoundError,
} from '@mina-bridge/sdk/services/quote';

Functions

getQuote(params, integrator, autoDeposit?, cache?, timeoutMs?)

Get a single optimal bridge quote.
async function getQuote(
  params: QuoteParams,
  integrator: string,
  autoDeposit?: boolean,
  cache?: QuoteCache,
  timeoutMs?: number
): Promise<Quote>
ParameterTypeRequiredDefaultDescription
paramsQuoteParamsYes-Quote parameters
integratorstringYes-Integrator identifier
autoDepositbooleanNotrueInclude auto-deposit step
cacheQuoteCacheNo-Optional cache instance
timeoutMsnumberNo30000Request timeout in ms
QuoteParams:
ParameterTypeRequiredDefaultDescription
fromChainIdnumberYes-Source chain ID
toChainIdnumberNo999Destination chain ID (HyperEVM)
fromTokenstringYes-Source token address
toTokenstringYes-Destination token address
fromAmountstringYes-Amount in smallest unit
fromAddressstringYes-User’s wallet address
toAddressstringNofromAddressDestination address
slippageTolerancenumberNo0.5Slippage as percentage (0.5 = 0.5%)
routePreferenceRoutePreferenceNo'recommended'Route priority
Returns: Quote Throws:
  • InvalidQuoteParamsError if parameters are invalid
  • NoRouteFoundError if no route is available
  • QuoteFetchError if API request fails
Example:
const quote = await getQuote(
  {
    fromChainId: 1,
    toChainId: 999,
    fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
    toToken: '0xb88339cb7199b77e23db6e890353e22632ba630f',   // HyperEVM USDC
    fromAmount: '1000000000', // 1000 USDC (6 decimals)
    fromAddress: '0x...',
    slippageTolerance: 0.5, // 0.5%
    routePreference: 'recommended',
  },
  'my-app'
);

console.log('Quote Details:');
console.log(`  ID: ${quote.id}`);
console.log(`  Input: ${quote.fromAmount}`);
console.log(`  Output: ${quote.toAmount}`);
console.log(`  Min Received: ${quote.minimumReceivedFormatted}`);
console.log(`  Time: ${quote.estimatedTime}s`);
console.log(`  Fees: $${quote.fees.totalUsd}`);
console.log(`  Price Impact: ${(quote.priceImpact * 100).toFixed(2)}%`);
console.log(`  High Impact: ${quote.highImpact}`);
console.log(`  Expires: ${new Date(quote.expiresAt).toISOString()}`);

getQuotes(params, integrator, autoDeposit?, cache?, timeoutMs?)

Get multiple bridge quotes for comparison.
async function getQuotes(
  params: QuoteParams,
  integrator: string,
  autoDeposit?: boolean,
  cache?: QuoteCache,
  timeoutMs?: number
): Promise<QuotesResponse>
Returns: QuotesResponse
PropertyTypeDescription
quotesQuote[]Array of quotes (up to 5)
recommendedIndexnumberIndex of recommended quote
Example:
const { quotes, recommendedIndex } = await getQuotes(
  {
    fromChainId: 42161, // Arbitrum
    toChainId: 999,
    fromToken: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', // USDC
    toToken: '0xb88339cb7199b77e23db6e890353e22632ba630f',
    fromAmount: '500000000', // 500 USDC
    fromAddress: '0x...',
  },
  'my-app'
);

console.log(`Found ${quotes.length} routes`);
console.log(`Recommended: Route ${recommendedIndex + 1}`);

// Display all routes
quotes.forEach((quote, i) => {
  const isRecommended = i === recommendedIndex;
  console.log(`\nRoute ${i + 1}${isRecommended ? ' (Recommended)' : ''}:`);
  console.log(`  Bridge: ${quote.steps[0]?.tool}`);
  console.log(`  Time: ${quote.estimatedTime}s`);
  console.log(`  Output: ${quote.toAmount}`);
  console.log(`  Fees: $${quote.fees.totalUsd.toFixed(2)}`);
});

estimatePriceImpact(fromToken, toToken, fromAmount, toAmount)

Calculate price impact for a swap or bridge.
function estimatePriceImpact(
  fromToken: Token,
  toToken: Token,
  fromAmount: string,
  toAmount: string
): PriceImpactResult
ParameterTypeDescription
fromTokenTokenSource token with price
toTokenTokenDestination token with price
fromAmountstringInput amount (smallest unit)
toAmountstringOutput amount (smallest unit)
Returns: PriceImpactResult
PropertyTypeDescription
priceImpactnumberPrice impact as decimal (0.01 = 1%)
impactPercentagestringFormatted percentage string
severity'low' | 'medium' | 'high' | 'very_high'Severity level
highImpactbooleanWhether impact exceeds 1% threshold
Severity Thresholds:
SeverityImpact
low< 0.5%
medium0.5% - 1%
high1% - 3%
very_high> 3%
Example:
const impact = estimatePriceImpact(
  { ...usdcToken, priceUsd: 1.0 },
  { ...hyperliquidUsdc, priceUsd: 1.0 },
  '1000000000', // 1000 USDC
  '997000000'   // 997 USDC (0.3% impact)
);

console.log(`Price Impact: ${impact.impactPercentage}`); // "0.30%"
console.log(`Severity: ${impact.severity}`); // "low"
console.log(`High Impact: ${impact.highImpact}`); // false

if (impact.severity === 'high' || impact.severity === 'very_high') {
  console.warn('Consider using a smaller amount');
}

invalidateQuoteCache(cache?)

Manually invalidate the quote cache.
function invalidateQuoteCache(cache?: QuoteCache): void
Example:
// Invalidate default cache
invalidateQuoteCache();

// Invalidate specific cache
const myCache = createQuoteCache();
invalidateQuoteCache(myCache);

createQuoteCache()

Factory function to create a new cache instance.
function createQuoteCache(): QuoteCache
Example:
const cache = createQuoteCache();
const quote = await getQuote(params, 'my-app', true, cache);

Error Types

QuoteFetchError

Thrown when quote fetching fails.
class QuoteFetchError extends MinaError {
  readonly code: 'QUOTE_FETCH_FAILED';
  readonly recoverable: true;
  readonly recoveryAction: 'retry';
  readonly statusCode?: number;
  readonly endpoint: string;
}
Example:
try {
  const quote = await getQuote(params, 'my-app');
} catch (error) {
  if (isQuoteFetchError(error)) {
    console.error(`API Error (${error.statusCode}): ${error.message}`);
    console.log('Retry recommended');
  }
}

InvalidQuoteParamsError

Thrown when quote parameters are invalid.
class InvalidQuoteParamsError extends MinaError {
  readonly code: 'INVALID_QUOTE_PARAMS';
  readonly recoverable: false;
  readonly field: string;
  readonly reason: string;
}
Example:
try {
  const quote = await getQuote({
    fromChainId: -1, // Invalid
    ...
  }, 'my-app');
} catch (error) {
  if (isInvalidQuoteParamsError(error)) {
    console.error(`Invalid ${error.field}: ${error.reason}`);
  }
}

NoRouteFoundError

Thrown when no bridge route is available.
class NoRouteFoundError extends MinaError {
  readonly code: 'NO_ROUTE_FOUND';
  readonly recoverable: true;
  readonly recoveryAction: 'try_different_route';
  readonly fromChainId: number;
  readonly toChainId: number;
  readonly fromToken: string;
  readonly toToken: string;
}
Example:
try {
  const quote = await getQuote(params, 'my-app');
} catch (error) {
  if (isNoRouteFoundError(error)) {
    console.error(
      `No route from chain ${error.fromChainId} to ${error.toChainId}`
    );
    console.log('Try a different token or chain');
  }
}

QuoteCache Class

The QuoteCache class manages caching for quote data with a 30-second TTL.

Methods

class QuoteCache {
  /** Get cached quote if not expired (30 second TTL) */
  getQuote(params: QuoteParams): {
    data: Quote;
    cachedAt: number;
  } | null;

  /** Store quote in cache */
  setQuote(params: QuoteParams, quote: Quote): void;

  /** Get cached quotes list */
  getQuotes(params: QuoteParams): {
    data: Quote[];
    recommendedIndex: number;
    cachedAt: number;
  } | null;

  /** Store quotes list in cache */
  setQuotes(
    params: QuoteParams,
    quotes: Quote[],
    recommendedIndex: number
  ): void;

  /** Invalidate all cached quotes */
  invalidate(): void;

  /** Check if any cached quotes exist */
  hasCached(): boolean;
}

Example Usage

const cache = createQuoteCache();

// First request - fetches from API
const quote1 = await getQuote(params, 'my-app', true, cache);
console.log('Fresh quote from API');

// Same request within 30 seconds - uses cache
const quote2 = await getQuote(params, 'my-app', true, cache);
console.log('Quote from cache (same ID):', quote1.id === quote2.id);

// Different amount - fetches new quote
const quote3 = await getQuote(
  { ...params, fromAmount: '2000000000' },
  'my-app',
  true,
  cache
);
console.log('New quote for different amount');

// Force refresh
cache.invalidate();
const quote4 = await getQuote(params, 'my-app', true, cache);
console.log('Fresh quote after invalidation');

Types

Quote

interface Quote {
  /** Unique quote ID */
  id: string;
  /** Route steps */
  steps: Step[];
  /** Fee breakdown */
  fees: Fees;
  /** Estimated total time in seconds */
  estimatedTime: number;
  /** Input amount */
  fromAmount: string;
  /** Expected output amount */
  toAmount: string;
  /** Slippage tolerance applied (percentage format) */
  slippageTolerance: number;
  /** Minimum amount to receive after slippage */
  minimumReceived: string;
  /** Minimum amount formatted with decimals */
  minimumReceivedFormatted: string;
  /** Price impact as decimal (0.01 = 1%) */
  priceImpact: number;
  /** Whether impact exceeds HIGH threshold (1%) */
  highImpact: boolean;
  /** Price impact severity level */
  impactSeverity: 'low' | 'medium' | 'high' | 'very_high';
  /** Quote expiration timestamp */
  expiresAt: number;
  /** Source token */
  fromToken: Token;
  /** Destination token */
  toToken: Token;
  /** Whether auto-deposit is included */
  includesAutoDeposit: boolean;
  /** Whether manual deposit is required */
  manualDepositRequired: boolean;
  /** Route preference used */
  routePreference: RoutePreference;
  /** Alternative routes for comparison */
  alternativeRoutes?: RouteComparison[];
}

Step

interface Step {
  id: string;
  type: 'swap' | 'bridge' | 'deposit' | 'approve';
  tool: string;
  toolLogoUrl?: string;
  fromChainId: number;
  toChainId: number;
  fromToken: Token;
  toToken: Token;
  fromAmount: string;
  toAmount: string;
  estimatedTime: number;
}

Fees

interface Fees {
  totalUsd: number;
  gasUsd: number;
  bridgeFeeUsd: number;
  protocolFeeUsd: number;
  gasEstimate: GasEstimate;
  gasFee?: FeeItem;
  bridgeFee?: FeeItem;
  protocolFee?: FeeItem;
}

RoutePreference

type RoutePreference = 'recommended' | 'fastest' | 'cheapest';

RouteComparison

interface RouteComparison {
  type: RoutePreference;
  estimatedTime: number;
  totalFees: string;
  outputAmount: string;
  routeId: string;
}

Common Patterns

Compare Route Options

async function compareRoutes(baseParams: QuoteParams) {
  const preferences: RoutePreference[] = ['recommended', 'fastest', 'cheapest'];

  const quotes = await Promise.all(
    preferences.map(pref =>
      getQuote({ ...baseParams, routePreference: pref }, 'my-app')
    )
  );

  console.log('Route Comparison:');
  quotes.forEach((quote, i) => {
    console.log(`\n${preferences[i].toUpperCase()}:`);
    console.log(`  Time: ${quote.estimatedTime}s`);
    console.log(`  Fees: $${quote.fees.totalUsd.toFixed(2)}`);
    console.log(`  Output: ${quote.toAmount}`);
  });
}

Handle High Price Impact

async function safeGetQuote(params: QuoteParams) {
  const quote = await getQuote(params, 'my-app');

  if (quote.highImpact) {
    console.warn(`High price impact detected: ${quote.priceImpact * 100}%`);

    if (quote.impactSeverity === 'very_high') {
      throw new Error(
        `Price impact too high (${quote.priceImpact * 100}%). ` +
        'Consider using a smaller amount.'
      );
    }
  }

  return quote;
}

Auto-Refresh Quotes

function useQuoteRefresh(params: QuoteParams, intervalMs = 25000) {
  const cache = createQuoteCache();
  let currentQuote: Quote | null = null;

  const refresh = async () => {
    try {
      cache.invalidate(); // Force fresh quote
      currentQuote = await getQuote(params, 'my-app', true, cache);
      console.log('Quote refreshed:', currentQuote.id);
    } catch (error) {
      console.error('Failed to refresh quote:', error);
    }
  };

  // Initial fetch
  refresh();

  // Set up refresh interval (before 30s expiry)
  const interval = setInterval(refresh, intervalMs);

  return {
    getQuote: () => currentQuote,
    stop: () => clearInterval(interval),
  };
}