@ring-protocol/smart-order-router
v0.6.2
Published
Few Smart Order Router
Readme
Uniswap Smart Order Router
This repository contains routing logic for the Uniswap V3 protocol.
It searches for the most efficient way to swap token A for token B, considering splitting swaps across multiple routes and gas costs.
Robinhood Chain (4663)
Robinhood uses the existing AlphaRouter providers and network tables. SDK package roles stay unchanged: @ring-protocol/few-v2-sdk supplies Ring FewV2; @ring-protocol/v2-sdk supplies ordinary V2. This network addition uses the Few-Protocol/ring-sdks consumer line based on 902db0d.
The chain uses ETH, WETH 0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73, and fwWETH 0xDD4eaAccA3b077c01e8eF45207fc48Fd1B54C735. Network support is added in the same chain, address, token and provider tables as BSC. The shared multicall providers report the explicitly requested RPC block, resolving Promise-valued block numbers before the call; without an explicit block they retain the contract result. Static V3 pools use the existing pool provider to compute their addresses. Mixed quotes use V2 when the network has a V2 quoter and no V1 quoter; caller-supplied quoter overrides retain their existing selection rules. Execution-gas parameters reuse the existing Arbitrum group; parent-chain publication fees are not modeled.
Robinhood uses USDG 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168 (6 decimals) as the ordinary USD gas reference. Read-only checks at L2 block 59328354 found liquid WETH/USDG pools in Uniswap V2 and all four standard V3 fee tiers. The canonical fwUSDG token 0x9e47A5F5D4028b3406c04bB16138712Ec035cB17 is also configured, but the Ring FewV2 factory returned allPairsLength() = 0 at that block. FewV2 therefore has no mainnet pool to quote or use for its USD gas reference; an absent pool is not a zero gas cost. USDG and fwUSDG are also registered as ordinary and Few base tokens respectively, allowing the existing providers to discover routes through those assets. This addition does not fabricate prices or change path search, split optimization, quote arithmetic, canonical-token conversion, or simulation logic.
The candidate uses SOR 0.5.42 with the published SDK core 0.1.8, FewV2 0.1.17, ordinary V2/V3 0.1.12, V4 0.1.16, router 0.1.45, and Universal Router 0.1.50. The lockfile was generated by installing these exact versions from the public npm registry, without local package overrides.
Use Node.js 22 for the local verification commands. Follow the existing npm install and npm run build steps below, then check the CLI types and run the additional local network fixtures explicitly:
node node_modules/typescript/bin/tsc --noEmit --incremental false -p tsconfig.oclif.json
node --test test/unit/routers/alpha-router/robinhood-network.test.cjs
node --test test/unit/providers/multicall-block-metadata.test.cjs
node --test test/unit/providers/v3/static-pool-address.test.cjs
node --test test/unit/providers/mixed-quoter-selection.test.cjs
node --test test/unit/util/network-router-encoding.test.cjs
node --test test/unit/cli/tick-display.test.cjs
node --test test/unit/routers/alpha-router/robinhood-usd-gas.test.cjs
node --test test/unit/build/copy-type-declarations.test.cjsThe fixtures cover network tables, deployment addresses, native currency, common block handling, chain-aware V3 pool addresses, mixed-quoter selection, existing router encoding, USD gas-reference selection, and CLI tick output with missing metadata. They do not access RPC endpoints. The build now copies TypeChain input declarations into both output directories after compilation so the published factory types resolve. Existing test and release scripts are unchanged.
Robinhood CLI checks
Set JSON_RPC_PROVIDER_ROBINHOOD in the process environment. The public mainnet endpoint is https://rpc.mainnet.chain.robinhood.com. The example below uses the verified WETH/USDG pool assets and small, human-readable quote amounts. WETH and fwWETH alone are the same underlying asset and do not establish an AMM trading pair.
For each selected protocol, run exact-input and exact-output quotes in both token directions below. fewv2 selects Ring FewV2; v2, v3, and v4 select the separate Uniswap protocols. Use mixed for a mixed-route check, or fewv2,v2,v3,v4,mixed for normal protocol selection. A mixed-route check requires a suitable path and more than one permitted hop. At the recorded block, fewv2 on mainnet is a no-liquidity check, not a positive quote case.
TOKEN_IN=0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73
TOKEN_OUT=0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168
AMOUNT_IN=0.001
AMOUNT_OUT=1
BLOCK=$(node -e 'new (require("@ethersproject/providers").JsonRpcProvider)(process.env.JSON_RPC_PROVIDER_ROBINHOOD, 4663).getBlockNumber().then(n => console.log(n)).catch(() => process.exitCode = 1)')
PROTOCOLS=v2
./bin/cli quote --chainId 4663 --router alpha --tokenIn "$TOKEN_IN" --tokenOut "$TOKEN_OUT" --amount "$AMOUNT_IN" --exactIn --protocols "$PROTOCOLS" --requestBlockNumber "$BLOCK" --minSplits 1 --maxSplits 1 --maxSwapsPerPath 3
./bin/cli quote --chainId 4663 --router alpha --tokenIn "$TOKEN_IN" --tokenOut "$TOKEN_OUT" --amount "$AMOUNT_OUT" --exactOut --protocols "$PROTOCOLS" --requestBlockNumber "$BLOCK" --minSplits 1 --maxSplits 1 --maxSwapsPerPath 3
./bin/cli quote --chainId 4663 --router alpha --tokenIn "$TOKEN_OUT" --tokenOut "$TOKEN_IN" --amount 1 --exactIn --protocols "$PROTOCOLS" --requestBlockNumber "$BLOCK" --minSplits 1 --maxSplits 1 --maxSwapsPerPath 3
./bin/cli quote --chainId 4663 --router alpha --tokenIn "$TOKEN_OUT" --tokenOut "$TOKEN_IN" --amount 0.0001 --exactOut --protocols "$PROTOCOLS" --requestBlockNumber "$BLOCK" --minSplits 1 --maxSplits 1 --maxSwapsPerPath 3Read and record BLOCK once immediately before the checks. The public RPC may not retain metadata or contract state at older blocks; 59328354 above is the historical evidence snapshot, not a guaranteed executable CLI block.
The CLI quote options and transaction-encoding implementation are unchanged from release/sor-sdk-dependency-versions@8e6a4d9. Tick statistics use one shared display path on all chains: complete counts are summed, including valid zeros; missing or incomplete metadata prints Total ticks crossed: unavailable without interrupting quote output. To inspect calldata, append --recipient "$RECIPIENT". The existing CLI requests UniversalRouter V2.0 for exact input, but SwapRouter02 with deadline: 100 for exact output. That deadline is expired, so exact-output CLI calldata is not ready to submit. Its exact-output path also uses the CLI startup block minus 10 instead of --requestBlockNumber. Record the exact-output CLI option limitations separately from routing failures. V4 and MixedQuoterV2 do not return tick counts, so their tick statistics are unavailable. Applications should explicitly select UniversalRouter for Ring FewV2 routes. These commands do not sign or broadcast; omit --simulate for RPC-only checks.
The same commands can use an existing isolated fork by setting JSON_RPC_PROVIDER_ROBINHOOD=http://127.0.0.1:18545 and BLOCK to the fork's L2 block. Any test tokens or pools must already exist in that fork. A fork of unchanged mainnet state retains the same liquidity and USD-reference limitations.
A successful quote check must return an actual route and quote at the block selected by the CLI; the CLI can return exit code 0 after logging Could not find route. Record missing liquidity, unavailable RPC state, or the USD-reference error as blocked checks. A fork fixture result is separate from a mainnet quote result.
Testing
Unit Tests
First make sure you have run npm install and npm run build.
npm run testIntegration Tests
Make sure the .env file is configured to connect to mainnet and other chains. See the CLI section below for more details.
npm run integ-testTenderly Simulations
Quotes can be simulated on Tenderly
Ensure you set the following environment variables:
process.env.TENDERLY_BASE_URL!,
process.env.TENDERLY_USER!,
process.env.TENDERLY_PROJECT!,
process.env.TENDERLY_ACCESS_KEY!,CLI
The package can be run as a CLI for testing purposes.
First create a .env file in the root of the project and configure:
JSON_RPC_PROVIDER = '<JSON_RPC_PROVIDER>'To run on chains other than mainnet set up a connection by specifying the environment variable
JSON_RPC_PROVIDER_GORLI = '<JSON_RPC_PROVIDER>'
JSON_RPC_PROVIDER_OPTIMISM = '<JSON_RPC_PROVIDER>'
JSON_RPC_PROVIDER_OPTIMISM_GOERLI = '<JSON_RPC_PROVIDER>'
JSON_RPC_PROVIDER_ARBITRUM_ONE = '<JSON_RPC_PROVIDER>'
JSON_RPC_PROVIDER_ARBITRUM_GOERLI = '<JSON_RPC_PROVIDER>'
JSON_RPC_PROVIDER_POLYGON = '<JSON_RPC_PROVIDER>'
JSON_RPC_PROVIDER_POLYGON_MUMBAI = '<JSON_RPC_PROVIDER>'
JSON_RPC_PROVIDER_CELO = '<JSON_RPC_PROVIDER>'
JSON_RPC_PROVIDER_CELO_ALFAJORES = '<JSON_RPC_PROVIDER>'
JSON_RPC_PROVIDER_BNB = '<JSON_RPC_PROVIDER>'
JSON_RPC_PROVIDER_ROBINHOOD = '<JSON_RPC_PROVIDER>'
JSON_RPC_PROVIDER_AVALANCHE = '<JSON_RPC_PROVIDER>'
JSON_RPC_PROVIDER_BASE = '<JSON_RPC_PROVIDER>'Then from the root directory you can execute the CLI.
Examples
Some examples to use for manual CLI testing.
Mainnet
./bin/cli quote --tokenIn 0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48 --tokenOut 0x1f9840a85d5af5bf1d1762f925bdaddc4201f984 --amount 1000 --exactIn --recipient 0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B --protocols v2,v3
Best Route:
100.00% = USDC -- 0.3% --> UNI
Raw Quote Out:
35.72
Gas Adjusted Quote Out:
34.03
Gas Used Quote Token: 1.691772
Gas Used USD: 47.592951
Calldata: 0x414bf389000000000000...
Value: 0x00
blockNumber: "13088815"
estimatedGasUsed: "113000"
gasPriceWei: "130000000000"
./bin/cli quote-to-ratio --token0 0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48 --token1 0xdac17f958d2ee523a2206206994597c13d831ec7 --feeAmount 3000 --recipient 0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B --token0Balance 1000 --token1Balance 2000 --tickLower -120 --tickUpper 120
Best Route:
100.00% = USDT -- 0.05% --> USDC
Raw Quote Exact In:
392.68
Gas Adjusted Quote In}:
346.13
Gas Used Quote Token: 46.550010
Gas Used USD: 46.342899
Calldata: 0x414bf389000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000001f4000000000000000000000000ab5801a7d398351b8be11c439e05c5b3259aec9b000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000176a736c000000000000000000000000000000000000000000000000000000001764f8650000000000000000000000000000000000000000000000000000000000000000
Value: 0x00
blockNumber: "13239188"
estimatedGasUsed: "113000"
gasPriceWei: "116690684398"
./bin/cli quote --tokenIn 0x0391D2021f89DC339F60Fff84546EA23E337750f --tokenOut 0x4d224452801ACEd8B2F0aebE155379bb5D594381 --amount 10000 --exactIn --recipient 0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B --protocols v2,v3,mixed
Best Route:
[V2 + V3] 100.00% = BOND -- [0x6591c4BcD6D7A1eb4E537DA8B78676C1576Ba244] --> USDC -- 0.3% [0xB07Fe2F407F971125D4EB1977f8aCEe8846C7324] --> APE
Raw Quote Exact In:
10437.85
Gas Adjusted Quote In:
10433.83
Gas Used Quote Token: 4.018625
Gas Used USD: 29.669402
Calldata: 0x5ae401dc0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000e4472b43f300000000000000000000000000000000000000000000021e19e0c9bab240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020000000000000000000000000391d2021f89dc339f60fff84546ea23e337750f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000104b858183f00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000080000000000000000000000000ab5801a7d398351b8be11c439e05c5b3259aec9b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002358df5b3b4459a3f5b000000000000000000000000000000000000000000000000000000000000002ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb84d224452801aced8b2f0aebe155379bb5d59438100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Value: 0x00
blockNumber: "15303839"
estimatedGasUsed: "434000"
gasPriceWei: "38218865879"
Total ticks crossed: 7Rinkeby
./bin/cli quote --tokenIn 0x5592ec0cfb4dbc12d3ab100b257153436a1f0fea --tokenOut 0x4DBCdF9B62e891a7cec5A2568C3F4FAF9E8Abe2b --amount 200000 --exactIn --minSplits 1 --router alpha --chainId 4Sepolia
./bin/cli quote --tokenIn 0x7AF17A48a6336F7dc1beF9D485139f7B6f4FB5C8 --tokenOut 0xA7a151d60Bbd522d3611C2dB3F1F972Ee9904B3e --amount 10 --exactIn --minSplits 1 --router alpha --chainId 11155111Kovan
./bin/cli quote --tokenIn 0x4f96fe3b7a6cf9725f59d353f723c1bdb64ca6aa --tokenOut 0xd0a1e359811322d97991e03f863a0c30c2cf029c --amount 10 --exactIn --minSplits 1 --router alpha --chainId 42Ropsten
./bin/cli quote --tokenIn 0x07865c6e87b9f70255377e024ace6630c1eaa37f --tokenOut 0xc778417e063141139fce010982780140aa0cd5ab --amount 200000 --exactIn --minSplits 1 --router alpha --chainId 3Optimism
./bin/cli quote --tokenIn 0x7F5c764cBc14f9669B88837ca1490cCa17c31607 --tokenOut 0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1 --amount 200000 --exactIn --minSplits 1 --router alpha --chainId 10
./bin/cli quote --tokenIn 0x7F5c764cBc14f9669B88837ca1490cCa17c31607 --tokenOut 0x4200000000000000000000000000000000000042 --amount 1 --exactIn --minSplits 1 --protocols v2 --router alpha --chainId 10Optimism-Goerli
./bin/cli quote --tokenIn 0x7E07E15D2a87A24492740D16f5bdF58c16db0c4E --tokenOut 0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1 --amount 200000 --exactIn --minSplits 1 --router alpha --chainId 420Optimistic-Kovan
./bin/cli quote --tokenIn 0x7F5c764cBc14f9669B88837ca1490cCa17c31607 --tokenOut 0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1 --amount 200 --exactIn --minSplits 1 --router alpha --chainId 69Arbitrum
./bin/cli quote --tokenIn 0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9 --tokenOut 0x82af49447d8a07e3bd95bd0d56f35241523fbab1 --amount 20000 --exactIn --minSplits 1 --router alpha --chainId 42161 --debugArbitrum-Rinkeby
./bin/cli quote --tokenIn 0x09b98f8b2395d076514037ff7d39a091a536206c --tokenOut 0xb47e6a5f8b33b3f17603c83a0535a9dcd7e32681 --amount 200 --exactIn --minSplits 1 --router alpha --chainId 421611Arbitrum-Goerli
./bin/cli quote --tokenIn 0xe39ab88f8a4777030a534146a9ca3b52bd5d43a3 --tokenOut 0x8FB1E3fC51F3b789dED7557E680551d93Ea9d892 --amount 200 --exactIn --minSplits 1 --router alpha --chainId 421613Polygon Mumbai
./bin/cli quote --tokenIn 0x001b3b4d0f3714ca98ba10f6042daebf0b1b7b6f --tokenOut 0x9c3c9283d3e44854697cd22d3faa240cfb032889 --amount 1 --exactIn --protocols v3 --recipient 0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B --minSplits 1 --router alpha --chainId 80001Polygon Mainnet
./bin/cli quote --tokenIn 0x2791bca1f2de4661ed88a30c99a7a9449aa84174 --tokenOut 0x7ceb23fd6bc0add59e62ac25578270cff1b9f619 --amount 5 --exactIn --minSplits 1 --protocols v3 --router alpha --chainId 137Celo Mainnet
./bin/cli quote --tokenIn CELO --tokenOut 0x765DE816845861e75A25fCA122bb6898B8B1282a --amount 5 --exactIn --minSplits 1 --protocols v3 --router alpha --chainId 42220BNB Mainnet
./bin/cli quote --tokenIn 0x55d398326f99059fF775485246999027B3197955 --tokenOut 0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d --amount 1 --exactIn --minSplits 1 --protocols v3 --router alpha --chainId 56AVAX Mainnet
./bin/cli quote --tokenIn 0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E --tokenOut 0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7 --amount 1000 --exactIn --minSplits 1 --protocols v3 --router alpha --chainId 43114BASE Mainnet
./bin/cli quote --tokenIn 0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA --tokenOut 0x4200000000000000000000000000000000000006 --amount 10 --exactIn --minSplits 1 --protocols v3 --router alpha --chainId 8453ZKSYNC Mainnet
./bin/cli quote --tokenIn 0x5aea5775959fbc2557cc8789bc1bf90a239d9a91 --tokenOut 0x1d17cbcf0d6d143135ae902365d2e5e2a16538d4 --amount 10 --exactIn --minSplits 1 --protocols v3 --router alpha --chainId 324WorldChain Mainnet
./bin/cli quote --tokenIn 0x4200000000000000000000000000000000000006 --tokenOut 0x79A02482A880bCE3F13e09Da970dC34db4CD24d1 --amount 0.1 --exactIn --minSplits 1 --protocols v3 --router alpha --chainId 480UNICHAIN Sepolia
./bin/cli quote --tokenIn 0x4200000000000000000000000000000000000006 --tokenOut 0x31d0220469e10c4E71834a79b1f276d740d3768F --amount 0.1 --exactIn --minSplits 1 --protocols v3 --router alpha --chainId 1301Unichain Mainnet
./bin/cli quote --tokenIn 0x4200000000000000000000000000000000000006 --tokenOut 0x078D782b760474a361dDA0AF3839290b0EF57AD6 --amount 0.1 --exactIn --minSplits 1 --protocols v3 --router alpha --chainId 130Monad Testnet
./bin/cli quote --tokenIn 0x760AfE86e5de5fa0Ee542fc7B7B713e1c5425701 --tokenOut 0xfBC2D240A5eD44231AcA3A9e9066bc4b33f01149 --amount 0.1 --exactIn --minSplits 1 --protocols v2 --router alpha --chainId 10143Base Sepolia
./bin/cli quote --tokenIn 0x4200000000000000000000000000000000000006 --tokenOut 0x036CbD53842c5426634e7929541eC2318f3dCF7e --amount 0.1 --exactIn --minSplits 1 --protocols v3 --router alpha --chainId 84532Soneium
./bin/cli quote --tokenIn 0x4200000000000000000000000000000000000006 --tokenOut 0xbA9986D2381edf1DA03B0B9c1f8b00dc4AacC369 --amount 0.1 --exactIn --minSplits 1 --protocols v3 --router alpha --chainId 1868Adding a new Chain
The main components to complete are:
- Deploy contracts on chain, add the pools to subgraph
- Populate v3 providers in
src/providers/v3/subgraph-providerandsrc/providers/v3/static-subgraph-provider - Populate chainId and addresses in
src/util/chains.tsandsrc/util/addresses.ts - Populate token providers in
src/providers/caching-token-providerandsrc/providers/token-provider.ts - Populate gas constants in
src/routers/alpha-router/gas-models/* - Populate bases in
src/routers/legacy-router/bases.ts - Populate
test/integ/routers/alpha-router/alpha-router.integration.test.tsandsrc/providers/v2/static-subgraph-provider.ts - Populate
src/routers/alpha-router/* - Add a log to
/CHANGELOG.md - Run
npm run integ-testsuccessfully
Troubleshooting
ProviderGasLimit errors
The package sends many large multicall requests to nodes. You must ensure that your node provider's eth_call gas limit is high enough to successfully process the RPC calls.
By default each eth_call will consume up to:
- 132,000,000 gas on Optimism
- 120,000,000 gas on Arbitrum
- 50,000,000 gas on Celo
- 150,000,000 gas on every other network (Mainnet, Goerli, etc.)
These parameters should work on Infura and Alchemy by default.
This total amount of gas each eth_call can consume is equal to the multicallChunk config value multiplied by the gasLimitPerCall config value. If you are using a node provider with a lower gas limit per eth_call you will need to override the default V3QuoteProvider with an instance that lowers the multicallChunk and gasLimitPerCall parameters such that the multiplication is below your node providers limit. Lowering these values will cause each multicall to consume less gas. See here for examples of how to set these values. Note some providers have different limits per chain.
If you are running your own node, we recommend you configure start your node with a higher gas limit per call. For example, on Geth you can use the command line argument --rpc.gascap 150000000 to raise the limit to 150m, which is enough to run the default configuration of this package.
If you are using Hardhat mainnet forking, you should add blockGasLimit: 150_000_000 to your Hardhat config to use the default package configuration.
