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

Simulation

To enable simulations for a newly added protocol, it must first be integrated into the Tycho monorepo. Please submit a pull request to the repository targeting crates/tycho-simulation.

Native Integration

In order to add a new native protocol, you will need to complete the following high-level steps:

  1. Create a protocol state struct that contains the state of the protocol, and implements the ProtocolSim trait (see here).

  2. Create a tycho decoder for the protocol state: i.e. implement TryFromWithBlock for ComponentWithState to your new protocol state.

Each native protocol should have its own module under crates/tycho-simulation/src/evm/protocol.

VM Integration

To create a VM integration, provide a manifest file and an implementation of the corresponding adapter interface. The Tycho monorepo contains the tools to integrate DEXs and other onchain liquidity protocols into Tycho.

Example Implementations

The following exchanges are integrated with the VM approach:

  • Balancer V2 (see code here)

Install prerequisites

  1. Install Foundry, start by downloading and installing the Foundry installer:

    curl -L https://foundry.paradigm.xyz | bash

    then start a new terminal session and run

    foundryup
  2. Clone the Tycho monorepo:

    git clone https://github.com/propeller-heads/tycho
  3. Install dependencies:

    cd ./tycho/protocols/adapter-integration/evm/
    forge install

Understanding the ISwapAdapter

Read the documentation of the Ethereum Solidity interface. It describes the functions that need to be implemented and the manifest file.

Additionally, read through the docstring of the ISwapAdapter.sol interface and the ISwapAdapterTypes.sol interface, which defines the data types and errors the adapter interface uses. You can also generate the documentation locally and look at the generated documentation in the ./docs folder:

Implementing the ISwapAdapter interface

Your integration should be in a separate directory in the protocols/adapter-integration/evm/src folder. Start by copying the template directory:

Implement the ISwapAdapter interface in the ./protocols/adapter-integration/evm/src/<your-adapter-name>.sol file. See Balancer V2 implementation for reference.

Testing your implementation

  1. Set up test files:

    • Copy evm/test/TemplateSwapAdapter.t.sol

    • Rename to <your-adapter-name>.t.sol

  2. Write comprehensive tests:

    • Test all implemented functions.

    • Use fuzz testing (see Foundry test guide, especially the chapter for Fuzz testing)

    • Reference existing test files: BalancerV2SwapAdapter.t.sol

  3. Configure fork testing (run a local mainnet fork against actual contracts and data):

    • Set ETH_RPC_URL environment variable

    • Use your own Ethereum node or services like Infura

  4. Run the tests with

Add implementation to Tycho simulation

Once you have the swap adapter implemented for the new protocol, you will need to:

  1. Generate the adapter runtime file by running the protocols/adapter-integration/evm/scripts/buildRuntime.sh script with the proper input parameters. For example, in order to build the Balancer V2 runtime, the following command can be run:\

  2. Add the associated adapter runtime file to crates/tycho-simulation/src/evm/protocol/vm/assets. Make sure to name the file according to the protocol name used by Tycho Indexer in the following format: <Protocol><Version>Adapter.evm.runtime. For example: vm:balancer_v2 will be BalancerV2Adapter.evm.runtime. Following this naming format is important as we use an automated name resolution for these files.

Filtering

If your implementation does not support all pools indexed for a protocol, you can create a filter function to handle this. Add your filter to filters.rs alongside the existing ones, and add your protocol name to the EXCHANGES_REQUIRING_FILTER constant in evm/stream.rs. This warns callers if they stream your protocol without providing the required filter.

Gas Estimation

The gas estimator builds total execution gas from two components: the simulation-reported swap gas and token-transfer overhead that simulation does not capture. Which overhead applies depends on your protocol's behavior.

Native Integrations

Your get_amount_out implementation must return a gas estimate. Measure only what happens inside the protocol's swap() call, including any token transfers that occur during a callback.

After implementing, add your protocol to the relevant constant slices in gas_estimator.rs:

  • PROTOCOLS_CALLBACK: The pool pulls tokens inside a callback fired during swap(). The simulation captures that transfer, so the estimator adds no separate input-transfer cost.

  • PROTOCOLS_OPTIMIZABLE_TRANSFER_IN: The router might send tokens directly to the pool, skipping the router-to-pool hop. The estimator adds no router-to-pool transfer cost for non-split strategies.

  • PROTOCOLS_NEEDING_APPROVAL: The pool pulls tokens from the router via approve + transferFrom (ProtocolWillDebit). The simulation includes the transferFrom gas, but not the approve (25,000 gas); the estimator adds it separately.

  • PROTOCOLS_OUTPUT_TO_ROUTER: The pool sends output to msg.sender (the router) instead of to an explicit receiver. The estimator adds an extra forward transfer from the router to the final receiver.

A protocol can appear in more than one list.

VM Integrations

In your adapter contract's swap function, measure gas with gasleft() and return it in the Trade struct. The measurement must cover only the pool call itself.

Do not include gas for:

  • Token transfers (input or output): the Dispatcher handles these via TransferManager

  • approve() calls: also handled by the Dispatcher

Including them would double-count with what the gas estimator adds based on your protocol's category.

Last updated

Was this helpful?