Smart Order Router

The Smart Order Router enables optimal trade execution by finding the most efficient trades across all available liquidity pools on the StoryHunt protocol.

Overview

  • AlphaRouter: The core routing engine responsible for pathfinding, quote simulation, gas estimation, and execution preparation.

  • SwapAlphaRouter: A singleton wrapper around the low-level AlphaRouter algorithm which is a public interface to query optimal swap routes.


graph TD
A[SwapAlphaRouter]
B[AlphaRouter]
A --> B
B --> C[On-chain Providers]
B --> D[Subgraph Pool Providers]
B --> E[Gas Price Estimator]
B --> F[Simulators]
B --> G[Route Cache]

Installation & Setup

Ensure the project has access to the required SDK:

npm install @storyhunt/smart-order-router
npm install @storyhunt/sdk-core

SwapAlphaRouter

SwapAlphaRouter acts as a singleton utility class to interact with the router logic.

Initialization

const router = SwapAlphaRouter.getInstance(
  'https://mainnet.storyrpc.io/', // RPC endpoint
  {
    url: '',
    auth: '',
  },
  ChainId.STORY
);

Core Function: Get Swap Route

const { data, error } = await router.getSwapRouteData({
  tokenIn: '<INPUT_TOKEN_ADDRESS>',
  tokenOut: '<OUTPUT_TOKEN_ADDRESS>',
  amount: '1000000000000000000', // 1 token in wei
  exactIn: true,
  recipient: '<RECIPIENT_ADDRESS>',
  slippageTolerance: 100, // e.g., 1.00%
  deadline: Math.floor(Date.now() / 1000) + 300,
});

Sample Output

{
  blockNumber: '17923412',
  quote: 0.9856,
  quoteGasAdjusted: 0.9731,
  estimatedGasUsed: 145000,
  estimatedGasUsedUSD: 1.45,
  methodParameters: {
    calldata: '0x123abc...',
    value: '0'
  },
  route: [
    {
      path: ['TokenA', 'TokenB', 'TokenC'],
      poolIds: ['pool1', 'pool2'],
      type: 'v3'
    }
  ]
}

Path Discovery: getPaths

Returns all possible candidate token paths (via pools) between two tokens. Useful for visualizing or debugging router behavior.

const { data: paths } = await router.getPaths({
  tokenIn: '<INPUT_TOKEN_ADDRESS>',
  tokenOut: '<OUTPUT_TOKEN_ADDRESS>',
  exactIn: true,
});
console.log(paths);

AlphaRouter: Parameters

The AlphaRouter uses a comprehensive set of providers to accurately compute the best route.

export type AlphaRouterParams = {
  chainId: ChainId;
  provider: BaseProvider;
  multicall2Provider?: StoryHuntMulticallProvider;
  v3SubgraphProvider: IV3SubgraphProvider;
  v3PoolProvider?: IV3PoolProvider;
  onChainQuoteProvider?: IOnChainQuoteProvider;
  tokenProvider?: ITokenProvider;
  gasPriceProvider?: IGasPriceProvider;
  v3GasModelFactory?: IOnChainGasModelFactory<V3RouteWithValidQuote>;
  blockedTokenListProvider?: ITokenListProvider;
  swapRouterProvider?: ISwapRouterProvider;
  simulator?: Simulator;
  routeCachingProvider?: IRouteCachingProvider;
  portionProvider?: IPortionProvider;
  cachedRoutesCacheInvalidationFixRolloutPercentage?: number;
};

Each of these components can be extended or replaced, enabling a high degree of modularity for advanced use cases like simulation, cache control, or risk-adjusted routing.

Full Example


import { ChainId } from '@storyhunt/sdk-core';
import { routeAmountsToString, SwapAlphaRouter } from '@storyhunt/smart-order-router';

const router = SwapAlphaRouter.getInstance(
  'https://mainnet.storyrpc.io/',
  {
    url: '',
    auth: '',
  },
  ChainId.STORY
);

class RouteTest {
  static async run() {
    const { data, error } = await router.getSwapRouteData({
      tokenIn: '0x25f9c9715d1d700a50b2a9a06d80fe9f98ccb549',
      tokenOut: '0x0000000000000000000000000000000000000000',
      amount: '100',
      exactIn: false,
      recipient: '0xAACc28fA97b7765E483e64cF640a0075FC97bED2',
      slippageTolerance: 3933,
      deadline: 1743129769,
      debugRouting: false,
      simulate: false,
    });

    if (data) {
      const {
        blockNumber,
        estimatedGasUsed,
        estimatedGasUsedQuoteToken,
        estimatedGasUsedUSD,
        gasPriceWei,
        methodParameters,
        quote,
        quoteGasAdjusted,
        route: routeAmounts,
      } = data;
      log.debug(`Best Route:`);
      log.debug(`${routeAmountsToString(routeAmounts)}`);

      log.debug(`\tRaw Quote`);
      log.debug(`\t\t${quote.toFixed(2)}`);
      log.debug(`\tGas Adjusted Quote:`);
      log.debug(`\t\t${quoteGasAdjusted.toFixed(2)}`);
      log.debug(``);
      log.debug(
        `Gas Used Quote Token: ${estimatedGasUsedQuoteToken.toFixed(6)}`
      );
      log.debug(`Gas Used USD: ${estimatedGasUsedUSD.toFixed(6)}`);
      log.debug(`Calldata: ${methodParameters?.calldata}`);
      log.debug(`Value: ${methodParameters?.value}`);
      log.debug({
        blockNumber: blockNumber.toString(),
        estimatedGasUsed: estimatedGasUsed.toString(),
        gasPriceWei: gasPriceWei.toString(),
      });
    } else {
      console.error(`No data returned`);
    }

    return data;
  }
}

class PathTest {
  static async run() {
    const { data: paths, error: pathError } = await router.getPaths({
      tokenIn: '0x210e36b5cdb47c010c3225a065467ea193ded463',
      tokenOut: '0x0000000000000000000000000000000000000000',
      exactIn: true,
    });
    if (paths) {
      console.log(`PATHS:`, paths);
    }
    return paths;
  }
}

(async () => {
  await PathTest.run();
  await RouteTest.run();
})();

Notes

  • ExactIn/ExactOut: The router supports both exactIn and exactOut trade types.

  • Simulation: By enabling simulate: true, a backend simulation can be triggered to return more accurate gas and slippage data.

  • Calldata: The router returns the calldata necessary to execute the trade via a smart contract.


Debugging Tips

  • Use debugRouting: true in development to see intermediate routing decisions.

  • Use getPaths() to check for missing pools or broken liquidity routes.

  • Ensure RPC and Subgraph URLs are up to date and synced.

Last updated