> For the complete documentation index, see [llms.txt](https://docs.metric.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.metric.xyz/reference/smart-contracts/get-quote.md).

# Get quote

Simulate a swap offchain to get exact token deltas, without executing it or changing state.

Two ways, both `eth_call` only:

* **`MetricOmmSwapQuoter`** (recommended): decodes the revert for you and returns `(amountIn, amountOut)` directly.
* **`MetricOmmPool.simulateSwapAndRevert`** (low-level, for testing): takes your own prices and reverts with the result encoded in the revert data; you decode it yourself.

Both are non-`view` in the ABI because they mutate state internally before reverting. Call them with `eth_call`, never as a transaction.

### MetricOmmSwapQuoter

A read-only lens with two flavors, each available in exact-input / exact-output and single-hop / multihop forms.

* **Live**: reads the pool's price provider onchain. You supply no prices. Reverts `PoolPaused(pool, pauseLevel)` if any pool on the route is paused.
* **Hypothetical**: you supply `bidPriceX64`, `askPriceX64` and `referencePriceX64` yourself, to test a price scenario.

#### The `sender` parameter

Every quoter function takes a `sender`. It is forwarded to the pool's swap extensions in place of `msg.sender`, so extensions that gate on the caller behave as they would for a real swap.

**A zero `sender` defaults to `msg.sender`.** Pass the address that will actually send the swap whenever a pool has swap extensions.

Leave `from` empty in the `eth_call`. Live quotes read the Price Provider outside a swap, and restricted providers refuse that read unless `tx.origin` is zero, reverting with no data.

#### Supplying prices (hypothetical only)

Pass your own scenario values in the provider's format: raw, unadjusted Q64.64 bid, ask and reference. Don't read them from the Price Provider: restricted providers refuse the read.

{% hint style="warning" %}
Do **not** substitute the API's `bidAdj` / `askAdj` or the pool's `getSellAndBuyPrices()` output. Those already include the pool's deviation and fees; feeding them back as provider inputs applies the adjustments twice. For a current-provider quote, use the Live path instead.
{% endhint %}

Your three values must satisfy `bid < ask` and `bid <= reference <= ask`, and the pool still enforces its configured bid/ask/reference asymmetry bound on them. `sqrt(bid * ask)` is a safe default for the reference; a reference sitting exactly on `bid` or `ask` reverts unless the pool has the asymmetry check disabled.

#### Single-hop

{% code overflow="wrap" %}

```solidity
// Live: no prices supplied
function quoteLiveExactInSingle(
    address pool,
    address sender,
    bool zeroForOne,
    uint128 amountIn,
    uint128 priceLimitX64
) external returns (uint256 amountIn_, uint256 amountOut);

function quoteLiveExactOutSingle(
    address pool,
    address sender,
    bool zeroForOne,
    uint128 amountOutDesired,
    uint128 priceLimitX64
) external returns (uint256 amountIn_, uint256 amountOut);

// Hypothetical: caller-supplied provider quote
function quoteHypotheticalExactInputSingle(
    address pool,
    address sender,
    bool zeroForOne,
    uint128 amountIn,
    uint128 priceLimitX64,
    uint128 bidPriceX64,
    uint128 askPriceX64,
    uint128 referencePriceX64
) external returns (uint256 amountIn_, uint256 amountOut);

function quoteHypotheticalExactOutputSingle(
    address pool,
    address sender,
    bool zeroForOne,
    uint128 amountOutDesired,
    uint128 priceLimitX64,
    uint128 bidPriceX64,
    uint128 askPriceX64,
    uint128 referencePriceX64
) external returns (uint256 amountIn_, uint256 amountOut);
```

{% endcode %}

Each has an overload that inserts `address recipient` after `sender` and appends `bytes extensionData`, for pools whose extensions gate on those fields:

{% code overflow="wrap" %}

```solidity
function quoteLiveExactInSingle(
    address pool,
    address sender,
    address recipient,
    bool zeroForOne,
    uint128 amountIn,
    uint128 priceLimitX64,
    bytes memory extensionData
) external returns (uint256 amountIn_, uint256 amountOut);
```

{% endcode %}

#### Multihop

{% code overflow="wrap" %}

```solidity
function quoteLiveExactIn(address sender, QuoteExactInputParams calldata params)
    external returns (uint256 amountIn_, uint256 amountOut);

function quoteLiveExactOut(address sender, QuoteExactOutputParams calldata params)
    external returns (uint256 amountIn_, uint256 amountOut);

function quoteHypotheticalExactInput(address sender, QuoteHypotheticalExactInputParams calldata params)
    external returns (uint256 amountIn_, uint256 amountOut);

function quoteHypotheticalExactOutput(address sender, QuoteHypotheticalExactOutputParams calldata params)
    external returns (uint256 amountIn_, uint256 amountOut);
```

{% endcode %}

Parameter structs:

{% code overflow="wrap" %}

```solidity
struct QuoteExactInputParams {
    address[] pools;            // one per hop, at least one
    bytes[] extensionDatas;     // length must match pools
    uint256 zeroForOneBitMap;   // bit i = direction for pools[i]
    uint128 amountIn;           // exact input for the first hop
}

struct QuoteHypotheticalExactInputParams {
    address[] pools;
    bytes[] extensionDatas;
    uint256 zeroForOneBitMap;
    uint128 amountIn;
    uint128[] bidPricesX64;         // one per pool
    uint128[] askPricesX64;         // one per pool
    uint128[] referencePricesX64;   // one per pool
}
```

{% endcode %}

The exact-output structs are identical except `amountIn` becomes `amountOut` (the exact output of the final hop).

Multihop paths use an open price limit on every hop. Slippage is bounded by the amounts you check afterwards, not by per-hop limits.

### Low-level: call the pool directly

{% code overflow="wrap" %}

```solidity
function simulateSwapAndRevert(
    address sender,
    address recipient,
    bool zeroForOne,
    int128 amountSpecified,
    uint128 priceLimitX64,
    uint128 bidPriceX64,
    uint128 askPriceX64,
    uint128 referencePriceX64,
    bytes calldata extensionData
) external returns (int128 amount0Delta, int128 amount1Delta);
```

{% endcode %}

The declared return values are never produced. When the simulation completes, the pool deliberately reverts with:

{% code overflow="wrap" %}

```solidity
error SimulateSwap(int256 amount0Delta, int256 amount1Delta);
```

{% endcode %}

Catch and decode that revert yourself. Use the quoter unless you specifically need the raw signed deltas.

### How it works

Both quoter paths call `pool.simulateSwapAndRevert`. The only difference is where the three prices come from:

* **Live** reads the pool's price provider first (immutable provider, else the mutable slot), then passes those values in.
* **Hypothetical** passes your values in directly.

The pool then validates the prices, runs `beforeSwap` extensions, computes the swap, runs `afterSwap` extensions, and reverts with `SimulateSwap`. The quoter decodes that and returns the amounts.

Any other revert is re-thrown wrapped:

{% code overflow="wrap" %}

```
error WrappedError(address target, bytes4 selector, bytes reason);
```

{% endcode %}

`target` is the pool, `selector` is `simulateSwapAndRevert`, and `reason` is the raw inner revert; decode it a second time to get the real error.

Provider errors on the Live path aren't wrapped: the quoter reads the provider before calling the pool, so they surface as the provider's own error.

Before reading the provider, the Live path checks each pool's `pauseLevel` and reverts `PoolPaused(address pool, uint8 pauseLevel)` for a paused one, also unwrapped. On a multihop route, `pool` names the paused hop.

### What a quote does and does not check

**Runs:** price validation, `beforeSwap` and `afterSwap` extensions, and the full bin-walking swap math. This is true for both Live and Hypothetical.

**Skips:** token settlement (no transfers, no balance or allowance checks). Hypothetical quotes also skip the pause check, so a paused pool still returns one; Live quotes revert `PoolPaused`.

**Caller identity:** the pool sees the quoter contract as `msg.sender`. Pass the real `sender`, `recipient` and `extensionData` on the overloads whenever extensions depend on them.

{% hint style="warning" %}
**A quote doesn't prove the swap will go through.** Before executing, check `pauseLevel` in pool metadata and simulate the actual router transaction from the address that will send it.
{% endhint %}

### Errors

#### Pool errors

| Condition                                                                                 | Error                                                                          |
| ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| `amountSpecified == 0`                                                                    | `InvalidAmount()`                                                              |
| Swap computes a zero fill, commonly an empty pool                                         | `FinalAmountIsZero()`                                                          |
| `bidPriceX64 == 0` and `askPriceX64 > 0`                                                  | `BidIsZero()`                                                                  |
| `bidPriceX64 >= askPriceX64`                                                              | `BidGreaterThanAsk()`                                                          |
| Reference outside `[bid, ask]`                                                            | `ReferencePriceOutOfRange()`                                                   |
| Directional fee asymmetry outside the pool's bound, or reference exactly on `bid` / `ask` | `BidAskRefAsymmetryOutOfBounds()`                                              |
| Price provider reverted (Live path)                                                       | The provider's own error, e.g. `FeedStalled()`; no data if the read is refused |
| Bin balance would exceed `uint104`                                                        | `OverflowedOnFittingBinBalances(uint256)`                                      |

The pool's own `PoolPaused()` cannot occur here, because simulation skips the pause check. A Live quote on a paused pool reverts with the quoter's `PoolPaused(address,uint8)` instead (below).

The asymmetry bound is skipped entirely when the pool's `bidAskRefAsymmetryX8` is `type(uint16).max`.

#### Quoter errors

| Error                                             | Meaning                                                                      |
| ------------------------------------------------- | ---------------------------------------------------------------------------- |
| `WrappedError(address,bytes4,bytes)`              | Downstream revert; decode `reason` for the real error                        |
| `InvalidPriceProvider()`                          | Pool has neither an immutable nor a mutable provider configured              |
| `PoolPaused(address,uint8)`                       | Live path only: that pool's `pauseLevel` is non-zero, so a swap would revert |
| `QuoteDidNotRevert()`                             | `simulateSwapAndRevert` returned instead of reverting                        |
| `AmountTooLarge(uint128)`                         | Amount does not fit in `int128`                                              |
| `InvalidSwapDeltas()`                             | Deltas do not match the expected exact-in / exact-out shape                  |
| `InvalidPath()`                                   | Route arrays inconsistent, pools not token-connected, or path too short      |
| `InvalidInputAmountAtHop(uint8,uint256,uint256)`  | An exact-input hop partially filled                                          |
| `InvalidOutputAmountAtHop(uint8,uint256,uint256)` | An exact-output hop did not produce the requested amount                     |
