# SpanDEX ## Advanced #### Configuration Deadlines, timeouts, retries, etc #### Overrides #### Promise Chaining import ConfigParams from '../snippets/params/config.mdx' ## Configuration Use `ConfigParams` to initialize config via `createConfig` or the `SpandexProvider` component. ## Examples If you build something cool with SpanDEX, let us know. * [SpanDEX Swap Demo](https://spandex-production.up.railway.app/) - Swap interface which compares performance of multiple aggregators and continuously provides the best price * [QuoteBench](https://quote-bench-production.up.railway.app/) - A benchmarking tool to compare stability, latency, accuracy, and price across aggregators and chains over time * [Hono Meta Aggregator](https://github.com/withfabricxyz/spandex/tree/main/examples/hono) - A minimal meta-aggregator API server built with Hono and SpanDEX ## Fees How to configure fee structures in SpanDEX with various providers swap fees, surplus, etc. import { Button } from 'vocs/components' ## Getting Started Depending on your project stack and state, there are multiple ways to get started with Spandex. ## New Projects If you are creating a new application, or kicking the tires, you can use our CLI to scaffold a new project with Spandex pre-configured. This command will choose which template to use based on your answers. :::code-group ```bash [npm] npm create @spandex/spandex ``` ```bash [pnpm] pnpm create @spandex/spandex ``` ```bash [bun] bun create @spandex/spandex ``` ::: ## Existing Projects For react applications, see the [React getting started guide](/react/getting-started.mdx). Otherwise, see the [Core getting started guide](/core/getting-started.mdx). ## Installation ### Core `@spandex/core` provides all the functionality to interact with multiple aggregators, simulate quotes onchain, and execute swaps. It depends on `viem` for blockchain interactions. :::code-group ```bash [npm] npm i viem @spandex/core ``` ```bash [bun] bun i viem @spandex/core ``` ```bash [pnpm] pnpm i viem @spandex/core ``` ::: ### React `@spandex/react` provides React hooks for integrating SpanDEX into your React applications. It depends on `wagmi`. :::code-group ```bash [npm] npm i wagmi @spandex/react ``` ```bash [bun] bun i wagmi @spandex/react ``` ```bash [pnpm] pnpm i wagmi @spandex/react ``` ::: ### Security Considerations Given that SpanDEX presents unsigned transaction data for execution, it's crucial have a security stance. Some recommended practices include: * Lock or pin dependencies to specific versions to reduce supply chain attack risk. * Use a package manager that supports integrity checks, such as npm or yarn, to verify the authenticity of packages. * Require packages have a minimum age before updating, using pnpms minimumReleaseAge or similar features in other package managers. * Ensure web apps have Content Security Policies (CSP) in place to mitigate risks from malicious scripts. * Remember that supply chain attacks are real and the EVM is a ripe target. ## Why SpanDEX Let's clarify some key concepts in DeFi: DEX aggregators and meta aggregators. ### Why Aggregators? Aggregators monitor liquidity across multiple DEXes. The benefits are numerous: * **Better Prices**: By indexing liquidity from various sources, aggregators find better prices for swaps than any single DEX could offer. * **Reduced Slippage**: Aggregators can split trades across multiple DEXes to minimize slippage, resulting in more favorable execution prices. * **Atomic Swaps**: Aggregators can facilitate complex trades that involve multiple tokens or routes, allowing users to swap tokens that may not have direct liquidity pools. ### Why Meta Aggregators? Meta aggregators automate the process of querying multiple DEX aggregators. The advantages include: * **Expanded Liquidity**: Not all aggregators cover the same DEXes or tokens. By using a meta aggregator, users can access a broader range of liquidity sources. * **Optimal Pricing**: Meta aggregators can compare quotes from different aggregators to ensure users receive the best possible price for their swaps. * **Redundancy**: If one aggregator is down or experiencing issues, a meta aggregator can fall back to other aggregators, ensuring continuous service. ### And SpanDEX? SpanDEX is an open-source meta aggregator library provides all the benefits of a meta aggregator in a developer-friendly package without any middlemen. Benefits: * **Open Source**: SpanDEX is fully open source, allowing developers to inspect, modify, and contribute to the codebase. * **No Middlemen**: SpanDEX interacts directly with DEX aggregators, eliminating a centralized meta-aggregation service that could introduce latency, fees, censorship, or single points of failure. * **Customizable**: Developers can choose which aggregators to include, configure settings, and extend functionality to suit their specific use cases. * **Onchain Simulation**: SpanDEX supports onchain simulation of swaps, allowing developers to verify quote accuracy, gas costs, and potential reverts before executing transactions. **The world is yours.** ## Onchain Simulation ### Overview While fetched quotes provide a good estimate of swap costs, simulating a swap can provide a more accurate picture of how the swap will perform when executed onchain. Simulation performs the swap and tracks state changes as if the transaction was executed onchain. This allows us to: * Confirm the accuracy of quotes by verifying that their quotes aren't stale or otherwise incorrect * Include gas costs to compare real costs across different aggregators * Verify swaps won't revert due to slippage, approvals, or insufficient balance Additionally, there are edge cases such as contracts that have allowlists or other unexpected behavior that can only be uncovered through simulation or a real transaction. Ultimately, simulation allows us to compare quotes against their simulated onchain output, not just quoted estimates. ### Installation To install SpanDEX with onchain simulation support, first ensure that you have the core package installed: Then, install the simulation package along with `viem`: :::code-group ```bash [npm] npm i viem @spandex/simulation ``` ```bash [bun] bun i viem @spandex/simulation ``` ```bash [pnpm] pnpm i viem @spandex/simulation ``` ::: ### Configuration > ⚠️ Some RPC providers do not support the underlying > [`eth_simulateV1`](https://github.com/ethereum/execution-apis/pull/484) method required for > simulation. Refer to your RPC provider's documentation to ensure that simulation is supported. To enable onchain simulation, you'll need: 1. a viem `PublicClient` connected to the target chain 2. a `MetaAggregator` instance from the core SpanDEX package with your desired aggregators configured import ConfigParams from "../../snippets/params/config.mdx"; ## ConfigParams ```ts import type { ConfigParams } from "@spandex/core"; ``` import SwapParams from "../../snippets/params/swap.mdx"; ## SwapParams ```ts import type { SwapParams } from "@spandex/core"; ``` ## Getting Started - React To get started we will create a shared meta aggregator instance with three providers, default settings, and support for base. Then, we will fetch the best quote for a swap and execute it. ### 1. Install Install the core library: :::code-group ```bash [npm] npm i wagmi @spandex/react ``` ```bash [bun] bun i wagmi @spandex/react ``` ```bash [pnpm] pnpm i wagmi @spandex/react ``` ::: ### 2. Configure See the [configuration reference](/configuration.mdx) for all options. ```ts import { WagmiProvider } from "wagmi"; import { wagmiConfig } from "./wagmiConfig.js"; import { SpandexProvider } from "@spandex/react"; import { fabric, zeroX } from "@spandex/core"; const config = { providers: [fabric({ appId: "YOUR_FABRIC_APP_ID" }), zeroX({ apiKey: "YOUR_0X_API_KEY" })], }; export function App() { return ( ); } ``` ### 3. Fetch Quotes ```ts import { useQuotes } from "@spandex/react"; export function Quotes() { const { data, isLoading, error } = useQuotes({ mode: "exactIn", inputToken: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // USDC outputToken: "0x4200000000000000000000000000000000000006", // WETH inputAmount: 1000000n, // 1 USDC slippageBps: 100, // 1% }); if (isLoading) return
Fetching quotes...
; if (error) return
Error: {error.message}
; return (
{quotes?.map((quote) => (
{quote.provider}: {quote.outputAmount.toString()}
))}
); } ``` ## useQuote Fetch quotes from all configured aggregators, simulate them, and select a winner using a strategy. ### Example ```ts twoslash import { useQuote } from "@spandex/react"; function App() { const { data, isLoading, error } = useQuote({ swap: { inputToken: "0x4200000000000000000000000000000000000006", // WETH outputToken: "0xd9AAEC86B65D86f6A7B5B1b0c42FFA531710b6CA", // USDbC mode: "exactIn", inputAmount: 1_000_000_000_000_000_000n, // 1 WETH slippageBps: 50, // Tolerance }, strategy: "bestPrice", }); } ``` ### Returns ```ts { data: SuccessfulSimulatedQuote | null | undefined; isLoading: boolean; error: Error | null; // ...other React Query return values } ``` ### Params #### swap The swap parameters, with optional `chainId` and `swapperAccount`, which are inferred from the current connection if not provided. ##### swap params * `inputToken: string` - The input token address. * `outputToken: string` - The output token address. * `mode: "exactIn" | "targetOut"` - The swap mode. * `inputAmount?: bigint` - The input amount (required if mode is "exactIn"). * `outputAmount?: bigint` - The desired output amount (required if mode is "targetOut"). * `slippageBps: number` - The slippage tolerance in basis points. #### strategy Defines how to choose the winning quote. Built-in strategies are `"fastest"`, `"bestPrice"`, `"estimatedGas"`, and `"priority"`. You can also supply a custom selection function. #### query Optional React Query overrides (for example `enabled`, `staleTime`, or `select`). queryKey and queryFn are managed by Spandex and cannot be provided. ## useQuoteExecutor Given a set of quotes, provides functions and state to execute necessary approvals and best quote to satisfy the swap itent. ## useQuoteMetadata ## useQuotes Fetch quotes from all configured aggregators and simulate each quote. ### Example ```ts twoslash import { useQuotes } from "@spandex/react"; function App() { const { data, isLoading, error } = useQuotes({ swap: { inputToken: "0x4200000000000000000000000000000000000006", // WETH outputToken: "0xd9AAEC86B65D86f6A7B5B1b0c42FFA531710b6CA", // USDbC mode: "exactIn", inputAmount: 1_000_000_000_000_000_000n, // 1 WETH slippageBps: 50, // Tolerance } }); } ``` ### Returns ```ts { data: SimulatedQuote[] | undefined; isLoading: boolean; error: Error | null; // ...other React Query return values } ``` ### Params #### swap The swap parameters, with optional `chainId` and `swapperAccount`, which are inferred from the current connection if not provided. ##### swap params * `inputToken: string` - The input token address. * `outputToken: string` - The output token address. * `mode: "exactIn" | "targetOut"` - The swap mode. * `inputAmount?: bigint` - The input amount (required if mode is "exactIn"). * `outputAmount?: bigint` - The desired output amount (required if mode is "targetOut"). * `slippageBps: number` - The slippage tolerance in basis points. ## useRawQuotes Fetch quotes from all configured aggregators without simulation. import { Button } from 'vocs/components' import { Callout } from 'vocs/components' ## 0x
### Configuration 0x requires an API key for access. You can obtain one by [signing up](https://dashboard.0x.org/create-account). Use the `zeroX` factory since JavaScript identifiers cannot start with a number. ```ts twoslash import { createConfig, zeroX } from "@spandex/core"; export const config = createConfig({ providers: [ zeroX({ // required! apiKey: "YOUR_0X_API_KEY", // If you have negotiated surplus sharing with 0x, override negotiatedOptions // negotiatedFeatures: ["integratorSurplus"], }), // ... ], }); ``` import { Button } from 'vocs/components' import { Callout } from 'vocs/components' ## Fabric
### Configuration ```ts twoslash import { createConfig, fabric } from "@spandex/core"; export const config = createConfig({ providers: [ fabric({ appId: "YOUR_FABRIC_APP_ID", // Optional, use if you have negotiated service apiKey: "YOUR_FABRIC_API_KEY", // Optional, only set if you have a private Fabric deployment url: undefined, }), // ... ], }); ``` import { Button } from 'vocs/components' import { Callout } from 'vocs/components' ## KyberSwap
### Configuration ```ts twoslash import { createConfig, kyberswap } from "@spandex/core"; export const config = createConfig({ providers: [ kyberswap({ // Required, you choose the value clientId: "myawesomeapp", }), // ... ], }); ``` ## Lifi import { Button } from 'vocs/components' import { Callout } from 'vocs/components' ## Odos
### Configuration ```ts twoslash import { createConfig, odos } from "@spandex/core"; export const config = createConfig({ providers: [ odos({ // Optional. Use for attribution referralCode: undefined, // Optional. Use it if you got it apiKey: undefined, // Optional. Enable fees and surplus if negotiated with Odos. // negotiatedFeatures: ["integratorSurplus", "integratorFees"], }), // ... ], }); ``` import { Button } from 'vocs/components' ## Relay
### Configuration ```ts twoslash import { createConfig, relay } from "@spandex/core"; export const config = createConfig({ providers: [ relay({ // Optional, override the API base URL url: undefined, }), // ... ], }); ``` ## Getting Started - Core To get started we will create a shared meta aggregator instance with three providers, default settings, and support for base. Then, we will fetch the best quote for a swap and execute it. ### 1. Install Install the core library: :::code-group ```bash [npm] npm i viem @spandex/core ``` ```bash [pnpm] pnpm i viem @spandex/core ``` ```bash [bun] bun i viem @spandex/core ``` ::: ### 2. Configure See the [configuration reference](/configuration.mdx) for all options. ```ts filename="config.ts" twoslash import { createConfig, fabric, kyberswap, odos } from "@spandex/core"; import { createPublicClient, http, type PublicClient } from "viem"; import { base } from "viem/chains"; // Create a base client for quote simulation const baseClient = createPublicClient({ chain: base, transport: http("https://base.drpc.org"), }); // The aggregator instance can be shared across your application export const config = createConfig({ providers: [fabric({ appId: "test" }), odos({}), kyberswap({ clientId: "test" })], clients: [baseClient] as PublicClient[], }); ``` ### 3. Fetch Quotes and Execute ```ts filename="quote.ts" twoslash import { config } from "./config.js"; import { createWalletClient, http } from "viem"; import { executeQuote, getQuote } from "@spandex/core"; import { base } from "viem/chains"; const swapParams = { chainId: 8453, inputToken: "0x4200000000000000000000000000000000000006", // WETH outputToken: "0xd9AAEC86B65D86f6A7B5B1b0c42FFA531710b6CA", // USDbC mode: "exactIn", inputAmount: 1_000_000_000_000_000_000n, // 1 WETH slippageBps: 50, // Tolerance swapperAccount: "0x1234567890abcdef1234567890abcdef12345678", }; const quote = await getQuote({ config, swap: swapParams, strategy: "bestPrice", }); if (!quote) { throw new Error("No providers responded in time"); } const walletClient = createWalletClient({ account: "0xdead00000000000000000000000000000000beef", chain: base, transport: http(), }); const { transactionHash } = await executeQuote({ config, swap: swapParams, quote, walletClient, }); console.log(`Swapped via ${quote.provider} @ ${transactionHash}`); ``` import ConfigParams from '../../../snippets/params/config.mdx' ## createConfig Create a configuration object to customize providers and options for all aggregation functions. ```ts import { createConfig, fabric, kyberswap, odos, zeroX } from "@spandex/core"; export const config = createConfig({ providers: [ zeroX({ apiKey: "YOUR_0X_API_KEY" }), fabric({ appId: "YOUR_FABRIC_APP_ID" }), odos({}), kyberswap({ clientId: "YOUR_KYBER_CLIENT_ID" }), ], options: { deadlineMs: 10000, numRetries: 2, initialRetryDelayMs: 200, integratorFeeAddress: "0xFee000", integratorSwapFeeBps: 25, }, clients: [ baseClient, mainnetClient ] as PublicClient[], }); ``` ### Proxy configuration Use a proxy when fetching quotes from a server (for example, to avoid browser CORS issues). ```ts import { createConfig, proxy } from "@spandex/core"; import { createPublicClient, http, type PublicClient } from "viem"; import { base } from "viem/chains"; const client = createPublicClient({ chain: base, transport: http("https://base.drpc.org"), }); export const config = createConfig({ proxy: proxy({ pathOrUrl: "https://example.com/api/quotes" }), clients: [client] as PublicClient[], }); ``` ### Returns `Config` Aggregator configuration object to be passed to aggregation functions. ## decodeQuoteStream Decode a quote stream produced by `newQuoteStream` into an array of quote promises. ### Example ```ts import { decodeQuoteStream } from "@spandex/core"; const response = await fetch("/api/quotes"); if (!response.body) { throw new Error("No response body"); } const quotePromises = await decodeQuoteStream(response.body); const quotes = await Promise.all(quotePromises); console.log(quotes.length); ``` ### Params #### stream `ReadableStream` Stream of serialized quotes produced by `newQuoteStream`. ### Returns `Promise>>` Promises that resolve to quotes as they stream in. ## executeQuote Execute a simulated quote onchain using a wallet client. Handles approval requirements and supports EIP-5792 batch calls when available. ### Example :::code-group ```ts [executeQuote] twoslash import { executeQuote, getQuote } from "@spandex/core"; import { config, publicClient, walletClient } from "./config.js"; const swap = { chainId: 8453, inputToken: "0x4200000000000000000000000000000000000006", // WETH outputToken: "0xd9AAEC86B65D86f6A7B5B1b0c42FFA531710b6CA", // USDbC mode: "exactIn", inputAmount: 1_000_000_000_000_000_000n, // 1 WETH slippageBps: 50, // Tolerance swapperAccount: "0x1234567890abcdef1234567890abcdef12345678", }; const quote = await getQuote({ config, swap, strategy: "bestPrice", }); if (!quote) { throw new Error("No providers returned a successful quote"); } const result = await executeQuote({ config, swap, quote, walletClient, publicClient, }); console.log(result.transactionHash); ``` ```ts [config] filename="config.ts" twoslash import { createConfig, fabric, odos } from "@spandex/core"; import { createPublicClient, createWalletClient, http, type PublicClient } from "viem"; import { base } from "viem/chains"; const publicClient = createPublicClient({ chain: base, transport: http("https://base.drpc.org"), }); const walletClient = createWalletClient({ account: "0xdead00000000000000000000000000000000beef", chain: base, transport: http(), }); export { publicClient, walletClient }; export const config = createConfig({ providers: [fabric({ appId: "your app id" }), odos({})], clients: [publicClient] as PublicClient[], }); ``` ::: ### Params #### swap `SwapParams` Swap parameters used to build the approval + swap transactions. #### quote `SimulatedQuote` The simulated quote to be executed. This should be obtained via [`getQuotes`](./getQuotes.mdx) or [`getQuote`](./getQuote.mdx). If the quote is not successful, `executeQuote` throws an `ExecutionError`. #### config `Config` Aggregator configuration object created via [`createConfig`](./createConfig.mdx). Used to resolve clients and options. #### walletClient `WalletClient` A Viem wallet client configured with the swapper's account and chain. This client is used to sign and send the transaction. #### publicClient `PublicClient | undefined` Optional public client for onchain reads (allowance checks, gas estimation, and receipt polling). If omitted, the configured client for the swap chain is used. ### Returns `Promise` A promise for an execution result containing the transaction hash. ## getPricing Aggregate pricing metadata across quotes and compute average USD prices when multiple sources are present. ### Example ```ts import { getPricing, getRawQuotes } from "@spandex/core"; import { config } from "./config.js"; const quotes = await getRawQuotes({ config, swap: { chainId: 8453, inputToken: "0x4200000000000000000000000000000000000006", outputToken: "0xd9AAEC86B65D86f6A7B5B1b0c42FFA531710b6CA", mode: "exactIn", inputAmount: 1_000_000_000_000_000_000n, slippageBps: 50, swapperAccount: "0x1234567890abcdef1234567890abcdef12345678", }, }); const pricing = getPricing(quotes); console.log(pricing.sources); console.log(pricing.inputToken?.usdPrice, pricing.outputToken?.usdPrice); ``` ### Params #### quotes `Quote[]` Quotes to summarize. Failed quotes and quotes without pricing metadata are ignored. ### Returns `PricingSummary` Aggregated pricing summary including averaged USD prices and contributing providers. ## getQuote Fetch quotes, simulate them, and return a single winning quote based on a selection strategy. ### Example :::code-group ```ts [getQuote] twoslash import { getQuote } from "@spandex/core"; import { config } from "./config.js"; const quote = await getQuote({ config, swap: { chainId: 8453, inputToken: "0x4200000000000000000000000000000000000006", // WETH outputToken: "0xd9AAEC86B65D86f6A7B5B1b0c42FFA531710b6CA", // USDbC mode: "exactIn", inputAmount: 1_000_000_000_000_000_000n, // 1 WETH slippageBps: 50, swapperAccount: "0x1234567890abcdef1234567890abcdef12345678", }, strategy: "bestPrice", }); if (!quote) { throw new Error("No providers returned a successful quote"); } console.log(quote.provider, quote.simulation.outputAmount); ``` ```ts [config] filename="config.ts" twoslash import { createConfig, fabric, odos } from "@spandex/core"; import { createPublicClient, http, type PublicClient } from "viem"; import { base } from "viem/chains"; const client = createPublicClient({ chain: base, transport: http("https://base.drpc.org"), }); export const config = createConfig({ providers: [fabric({ appId: "your app id" }), odos({})], clients: [client] as PublicClient[], }); ``` ::: ### Params #### config `Config` Aggregator configuration object created via [`createConfig`](./createConfig.mdx). #### swap `SwapParams` Parameters defining the swap operation. See [SwapParams reference](/reference/SwapParams.mdx) for details. #### strategy `QuoteSelectionStrategy` Selection strategy used to pick a winning quote. Built-in strategy names: | Strategy | Waits for All | Selection Criteria | Best For | | -------------- | ------------- | ------------------------- | -------------------- | | `bestPrice` | ✓ | Highest simulated output | Price optimization | | `estimatedGas` | ✓ | Lowest simulated gas used | Gas efficiency | | `fastest` | ✗ | First success | Lowest latency | | `priority` | ✗ | Provider order | Failover chains | | Custom | Depends | Your logic | Special requirements | Custom function signature: ```ts type QuoteSelectionFn = ( quotes: Array> ) => Promise ``` #### client `PublicClient | undefined` Optional custom PublicClient to use for onchain simulations. If not provided, the configured clients in the `config` will be used. #### simulate `typeof simulateQuote | undefined` Optional override for the simulation function used on each quote. ### Returns `Promise` Winning simulated quote, or `null` if no provider succeeds. ## getQuotes Fetches quotes from all configured providers and simulates the quote transactions to validate the swap operation and the final token output amount. ### Example :::code-group ```ts [getQuotes] twoslash import { getQuotes } from "@spandex/core"; import { config } from "./config.js"; const quotes = await getQuotes({ config, swap: { chainId: 8453, inputToken: "0x4200000000000000000000000000000000000006", // WETH outputToken: "0xd9AAEC86B65D86f6A7B5B1b0c42FFA531710b6CA", // USDbC mode: "exactIn", inputAmount: 1_000_000_000_000_000_000n, // 1 WETH slippageBps: 50, // Tolerance swapperAccount: "0x1234567890abcdef1234567890abcdef12345678", } }); for (const quote of quotes) { if (quote.success && quote.simulation.success) { console.log( `${quote.provider} yields ${quote.simulation.outputAmount}` ); } } /// @log: fabric yields 995000000n /// @log: odos yields 994500000n ``` ```ts [config] filename="config.ts" twoslash import { createConfig, fabric, odos } from "@spandex/core"; import { createPublicClient, http, type PublicClient } from "viem"; import { base } from "viem/chains"; const client = createPublicClient({ chain: base, transport: http("https://base.drpc.org"), }); export const config = createConfig({ providers: [fabric({ appId: "your app id" }), odos({})], clients: [client] as PublicClient[], }); ``` ::: ### Params #### config `Config` Aggregator configuration object created via [`createConfig`](./createConfig.mdx). #### swap `SwapParams` Parameters defining the swap operation. See [SwapParams reference](/reference/SwapParams.mdx) for details. #### client `PublicClient | undefined` Optional custom PublicClient to use for onchain simulations. If not provided, the configured clients in the `config` will be used. ### Returns `Promise` List of simulated quotes from all providers. Each quote includes the provider name, raw quote data, and the simulated output amount after onchain validation. Quotes that fail simulation are denoted by `success=false` and include an error message. Simulation may also fail, due to a revert, etc. See `SimulatedQuote` for details. ## getRawQuotes Fetches quotes from all configured providers. This function does not perform any onchain simulation or validation. It is useful for scenarios where you plan to handle validation separately. Always simulate! ### Example :::code-group ```ts [getRawQuotes] twoslash import { getRawQuotes } from "@spandex/core"; import { config } from "./config.js"; const quotes = await getRawQuotes({ config, swap: { chainId: 8453, inputToken: "0x4200000000000000000000000000000000000006", // WETH outputToken: "0xd9AAEC86B65D86f6A7B5B1b0c42FFA531710b6CA", // USDbC mode: "exactIn", inputAmount: 1_000_000_000_000_000_000n, // 1 WETH slippageBps: 50, // Tolerance swapperAccount: "0x1234567890abcdef1234567890abcdef12345678", } }); ``` ```ts [config] filename="config.ts" twoslash import { createConfig, fabric, odos } from "@spandex/core"; export const config = createConfig({ providers: [fabric({ appId: "your app id" }), odos({})], }); ``` ::: ### Params #### config `Config` Aggregator configuration object created via [`createConfig`](./createConfig.mdx). #### swap `SwapParams` Parameters defining the swap operation. See [SwapParams reference](/reference/SwapParams.mdx) for details. ### Returns `Promise` List of quotes from all providers. Quotes that resulted in failure are denoted by `success=false` and include an error message. ## newQuoteStream Create a `ReadableStream` that emits serialized quotes as each promise resolves. This is useful for server-to-client streaming. ### Example ```ts import { newQuoteStream, prepareQuotes } from "@spandex/core"; import { config, swap } from "./config.js"; export async function GET() { const promises = await prepareQuotes({ config, swap, mapFn: async (quote) => quote, }); return new Response(newQuoteStream(promises), { headers: { "Content-Type": "application/octet-stream", }, }); } ``` ### Params #### promises `Array>` Quote promises to resolve and stream. ### Returns `ReadableStream` Stream of serialized quote frames. ## prepareQuotes Prepare a list of swap quote promise chains for quote fetching and secondary actions such as simulation. This is useful when custom behavior is required, such as racing quotes+simulation for multiple providers. ### Example :::code-group ```ts [getQuotes] import { prepareQuotes, type Quote } from "@spandex/core"; import { config } from "./config.ts"; // Identity fn async function mapFn(quote: Quote): Promise { return quote; } const promises = await prepareQuotes({ config, swap: { chainId: 8453, inputToken: "0x4200000000000000000000000000000000000006", // WETH outputToken: "0xd9AAEC86B65D86f6A7B5B1b0c42FFA531710b6CA", // USDbC mode: "exactIn", inputAmount: 1_000_000_000_000_000_000n, // 1 WETH slippageBps: 50, // Tolerance swapperAccount: "0x1234567890abcdef1234567890abcdef12345678", }, mapFn, }); ``` ```ts [config.ts] filename="config.ts" twoslash import { createConfig, fabric, odos } from "@spandex/core"; import { createPublicClient, http, type PublicClient } from "viem"; import { base } from "viem/chains"; const client = createPublicClient({ chain: base, transport: http("https://base.drpc.org"), }); export const config = createConfig({ providers: [fabric({ appId: "your app id" }), odos({})], clients: [client] as PublicClient[], }); ``` ::: ### Params #### config `Config` Aggregator configuration object created via [`createConfig`](./createConfig.mdx). #### swap `SwapParams` Parameters defining the swap operation. See [SwapParams reference](/reference/SwapParams.mdx) for details. #### mapFn `(quote: Quote) => Promise` Function that maps each fetched quote to a promise of type `T`. This allows for custom processing of each quote, such as simulation or other enrichment. Identity function is also valid. ### Returns `Array>` List of promises, each resolving to type `T` as defined by the `mapFn`. ## selectQuote Pick the best simulated quote from a set of in-flight quote+simulation promises. This is useful when you want to stream quotes and simulations concurrently and only commit to the best result once enough data is available. ### Example :::code-group ```ts [selectQuote] import { prepareQuotes, selectQuote, simulateQuote } from "@spandex/core"; import { config, client, swap } from "./config.ts"; const quotes = await prepareQuotes({ config, swap, mapFn: (quote) => simulateQuote({ quote, swap, client }), }); const quote = await selectQuote({ quotes, strategy: "bestPrice", }); ``` ::: ### Params #### quotes `Array>` A list of promises each resolving to a simulated quote. These can be created via the [`prepareQuotes`](./prepareQuotes.mdx) function. #### strategy `QuoteSelectionStrategy` Strategy used to pick a winning quote. | Strategy | Waits for All | Selection Criteria | Best For | | -------------- | ------------- | ------------------------- | -------------------- | | `bestPrice` | ✓ | Highest simulated output | Price optimization | | `estimatedGas` | ✓ | Lowest simulated gas used | Gas efficiency | | `fastest` | ✗ | First success | Speed/latency | | `priority` | ✗ | Provider order | Fallback chains | | Custom | Depends | Your logic | Special requirements | **Custom** ```ts type QuoteSelectionFn = ( quotes: Array> ) => Promise ``` ### Returns `Promise` A promise for a single simulated quote that offers the best outcome depending on the strategy. ## simulateQuote Simulate a single quote onchain and decorate it with simulation results. ### Example ```ts import { getRawQuotes, simulateQuote } from "@spandex/core"; import { config, client } from "./config.js"; const swap = { chainId: 8453, inputToken: "0x4200000000000000000000000000000000000006", outputToken: "0xd9AAEC86B65D86f6A7B5B1b0c42FFA531710b6CA", mode: "exactIn", inputAmount: 1_000_000_000_000_000_000n, slippageBps: 50, swapperAccount: "0x1234567890abcdef1234567890abcdef12345678", }; const quotes = await getRawQuotes({ config, swap, }); const first = quotes.find((quote) => quote.success); if (!first) { throw new Error("No successful quotes to simulate"); } const simulated = await simulateQuote({ quote: first, swap, client, }); if (simulated.success && simulated.simulation.success) { console.log(simulated.simulation.outputAmount); } ``` ### Params #### client `PublicClient` Client used to perform the simulation. #### swap `SwapParams` Swap parameters associated with the quote. See [SwapParams reference](/reference/SwapParams.mdx) for details. #### quote `Quote` Quote to simulate. ### Returns `Promise` The quote decorated with simulation results. Simulation failures are returned in the `simulation` field (for example `SimulationRevertError`). ## simulateQuotes Simulate a batch of quotes given shared swap parameters and a client. ### Example ```ts import { getRawQuotes, simulateQuotes } from "@spandex/core"; import { config, client } from "./config.js"; const swap = { chainId: 8453, inputToken: "0x4200000000000000000000000000000000000006", outputToken: "0xd9AAEC86B65D86f6A7B5B1b0c42FFA531710b6CA", mode: "exactIn", inputAmount: 1_000_000_000_000_000_000n, slippageBps: 50, swapperAccount: "0x1234567890abcdef1234567890abcdef12345678", }; const quotes = await getRawQuotes({ config, swap }); const simulated = await simulateQuotes({ client, swap, quotes, }); for (const quote of simulated) { if (quote.success && quote.simulation.success) { console.log(quote.provider, quote.simulation.outputAmount); } } ``` ### Params #### client `PublicClient` Client used to perform the simulations. #### swap `SwapParams` Swap parameters shared across all quotes. See [SwapParams reference](/reference/SwapParams.mdx) for details. #### quotes `Quote[]` Quotes to simulate. ### Returns `Promise` Quotes decorated with simulation results. Simulation failures are represented in the `simulation` field. ## sortQuotesByPerformance Sort successful simulated quotes by a selected performance metric. ### Example ```ts import { getRawQuotes, simulateQuotes, sortQuotesByPerformance, type SuccessfulSimulatedQuote, } from "@spandex/core"; import { config, client } from "./config.js"; const swap = { chainId: 8453, inputToken: "0x4200000000000000000000000000000000000006", outputToken: "0xd9AAEC86B65D86f6A7B5B1b0c42FFA531710b6CA", mode: "exactIn", inputAmount: 1_000_000_000_000_000_000n, slippageBps: 50, swapperAccount: "0x1234567890abcdef1234567890abcdef12345678", }; const quotes = await getRawQuotes({ config, swap }); const simulated = await simulateQuotes({ quotes, swap, client }); const successful = simulated.filter( (quote) => quote.success && quote.simulation.success, ) as SuccessfulSimulatedQuote[]; const sorted = sortQuotesByPerformance({ quotes: successful, metric: "accuracy", ascending: true, }); console.log(sorted[0]?.performance); ``` ### Params #### quotes `SuccessfulSimulatedQuote[]` Quotes to sort. Only successful simulated quotes include performance metrics. #### metric `keyof QuotePerformance` Metric to sort by. Supported metrics include `latency`, `gasUsed`, `outputAmount`, `priceDelta`, and `accuracy`. #### ascending `boolean | undefined` Sort order (default: `true`). When `false`, the list is sorted descending. ### Returns `SuccessfulSimulatedQuote[]` New array of quotes sorted by the selected performance metric. ## Move along... nothing to see here!