All pages
Powered by GitBook
1 of 2

Loading...

Loading...

Simulation

To enable simulations for a newly added protocol, it must first be integrated into the Tycho Simulation repository. Please submit a pull request to the repository to include it.

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 ).

  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 tycho-simulation/src/evm/protocol.

VM Integration

To create a VM integration, provide a manifest file and an implementation of the corresponding adapter interface. is a library 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 )

Install prerequisites

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

    then start a new terminal session and run

  2. Clone the Tycho Protocol SDK:

  3. Install dependencies:

Understanding the ISwapAdapter

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

Additionally, read through the docstring of the interface and the 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 evm/src folder. Start by cloning the template directory:

Implement the ISwapAdapter interface in the ./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

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 script in our SDK repository 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 tycho-simulations/src/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. This filter can then be used when registering an exchange in the ProtocolStreamBuilder. See for example implementations.

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

  • 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

  • Run the tests with

  • here
    Tycho Protocol SDK
    here
    Foundry
    Ethereum Solidity
    ISwapAdapter.sol
    ISwapAdapterTypes.sol
    evm/scripts/buildRuntime.sh
    here
    cd ./evm
    forge test
    curl -L https://foundry.paradigm.xyz | bash
    foundryup
    git clone https://github.com/propeller-heads/tycho-protocol-lib
    cd ./tycho-protocol-lib/evm/
    forge install
    cd ./evm/
    forge doc
    cp ./evm/src/template ./evm/src/<your-adapter-name>
    >>> cd evm
    >>> ./scripts/buildRuntime.sh -c “BalancerV2SwapAdapter” -s “constructor(address)” -a “0xBA12222222228d8Ba445958a75a0704d566BF2C8”

    Ethereum: Solidity

    Swap/Exchange Protocol Guide

    Implementing the Protocol

    To integrate an EVM exchange protocol:

    1. Implement the interface.

    2. Create a manifest file summarizing the protocol's metadata.

    The Manifest File

    The manifest file contains author information and additional static details about the protocol and its testing. Here's a list of all valid keys:

    Key Functions

    Price (optional)

    Calculates marginal prices for specified amounts.

    The marginal price which is distinct from the executed price: swap(amount_in) / amount_in! The marginal price is defined as the price to trade an arbitrarily small (almost zero) amount after the trade of (amount). E.g. the marginal price of a uniswapv2 pool at zero is: price(0) = reserve0/reserve1

    • Return marginal prices in buyToken/sellToken units.

    • Include all protocol fees (use minimum fee for dynamic fees).

    • If you don't implement this function, flag it accordingly in capabilities and make it revert using the NotImplemented error.

    • While optional, we highly recommend implementing this function. If unavailable, we'll numerically estimate the price function from the swap function.

    Swap

    Simulates token swapping on a given pool.

    • Execute the swap and change the VM state accordingly.

    • Include a gas usage estimate for each amount (use gasleft() function).

    • Return a Trade struct with a price attribute containing price(specifiedAmount).

    GetLimits

    Retrieves token trading limits.

    • Return the maximum tradeable amount for each token.

    • The limit is reached when the change in received amounts is zero or close to zero.

    • Overestimate the limit if in doubt.

    • Ensure the swap function doesn't error with LimitExceeded for amounts below the limit.

    getCapabilities

    Retrieves pool capabilities.

    getTokens (optional)

    Retrieves tokens for a given pool.

    • We mainly use this for testing, as it's redundant with the required substreams implementation.

    getPoolIds (optional)

    Retrieves a range of pool IDs.

    • We mainly use this for testing. It's okay not to return all available pools here.

    • This function helps us test against the substreams implementation.

    • If you implement it, it saves us time writing custom tests.

    If the price function isn't supported, return Fraction(0, 1) for the price (we'll estimate it numerically).

    ISwapAdapter.sol
    yamlCopy# Author information helps us reach out in case of issues
    author:
      name: Propellerheads.xyz
      email: [email protected]
    
    # Protocol Constants
    constants:
      # Minimum gas usage for a swap, excluding token transfers
      protocol_gas: 30000
      # Minimum expected capabilities (individual pools may extend these)
      # To learn about Capabilities, see ISwapAdapter.sol)
      capabilities:
        - SellSide
        - BuySide
        - PriceFunction
    
    # Adapter contract (byte)code files
    contract: 
      # Contract runtime (deployed) bytecode (required if no source is provided)
      runtime: UniswapV2SwapAdapter.bin
      # Source code (our CI can generate bytecode if you submit this)
      source: UniswapV2SwapAdapter.sol
    
    # Deployment instances for chain-specific bytecode
    # Used by the runtime bytecode build script
    instances:
      - chain:
          name: mainnet
          id: 1
        # Constructor arguments for building the contract
        arguments:
          - "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f"
    
    # Automatic test cases (useful if getPoolIds and getTokens aren't implemented)
    tests:
      instances:
        - pool_id: "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc"
          sell_token: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
          buy_token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
          block: 17000000
          chain:
            name: mainnet
            id: 1
    function price(
        bytes32 poolId,
        IERC20 sellToken,
        IERC20 buyToken,
        uint256[] memory sellAmounts
    ) external returns (Fraction[] memory prices);
    function swap(
        bytes32 poolId,
        IERC20 sellToken,
        IERC20 buyToken,
        OrderSide side,
        uint256 specifiedAmount
    ) external returns (Trade memory trade);
    function getLimits(bytes32 poolId, address sellToken, address buyToken)
        external
        returns (uint256[] memory limits);
    function getCapabilities(bytes32 poolId, IERC20 sellToken, IERC20 buyToken)
        external
        returns (Capability[] memory);
    function getTokens(bytes32 poolId)
        external
        returns (IERC20[] memory tokens);
    function getPoolIds(uint256 offset, uint256 limit)
        external
        returns (bytes32[] memory ids);