For the complete documentation index, see llms.txt. This page is also available as Markdown.

Request for Quote Protocols

Request for Quote (RFQ) protocols work differently from on-chain protocols. Instead of reading pool data from the chain, they fetch prices from off-chain market makers via WebSocket or API.

You ask for a quote for a specific trade size, and they return a price. Quotes can be:

  • Indicative — estimated prices used for simulation.

  • Binding — firm prices, valid for a short time, used at execution.

Tycho supports streaming, simulating, and executing RFQ quotes as part of multi-protocol swaps.

Currently, Tycho supports the following RFQ protocols:

Protocol
Simulation Time
Credentials
Chains

bebop

0.5 µs

Required

Ethereum, Base

hashflow

0.4 µs

Required

Ethereum

liquorice

0.4 µs

Required

Ethereum

metric

-

None (public endpoint)

Base, Robinhood

native

-

Required

Ethereum, Base, Arbitrum, BSC, Robinhood

On Ethereum, Metric is available as a pAMM venue on the pAMM price level stream.

Quickstart

The RFQ quickstart is similar to the other protocols quickstart.

See the code here. As of now, Bebop, Hashflow, Liquorice, Metric and Native are the only supported providers.

You need to set up the API credentials of the desired RFQs to access live pricing data and quoting, as well as your private key if you wish to execute against the Tycho Router:

unset HISTFILE # to not save your credentials to your shell history
export BEBOP_KEY=<your-bebop-api-key>
export HASHFLOW_USER=<your-hashflow-api-username>
export HASHFLOW_KEY=<your-hashflow-api-key>
export LIQUORICE_USER=<your-liquorice-api-username>
export LIQUORICE_KEY=<your-liquorice-api-key>
export NATIVE_API_KEY=<your-native-api-key>
export PRIVATE_KEY=<your-wallet-private-key>

Metric needs no credentials: the client defaults to Metric's public endpoint. Override it with METRIC_API_URL, and set METRIC_SECRET_KEY only if your endpoint requires one. The example registers Metric under the --run-pamm-protocols flag, which is on by default, so it runs even without any authenticated RFQ credentials.

Then run the example:

You’ll need to request credentials directly from RFQ providers.

What it does

The quickstart:

  • Connects to the RFQ stream and fetches live price updates.

  • Simulates the best available amount out for a given pair (default: 10 USDC → WETH on mainnet).

  • Encodes the swap and prepares calldata to execute it via the Tycho Router.

If you want to see results for a different token, amount, minimum TVL, or chain, you can set additional flags:

This example would seek the best swap for 10 USDC -> WETH on Base.

Set up

You’ll need to configure:

  • Tycho URL (by default "tycho-beta.propellerheads.xyz")

  • Tycho API key

  • RFQ API keys (Have a look at src/rfq/constants.rs to see the authentication variables that are expected)

  • Private key if you wish to execute the swap against the Tycho Router

To get token information from Tycho Indexer RPC please use load_all_tokens.

RFQClient

Each RFQ protocol will have its own client. The client can stream live prices updates and request binding quotes.

Example setup for Bebop:

TVL threshold is specified in USD, as most RFQ quotes are USD-denominated. This setting filters out token pairs with low liquidity on the RFQ side, helping avoid thin or illiquid quotes.

Quote tokens: You can optionally specify quote tokens when configuring the RFQ client to define which tokens the client should consider “approved” for TVL normalization purposes. The client uses this approved quote token list exclusively for TVL filtering and does not use it for quote requests or trade execution.

You should specify USD-priced stablecoins (e.g., USDC, USDT, DAI) as quote tokens, since currently-supported RFQ providers quote most of their currently supported liquidity in USD stablecoins. This ensures the client calculates TVL accurately when comparing pairs with different quote tokens. For instance, if you receive price levels for an ETH/WBTC pair where WBTC is the quote token, the client will look up the WBTC price in one of your approved quote tokens (USD stablecoins) to properly calculate the TVL in dollar terms. If you don’t explicitly set quote tokens, the client uses chain-specific defaults.

Note: Some RFQ providers may support tokens that Tycho does not. Because execution happens through the Tycho Router, it’s important to ensure that all tokens used in RFQ quotes are also supported by Tycho.

Stream: Real-Time Price Updates

The RFQStreamBuilder handles registration of multiple RFQ clients and merges their message streams. It merges updates from one or more RFQ clients and decodes them into Update messages:

  • Use add_client() for each RFQ provider.

  • Streams that return errors are removed automatically.

RFQ streams are timestamped, not block-based. Each update provides the full known state from the provider at that moment (not just deltas). The removed_pairs field indicates any pairs that disappeared since the last update. The new_pairs field contains all the currently available pairs.

Venue components

By default, Hashflow, Liquorice, Bebop and Native stream one component per token pair, and a swap leaves the state unchanged. .component_layout(ComponentLayout::AllPairs) on the client builder makes the client stream one component for all token pairs instead. Register an all-pairs client with HashflowAllPairsState, LiquoriceAllPairsState, BebopAllPairsState or NativeAllPairsState under the name <venue>_all_pairs, for example .add_client::<HashflowAllPairsState>("hashflow_all_pairs", Box::new(client)). The two layouts stream under different names, so one stream builder can carry a per-pair and an all-pairs client of the same venue. ComponentLayout and QuoteRule are in tycho_simulation::rfq::models.

An all-pairs component has these properties. Its tokens are every token the venue quotes, and its swap_directions static attribute lists the directions it quotes, 40 bytes each: the token in, then the token out. Add graph edges from that attribute, not from every pair of tokens, and rebuild them when the component's tokens change between updates.

A swap records what it used in the new_state it returns. How often one route may quote a venue is its quote rule, carried as the quote_rule static attribute:

  • Hashflow and Liquorice name their market makers. By default every maker quotes once per route (once_per_maker). .quote_rule(QuoteRule::OncePerVenue) on the client builder limits the venue to one quote per route.

  • Bebop and Native name no maker and quote once per route (once_per_venue).

The rule takes effect wherever your algorithm threads new_state between two uses of the component. A swap the rule refuses returns a recoverable No liquidity error.

Request the firm quote on the state the swap was simulated on, with the simulated amount. For Hashflow and Liquorice, that state picks the market maker the firm quote asks. On the new_state the swap returned, that maker is already used.

Simulation

You can simulate a swap against an RFQ state using:

This returns an indicative output amount, which you can use to decide if this swap is worth including.

Encoding

After choosing the best swap, you can use Tycho Execution to encode it. This is very similar to the encoding done in the general quickstart.

Create a solution object

The key parameters are the quoted expected amount out and the min amount out you accept below it. The router uses both as guardrails, protecting you against slippage and MEV. The quickstart sets the min amount out 0.25% below the quote.

Build the Swap and Solution:

When working with RFQs, two fields are required in Swap:

  • protocol_state: This is needed to enable the runtime generation of a binding quote at encoding time—for example:

  • estimated_amount_in : This represents the estimaed input amount for the quote request. It’s especially important when the swap path is complex (e.g., involving multiple hops), where the actual input amount may differ slightly because of slippage. We recommend setting estimated_amount_in a bit higher than your expected value. Many RFQs enforce that execution can only occur for amounts less than or equal to the quoted base amount—so setting it conservatively helps avoid dropping funds. If the actual required input exceeds your estimate, any leftover tokens will remain in the Tycho Router.

This mechanism also makes RFQs composable with other on-chain swaps. That enables hybrid routing strategies, such as a path like Uniswap → RFQ → Curve, seamlessly combining RFQ-based and traditional on-chain routes.

Encode solution

Encode full method calldata

You need to build the full calldata for the router. Tycho handles the swap encoding, but you control the full input to the router method. This quickstart provides helper functions (encode_tycho_router_call and sign_permit)

Use it as follows:

Execution

This step allows you to test or perform real transactions based on the best available swap options. It needs the PRIVATE_KEY environment variable from Quickstart. Handle that key securely and never expose it publicly.

Once the best swap is found you can:

  1. Simulate the swap: Tests the swap without executing it on-chain. It simulates an approval (for permit2) and a swap transaction on the node. If the status is false, the simulation has failed. You can print the full simulation output for detailed failure information.

  2. Execute the swap: Performs the swap on-chain using your real funds. The process performs an approval (for permit2) and a swap transaction. You'll receive transaction hashes and statuses. After a successful execution, the program will exit. If the transaction fails, the program continues to stream new price updates.

  3. Skip this swap: Ignores this swap. Then the program resumes listening for price updates.

Because the RFQ will only let you swap up to the amount of tokens specified in the quote, when the RFQ swap happens after another protocol in a sequential swap, if positive slippage occurs during the preceding swap, any additional input tokens beyond the permitted quote amount will remain in the Tycho Router and not be sent to the RFQ protocol.

pAMM Price Level Stream

Besides the RFQ clients above, Tycho Simulation consumes Titan Builder's pAMM price level stream: a WebSocket of per-pair quote ladders for a subset of the pAMMs Titan serves. It only serves Ethereum Mainnet.

PriceLevelStreamBuilder turns those frames into the same Update messages the protocol stream emits, so you consume it like any other stream:

Quotes target the block currently being built, so the stream marks every update partial and supersedes the previous one for the pairs it contains. Each update carries the block its frame targets; that number only decreases after every component has been removed, when the stream re-anchors on the next frame. The stream never terminates — run it in its own task alongside your protocol stream.

Frames are best effort, not complete snapshots: a venue or a pair can be absent from one frame and present in the next, so the stream never removes a component because a frame omits it. Instead it serves a component for stale_after (default 24 s) after the last frame that carried it, then lists the component in removed_pairs; the next frame that carries it adds it back in new_pairs. Every state also refuses to quote once its frame is 12 s old, so you cannot quote a ladder past the block it targeted even before the removal arrives. Treat a removal like any other: stop routing through the component until it reappears in new_pairs. If you quote states more than 12 s after they arrive by design (a batch simulator, a validation harness), without_quote_guard() emits states that never refuse; the 24 s removal still applies, and a quote from such a state may no longer be fillable.

with_known_pamms() serves these venues: Bebop, FermiSwap, Kipseli, Metric, TaurusFi and Tempest. Titan does not stream every venue at all times, so the live set can be smaller.

Components arrive as fallback:{pamm}, where {pamm} is the venue name for a known venue or its address for an auto-detected one: tycho-execution routes those swaps through TychoFallbackRouter, which retries a reverted pAMM swap on the fallback pool named in the swap's user_data. without_fallback_router() keeps every venue on the direct pricelevelstream:{pamm} path, where a stale quote reverts the route.

Last updated

Was this helpful?