---
id: TIP-1001
title: Place-only mode for next quote token
description: A new DEX function for creating trading pairs against a token's staged next quote token, to allow orders to be placed on it.
authors: Dan Robinson
status: Draft
---

# TIP-1001: Place-only mode for next quote token

## Abstract

This TIP adds a `createNextPair` function to the Stablecoin DEX that creates a trading pair between a base token and its `nextQuoteToken()`, along with `place` and `placeFlip` overloads that accept a book key to target specific pairs. This enables market makers to place orders on the new pair before a quote token update is finalized, providing a smooth liquidity transition.

## Motivation

When a token issuer decides to change their quote token (via `setNextQuoteToken` and `completeQuoteTokenUpdate`), there is currently no way to establish liquidity on the new pair before the transition completes. This means that market makers will need to wait until the quote token has been updated before they can place orders, which could cause a period where there is no liquidity, or limited liquidity, for the token, which will interrupt swaps involving that token.

By allowing pair creation against `nextQuoteToken()`, this change allows users and market makers to add liquidity to the DEX before it is used on swaps. Since swaps route through `quoteToken()` (not `nextQuoteToken()`), the new pair operates in "place-only" mode: orders can be placed and cancelled, but no swaps route through it until `completeQuoteTokenUpdate()` is called.

---

# Specification

## New functions

Add the following functions to the Stablecoin DEX interface:

```solidity
/// @notice Creates a trading pair between a base token and its next quote token
/// @param base The base token address
/// @return key The pair key for the created pair
/// @dev Reverts if:
///   - The base token has no next quote token staged (nextQuoteToken is zero)
///   - The pair already exists
///   - Either token is not USD-denominated
function createNextPair(address base) external returns (bytes32 key);

/// @notice Places an order on a specific pair identified by book key
/// @param bookKey The pair key identifying the orderbook
/// @param token The base token of the pair
/// @param amount The order amount in base tokens
/// @param isBid True for buy orders, false for sell orders
/// @param tick The price tick for the order
/// @return orderId The ID of the placed order
function place(bytes32 bookKey, address token, uint128 amount, bool isBid, int16 tick) external returns (uint128 orderId);

/// @notice Places a flip order on a specific pair identified by book key
/// @param bookKey The pair key identifying the orderbook
/// @param token The base token of the pair
/// @param amount The order amount in base tokens
/// @param isBid True for buy orders, false for sell orders
/// @param tick The price tick for the order
/// @param flipTick The price tick for the flipped order when filled
/// @param internalBalanceOnly If true, only use internal balance for the flipped order
/// @return orderId The ID of the placed order
function placeFlip(bytes32 bookKey, address token, uint128 amount, bool isBid, int16 tick, int16 flipTick, bool internalBalanceOnly) external returns (uint128 orderId);
```

## Behavior

### Pair creation

`createNextPair(base)` creates a pair between `base` and `base.nextQuoteToken()`. The function:

1. Calls `nextQuoteToken()` on the base token
2. Reverts with `NO_NEXT_QUOTE_TOKEN` if the result is `address(0)`
3. Validates both tokens are USD-denominated (same as `createPair`)
4. Creates the pair using the same mechanism as `createPair`
5. Emits `PairCreated(key, base, nextQuoteToken)`

### Place-only mode

Once the pair exists, it supports the full order lifecycle:

- `place(bookKey, ...)` and `placeFlip(bookKey, ...)` allow placing orders on the pair
- `cancel` and `cancelStaleOrder` work normally (they use order ID, not pair lookup)
- `books` returns accurate data (it takes the book key directly)

The new `place` and `placeFlip` overloads are required because the existing functions derive the pair from `token.quoteToken()`, which would look up the wrong pair. The overloads accept a `bookKey` parameter to target the correct pair.

Swap functions (`swapExactAmountIn`, `swapExactAmountOut`) and quote functions (`quoteSwapExactAmountIn`, `quoteSwapExactAmountOut`) do not route through this pair because routing uses `quoteToken()` to find paths between tokens.

### After quote token update

When the token issuer calls `completeQuoteTokenUpdate()`:

1. The token's `quoteToken()` changes to what was `nextQuoteToken()`
2. The token's `nextQuoteToken()` becomes `address(0)`
3. The existing pair (created via `createNextPair`) is now the active pair
4. Swaps begin routing through the pair

The old pair (against the previous quote token) remains but will no longer be used for routing swaps involving this base token. Orders on it can be canceled using their ID.

## New error

```solidity
/// @notice The base token has no next quote token staged
error NO_NEXT_QUOTE_TOKEN();
```

## Events

No new events. The existing `PairCreated` event is emitted by `createNextPair`, and the existing `OrderPlaced` event is emitted by the `place` and `placeFlip` overloads. 

---

# Invariants

- A pair created via `createNextPair` must be identical to one created via `createPair` once `completeQuoteTokenUpdate` is called
- `createNextPair` must revert if `nextQuoteToken()` returns `address(0)`
- `createNextPair` must revert if the pair already exists (same as `createPair`)
- Orders placed on a next-quote-token pair must be executable via swaps after the quote token update completes
- Swap routing must not change until `completeQuoteTokenUpdate` is called on the base token
