Skip to content

OverFlow and state.get_amount errors #310

@johnwhitton

Description

@johnwhitton

Hi,

We're running tycho-simulation and have been experiencing a couple of errors.

  1. A problem with state.get_amount_out - this appears whenstate.get_amount_out() call fails (returns an error) for a specific pool
  2. A problem with swap_math.rs for uniswap-v4

Filtering out v4 pools with hooks, helped reduce the frequency of each of these.
But we are still receiving intermittent issues and occasionally unable to retrieve state for components which we have received.

Below is the code I am using for setting up and filtering our streams and also some reference information.

Please let me know if I have something misconfigured, or if there any best practices on retries or error handling I should put in place.

🙏 💙 🙏

Stream creation and filtering

Creation

        use tycho_simulation::tycho_client::feed::component_tracker::ComponentFilter;
        use tycho_simulation::{
            evm::{
                protocol::{
                    uniswap_v2::state::UniswapV2State, uniswap_v3::state::UniswapV3State,
                    uniswap_v4::state::UniswapV4State,
                },
                stream::ProtocolStreamBuilder,
            },
            protocol::models::{ProtocolComponent, Update},
            tycho_common::models::Chain,
        };

Filtering

      let tvl_filter = ComponentFilter::with_tvl_range(min_tvl, max_tvl);

        let mut protocol_stream = ProtocolStreamBuilder::new(hostname, self.chain)
            .exchange::<UniswapV2State>("uniswap_v2", tvl_filter.clone(), None)
            .exchange::<UniswapV3State>("uniswap_v3", tvl_filter.clone(), None)
            .exchange::<UniswapV4State>(
                "uniswap_v4",
                tvl_filter.clone(),
                // None, // Remove hook filter for now
                Some(uniswap_v4_pool_with_hook_filter),
            )
            .auth_key(Some(self.auth_key.clone()))
            .skip_state_decode_failures(true)
            .set_tokens(self.all_tokens.clone())
            .await
            .build()
            .await?;

Reference Information

Analysis of state.get_amount_out

This occurs when the state.get_amount_out() call fails (returns an error) for a specific pool. This is different from the V4 overflow panic - this is when the ProtocolSim state exists but the get_amount_out method itself fails.

When it happens: When there's a valid ProtocolSim state for the pool, but calling state.get_amount_out() returns an error (not a panic). The system then falls back to using the home-grown Self::get_amount() calculation instead.

Impact on Route Evaluation:

  • 703 V4 OVERFLOW PROTECTION: These routes are completely skipped because V4 pools are disabled. This means 703 potential routes cannot be evaluated at all.
  • 13 Failed state.get_amount_out: These routes are still evaluated, but using the fallback calculation method instead of the ProtocolSim state. This means the routes are processed but may have less accurate calculations.

The high number of V4 OVERFLOW PROTECTION messages (703) suggests that the V4 overflow issue is the primary blocker preventing route evaluation, while the Failed state.get_amount_out (13) is a much smaller issue affecting specific pools.

get_amount_code

let amount_out = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
                    state.get_amount_out(BigUint::from(adjusted_amount), &token_in, &token_out)
                }))

Analysis on Overflow Panic

Root Cause of V4 Overflow Panic:

The V4 overflow panic occurs in this sequence:

  1. Route Evaluation Process: When evaluating a route, the system calls evaluate_route in route_analyzer.rs
  2. ProtocolSim State Retrieval: The system calls pool_store.get_pool_state(&edge.pool_id).await? to get the V4 pool state from the database
  3. get_amount_out Call: For V4 pools, it calls state.get_amount_out(BigUint::from(adjusted_amount), &token_in, &token_out) where state is a UniswapV4State
  4. V4 Swap Execution: The get_amount_out method calls self.swap(zero_for_one, amount_specified, None)?
  5. Swap Math Calculation: The swap method iterates through tick ranges and calls compute_swap_step from swap_math.rs
  6. The Overflow Point: In compute_swap_step, at line 130, when calculating the fee amount:
   safe_sub_u256(amount_remaining.abs().into_raw(), amount_in)?

This subtraction operation amount_remaining - amount_in causes a u64 overflow when:

  • The input amount is very large
  • The pool has small reserves
  • The token values are vastly different (e.g., $0.001 token vs $130,000 token)
  1. Panic Propagation: The safe_sub_u256 function panics with "attempt to subtract with overflow" when the subtraction would result in a negative number, but the function expects unsigned integers.

What Triggers the Overflow:

  • Large Input Amounts: When the input amount exceeds what the pool can handle
  • Small Pool Reserves: V4 pools with low liquidity
  • Token Value Mismatch: Swapping between tokens with vastly different values
  • Precision Issues: The V4 implementation uses u64 internally, which can overflow with large amounts
    The system has safety measures (amount limiting) but they're not sufficient for all edge cases, leading to these overflow panics during route evaluation.

The swap_math.rs file is located at `lib/tycho-simulation/src/evm/protocol/utils/uniswap/swap_math.rs

This is part of the tycho-simulation library, not the main solver project. It's in the lib/tycho-simulation/ directory which contains the simulation engine that the solver uses for calculating swap amounts and simulating transactions.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions