# Venue adapters (/developers/internals/venue-adapters)



Every venue is reduced to one interface so the quote engine never branches on a venue name.
The execution class is part of that interface, because it decides which transaction path is
used and what the interface is allowed to promise.

## The interface [#the-interface]

```ts
interface VenueAdapter {
  readonly id: string;
  readonly displayName: string;
  readonly executionClass: ExecutionClass;
  readonly networks: readonly Network[];

  isAvailable(network: Network): boolean;
  discoverPools(provider: ChainProvider, network: Network): Promise<PoolState[]>;
  decodePool(utxo: Utxo, network: Network, readAt: Tip): PoolState;
  quote(input: SwapQuoteInput): SwapQuoteResult;
}
```

`discoverPools` finds every pool of the venue on the network, decoded, all read at one tip.
Discovery is by payment credential, because a venue's pools sit at many addresses that share
one payment credential and differ in their staking part.

`quote` returns the exact output for an input, in the venue's own integer arithmetic and with
the venue's own rounding. Never floating point: an off-by-one in rounding produces an order
the validator rejects, not a slightly worse price.

## The adapters that ship [#the-adapters-that-ship]

| Adapter                | Venue id            | Class      | Curve                  |
| ---------------------- | ------------------- | ---------- | ---------------------- |
| Dano Finance CLMM      | `danogo-clmm`       | Kernel     | Concentrated liquidity |
| Minswap V2             | `minswap-v2`        | Commitment | Constant product       |
| Minswap Stableswap     | `minswap-stable`    | Commitment | Stableswap             |
| SundaeSwap V3          | `sundaeswap-v3`     | Commitment | Constant product       |
| SundaeSwap Stableswaps | `sundaeswap-stable` | Commitment | Stableswap             |

Which of these run on a given network comes from `config/<network>.json`. A venue that is
disabled there, or whose `poolScriptHash` is null, is skipped with its reason recorded.

## PoolState [#poolstate]

```ts
interface PoolState {
  identity: PoolIdentity;
  executionClass: ExecutionClass;
  assetA: AssetRef;
  assetB: AssetRef;
  reserveA: bigint;
  reserveB: bigint;
  rawValueA: bigint;
  rawValueB: bigint;
  curve: CurveParameters;
  cost: VenueExecutionCost;
  risk: PoolRiskFlags;
  supportsAtomicMultiHop: boolean;
}
```

### Tradeable reserves are not the UTxO value [#tradeable-reserves-are-not-the-utxo-value]

`reserveA` and `reserveB` are the tradeable reserves after every venue specific exclusion has
been applied. `rawValueA` and `rawValueB` are what the UTxO literally holds.

The two differ, and the difference is the point. The validator reads the datum, so quoting
from the UTxO value produces orders the validator rejects.

### There is deliberately no decimal count [#there-is-deliberately-no-decimal-count]

Decimals are a property of an asset, not of a pool, and their single source of truth is the
network's asset registry. A consumer that needs them resolves them there by asset reference.

Carrying a number the adapter never read would invite a consumer to trust it, and a wrong
decimal count is a pricing error by orders of magnitude rather than a cosmetic one.

### Identity carries the chain state [#identity-carries-the-chain-state]

```ts
interface PoolIdentity {
  venueId: string;
  /** `txHash#index` of the pool UTxO this state was decoded from. */
  outRef: string;
  poolId: string;
  address: string;
  readAt: Tip;
}
```

## Curve parameters [#curve-parameters]

`CurveParameters` is a discriminated union so the maths cannot be mixed up.

<Tabs items="[&#x22;constant-product&#x22;, &#x22;stableswap&#x22;, &#x22;concentrated-liquidity&#x22;]">
  <Tab>
    ```ts
    {
      type: "constant-product";
      /** Fee numerator per direction; the two can differ on some venues. */
      feeNumeratorAToB: bigint;
      feeNumeratorBToA: bigint;
      feeDenominator: bigint;
    }
    ```
  </Tab>

  <Tab>
    ```ts
    {
      type: "stableswap";
      /** Amplification. Note the `ann = amp * n` convention where it applies. */
      amp: bigint;
      fee: bigint;
      adminFee: bigint;
      feeDenominator: bigint;
      /** Per-asset scaling factors that bring the reserves to one precision. */
      multiples: readonly bigint[];
    }
    ```
  </Tab>

  <Tab>
    ```ts
    {
      type: "concentrated-liquidity";
      /** Fee in ten-thousandths, matching the venue's own BASE of 10000. */
      lpFeeRate: bigint;
      sqrtLowerPriceNum: bigint;
      sqrtLowerPriceDen: bigint;
      sqrtUpperPriceNum: bigint;
      sqrtUpperPriceDen: bigint;
      /** Platform fee already accrued and no longer tradeable depth. */
      platformFeeX: bigint;
      platformFeeY: bigint;
      /** Ada already collected as swap fees, excluded from an ada reserve. */
      totalSwapFee: bigint;
      /** Fraction of the lp fee taken by the protocol, in ten-thousandths. */
      platformFeeRate: bigint;
      /** Flat ada added to the pool per swap, from the protocol config. */
      swapFeeLovelace: bigint;
    }
    ```
  </Tab>
</Tabs>

## Risk flags change what the product may promise [#risk-flags-change-what-the-product-may-promise]

`PoolRiskFlags` are not diagnostics. Each one changes what can be said before a user signs,
and each one travels to the quote's `warnings` array.

| Flag                   | What it means                                                                                            |
| ---------------------- | -------------------------------------------------------------------------------------------------------- |
| `dynamicFee`           | The pool's fee can change after the quote. Such a route cannot honour a fill-at-the-quoted-rate promise. |
| `noOrderExpiry`        | The venue's order carries no expiry, so a stale order needs a paid cancel.                               |
| `permissionedBatching` | Batching is permissioned, so a fill depends on a whitelisted third party.                                |
| `notes`                | The plain language reasons behind the flags above, for display and for logs.                             |

`routing.dynamicFeePolicy` decides whether a `dynamicFee` pool is excluded from routing
outright or kept and labelled.

## Execution cost [#execution-cost]

```ts
interface VenueExecutionCost {
  /** Batcher or execution fee in lovelace. Null when the venue has none. */
  executionFeeLovelace: bigint | null;
  /** Refundable minimum ada that must ride along with the order. */
  depositLovelace: bigint | null;
  /** True when the fee above is a worst case rather than a fixed amount. */
  feeIsWorstCase: boolean;
}
```

`feeIsWorstCase` covers venues whose fee is amortised across a scooped batch. The quote shows
the worst case and says so, and a total containing one upper bound is itself only an upper
bound.

## The venue registry [#the-venue-registry]

`VenueRegistry.forNetwork(provider, network)` builds every adapter the configuration enables.

It refuses a provider for a different network outright, because that provider's reads would
come from the wrong chain.

For each adapter it also asserts four things against the configuration, and each mismatch
throws naming both sides:

* The adapter's `id` matches the venue it was built for.
* The adapter's `executionClass` matches the configured one. The two decide different
  transaction paths, so a silent disagreement would be a wrong promise.
* The adapter declares support for this network.
* The adapter reports itself available on this network.

Venues that produce no adapter are collected in `registry.skipped` with the reason, because
"the router saw one venue" and "the router saw one venue and silently lost three" look
identical without it.

## Related [#related]

<Cards>
  <Card title="Execution classes" href="/developers/concepts/execution-classes" />

  <Card title="Routing" href="/developers/concepts/routing" />

  <Card title="Configuration reference" href="/developers/reference/configuration" />
</Cards>
