affaan-m/ECC · error · SlippageError

Simulation: {actual_out} < {expected_min_out}

Error message

Simulation: {actual_out} < {expected_min_out}

What it means

`safe_execute` simulates the call (`eth_call`) and decodes the returned uint256. If `actual_out < expected_min_out`, it raises `SlippageError(f"Simulation: {actual_out} < {expected_min_out}")` and never signs. This is the slippage protection that 594 guards.

Source

Thrown at skills/llm-trading-agent-security/SKILL.md:86

        self._record_spend(usd_amount)
```

### Simulate before sending

```python
class SlippageError(Exception):
    pass

async def safe_execute(self, tx: dict, expected_min_out: int | None = None) -> str:
    sim_result = await self.w3.eth.call(tx)

    if expected_min_out is None:
        raise ValueError("min_amount_out is required before send")

    actual_out = decode_uint256(sim_result)
    if actual_out < expected_min_out:
        raise SlippageError(f"Simulation: {actual_out} < {expected_min_out}")

    signed = self.account.sign_transaction(tx)
    return await self.w3.eth.send_raw_transaction(signed.raw_transaction)
```

### Circuit breaker

```python
class TradingCircuitBreaker:
    MAX_CONSECUTIVE_LOSSES = 3
    MAX_HOURLY_LOSS_PCT = 0.05

    def check(self, portfolio_value: float) -> None:
        if self.consecutive_losses >= self.MAX_CONSECUTIVE_LOSSES:
            self.halt("Too many consecutive losses")

        if self.hour_start_value <= 0:
            self.halt("Invalid hour_start_value")

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Re-fetch a fresh quote and recompute `expected_min_out = quote * (1 - tolerance)`.
  2. Loosen slippage tolerance for volatile pairs (but stay within the spend-limit guard).
  3. Retry with a higher deadline / different route; consider splitting the trade.
  4. If simulations consistently underperform, investigate the router and pool fee tier.

Example fix

# before
actual_out = decode_uint256(sim_result)
if actual_out < expected_min_out:
    raise SlippageError(f"Simulation: {actual_out} < {expected_min_out}")

# after — expose tolerance in the message; suggest a retry bound
slippage_bps = (expected_min_out - actual_out) * 10_000 // expected_min_out
raise SlippageError(
    f"Simulation: got {actual_out}, min {expected_min_out} "
    f"(negative {slippage_bps} bps); re-quote or raise tolerance"
)
Defensive patterns

Strategy: retry

Validate before calling

null

Type guard

null

Try / catch

for attempt in range(3):
    try:
        return await agent.safe_execute(tx, expected_min_out=min_out)
    except SlippageError as e:
        quote = await refetch_quote()
        min_out = int(quote * (1 - SLIPPAGE_TOLERANCE))
        continue
raise SlippageError('slippage exceeded after retries')

Prevention

When it happens

Trigger: On-chain liquidity moved between quote and execution; the simulated return is below the caller's minimum. MEV/front-run reduces the realistic output. Pool fee or route changed.

Common situations: Stale quote used as `expected_min_out`; slippage tolerance set too tight (e.g. 0.1% on a volatile pool); router path is no longer optimal; fee bump from the pool.

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/806f47205ef9899d. Report an issue: GitHub.