Slippage
Why You Never Get the Price You Quoted

What Is Slippage?

Slippage is the difference between the price you expect when you submit a trade and the price you actually receive when the trade executes. If you see a quoted price of $2,000 per ETH and submit a buy order, but by the time the trade processes you end up paying $2,012, that $12 difference is slippage. It is an unavoidable friction in every market, but especially pronounced in on-chain trading where block latency and public mempools create unique challenges.

Expected Price vs Actual Execution time price expected: $2,000 actual: $2,012 you submit the trade trade executes slippage = $12 mempool wait + block inclusion (~12s or more)

Why Slippage Happens

Slippage has three main causes.

Slippage in Order Books vs AMMs

In an order book, slippage happens when your trade is too large to fill at the best price level. A market buy order for 100 ETH might fill the first 50 ETH at $2,001 (the best ask), but the remaining 50 ETH must "walk the book" and fill at $2,002, $2,003, and so on. The average execution price ends up higher than the quoted best ask. This is sometimes called market depth slippage.

In an AMM, the same concept applies but the mechanism is continuous. Instead of stepping through discrete price levels, your trade moves along a smooth curve. A large trade pushes the price further along the \(x \cdot y = k\) curve, and the average execution price is worse than the spot price at the start.

Large Trade Moving Along the AMM Curve USDC per ETH ETH reserve in pool $2,400 $2,100 $2,000 $1,700 x * y = k before trade spot = $2,000 after trade new spot = $2,400 large buy (100 ETH) price impact avg execution = ~$2,200 slippage ~$200 per ETH (10% price impact)

Price Impact

Price impact is the component of slippage caused by your own trade moving the price. For a constant product AMM with reserves \(x\) (ETH) and \(y\) (USDC), buying \(\Delta x\) ETH costs you

\[\text{cost} = y - \frac{x \cdot y}{x + \Delta x}\]

The average execution price is \(\text{cost} / \Delta x\), and the price impact is the percentage difference between this average price and the spot price before the trade.

Go
// Calculate price impact for a given trade size
func calculatePriceImpact(reserveETH, reserveUSDC, ethToBuy float64) float64 {
    k := reserveETH * reserveUSDC
    spotPrice := reserveUSDC / reserveETH

    // After buying ethToBuy ETH from the pool
    newReserveETH := reserveETH - ethToBuy
    newReserveUSDC := k / newReserveETH
    usdcCost := newReserveUSDC - reserveUSDC

    avgExecutionPrice := usdcCost / ethToBuy
    priceImpactPct := ((avgExecutionPrice - spotPrice) / spotPrice) * 100

    fmt.Printf("Spot price:      $%.2f\n", spotPrice)
    fmt.Printf("Avg exec price:  $%.2f\n", avgExecutionPrice)
    fmt.Printf("Price impact:    %.2f%%\n", priceImpactPct)
    fmt.Printf("New spot price:  $%.2f\n", newReserveUSDC/newReserveETH)

    return priceImpactPct
}

// Pool with 1,000 ETH and 2,000,000 USDC
calculatePriceImpact(1000, 2000000, 1)   // ~0.1% impact
calculatePriceImpact(1000, 2000000, 10)  // ~1.0% impact
calculatePriceImpact(1000, 2000000, 100) // ~11.1% impact

The relationship is nonlinear. Doubling the trade size more than doubles the price impact. A trade that is 1% of the pool reserves has roughly 1% impact, but a trade that is 10% of the reserves has roughly 11% impact. This is why pool depth matters so much.

Slippage Protection

Because slippage is unpredictable (other trades can land before yours), DeFi protocols provide parameters to protect you. The most important one is amountOutMin, the minimum amount of output tokens you will accept. If the actual output would be less than this threshold, the entire transaction reverts and you only lose gas fees.

Go
// Uniswap V2 Router swap with slippage tolerance
func swapWithSlippageProtection(
    client *ethclient.Client,
    routerAddr common.Address,
    tokenOut common.Address,
    amountIn *big.Int,
    auth *bind.TransactOpts,
) (*types.Transaction, error) {
    parsed, _ := abi.JSON(strings.NewReader(routerABI))
    router := bind.NewBoundContract(routerAddr, parsed, client, client, client)

    weth := common.HexToAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2")
    path := []common.Address{weth, tokenOut}

    // Get expected output
    var result []interface{}
    router.Call(&bind.CallOpts{}, &result, "getAmountsOut", amountIn, path)
    amounts := result[0].([]*big.Int)
    expectedOut := amounts[1]

    // Set 0.5% slippage tolerance (50 bps)
    bps := big.NewInt(10000)
    slippage := big.NewInt(50)
    amountOutMin := new(big.Int).Sub(bps, slippage)
    amountOutMin.Mul(expectedOut, amountOutMin)
    amountOutMin.Div(amountOutMin, bps)

    // Deadline 5 minutes from now
    deadline := big.NewInt(time.Now().Unix() + 300)

    // Send ETH with the transaction
    auth.Value = amountIn

    tx, err := router.Transact(auth, "swapExactETHForTokens",
        amountOutMin, // reverts if output < this
        path,
        auth.From,
        deadline,      // reverts if block.timestamp > this
    )

    return tx, err
}

The deadline parameter adds a second layer of protection. If your transaction sits in the mempool too long and the market has moved significantly, the deadline ensures it expires rather than executing at a stale price.

MEV and Sandwich Attacks

Because Ethereum transactions are visible in the public mempool before they are included in a block, bots can see your pending swap and exploit your slippage tolerance. The most common exploit is the sandwich attack.

A sandwich bot sees your pending swap for 10 ETH with 1% slippage tolerance. It submits a transaction before yours (frontrun) that buys ETH, pushing the price up. Your swap then executes at the inflated price, but still within your slippage tolerance. The bot then submits a transaction after yours (backrun) that sells the ETH it bought, pocketing the difference. You got a worse price, and the bot extracted value from you.

Sandwich Attack Sequence time You submit swap: buy 10 ETH 1% slippage, visible in mempool 1. Bot FRONTRUN Bot buys ETH, pushes price up ETH: $2,000 → $2,008 2. YOUR TRADE executes Buy at inflated price $2,015 within 1% tolerance, so it passes 3. Bot BACKRUN Bot sells ETH, pockets profit profit: ~$7 per ETH × amount same block

This is a form of MEV (Maximal Extractable Value). Validators and searcher bots can reorder transactions within a block to extract value from other users. Sandwich attacks are the most visible form, but MEV also includes arbitrage and liquidations.

Minimizing Slippage

There are several practical strategies to reduce the slippage you experience.

For the broader framework of trading mechanics that produce slippage, see Market Microstructure. For the related concept of the baseline trading cost, see The Bid-Ask Spread. For how transactions travel through the mempool before execution, see Sending Transactions on Ethereum. For production patterns on managing transaction ordering, see Nonce Management Patterns.