---
id: TIP-1056
title: Keep the same order ID when flip orders flip
description: Reuses the same order ID when a flip order flips and emits OrderFlipped instead of creating a new order ID and emitting OrderPlaced.
authors: Dan Robinson
status: Draft
related: TIP-1030, TIP-1044
protocolVersion: T5
---

# TIP-1056: Keep the same order ID when flip orders flip

## Abstract

This TIP adds a new `OrderFlipped` event and changes flip order execution so that when a flip order is fully filled and flips to the opposite side of the book, it keeps the same `orderId`. Instead of deleting the filled order and creating a new order with a new ID, the order is rewritten in place and `OrderFlipped` is emitted.

This makes `orderId` the stable identity of a flip order across flips. It also avoids allocating a new order ID, and orders no longer emit `OrderPlaced` when they flip.

## Motivation

Today, when a flip order is fully filled, the Stablecoin DEX emits `OrderFilled` for the filled order, then creates a fresh opposite-side order with a new `orderId` and emits `OrderPlaced`. That makes a single logical flip order appear as a sequence of unrelated orders.

This creates avoidable offchain complexity for market makers that use flip orders. To know which orders they currently have open, they must index the chain and watch both `OrderFilled` and `OrderPlaced`, then update their own state every time a flip replaces one order ID with another. It also means canceling a flip order is not guaranteed: if the order flips while a cancel is pending, the cancel attempt fails because it targets the old order ID. Keeping the same `orderId` makes the order a stable handle across flips instead of forcing market makers to chase a moving ID, and makes cancellation target the currently active order.

It also saves substantial gas. Today, a flip costs about 1.5 million gas because it deletes the filled order and creates a fresh opposite-side order with a new ID. Even after TIP-1055 removes the redundant stored `orderId`, a flip would still cost about 1.25 million gas if it continues creating a new order record on every flip. Rewriting the same order in place avoids most of that overhead.

This TIP is an alternative to TIP-1044. TIP-1044 keeps the current semantics and discounts the gas charged for creating the replacement order. TIP-1056 instead changes the semantics so no replacement order is created at all. Because it removes that work rather than discounting it, it achieves greater gas savings.

## Assumptions

- Flip orders remain orders that automatically rest on the opposite side only after they are fully filled.
- A flipped order should receive fresh queue priority at its destination tick, even though it keeps the same `orderId`.
- Consumers that distinguish ordinary order placement from automatic flipping can switch from `OrderPlaced` to `OrderFlipped` for flip transitions.
- `orderId` is allowed to represent a logical order across flips rather than a single open-to-close resting-order instance.

---

# Specification

## Flip behavior

When a flip order is fully filled, the Stablecoin DEX MUST:

1. emit `OrderFilled(orderId, maker, taker, amountFilled, false)` for the fill
2. rewrite that same order in place to its flipped state
3. emit `OrderFlipped` for the newly resting flipped order

It MUST NOT:

- allocate a new `orderId`
- increment `nextOrderId`
- emit `OrderPlaced` for the automatically flipped order

## Rewritten order state

When order `orderId` flips, the stored order with key `orderId` is updated as follows:

- `isBid` is inverted
- `tick` becomes the prior `flipTick`
- `flipTick` becomes the prior `tick`
- `amount` is unchanged
- `remaining` is reset to `amount`
- `maker` is unchanged
- `bookKey` is unchanged
- `isFlip` remains `true`
- `prev` and `next` are reset before reinsertion into the destination tick level

"Rewrite in place" means the order remains stored under the same mapping key and the implementation updates the existing order record rather than deleting it and allocating a new order record.

If TIP-1055 is active and the order is stored in an older order-storage version, the rewrite MUST upgrade the order to the latest active storage version. In particular, a version `0` order that flips after TIP-1055 activates MUST become a version `1` order as part of that rewrite.

That storage-version upgrade MUST clear deprecated slot `0` as part of the upgrade. The order keeps the same mapping key as its canonical `orderId`, and after the upgrade slot `0` remains zero and MUST remain untouched for the rest of the order's live lifetime.

Outside of the tick-level and linked-list bookkeeping needed to remove and reinsert the order, the implementation MUST write only order-record slots whose post-flip contents differ from their pre-flip contents.

Under the TIP-1055 storage layout, slots `1` and `2` of the order record do not change during a flip and MUST remain untouched. Slot `0` MUST be cleared if and only if the flip upgrades a version `0` order to version `1`; otherwise slot `0` MUST remain untouched. Only the slots whose packed contents actually change may be written.

## Queue priority

Keeping the same `orderId` does not preserve queue position.

After a flip, the rewritten order is inserted into the destination tick level as a newly resting order with fresh time priority at that level.

## Cancellation

`cancel(orderId)` and `cancelStaleOrder(orderId)` apply to the currently active state of that order ID.

If a flip order has already flipped, cancelling that `orderId` cancels the flipped order.

## Getter behavior

`getOrder(orderId)` continues to return the current active state of that order ID.

For a flip order that has flipped one or more times, `getOrder(orderId)` returns the latest resting state.

## Events

Add a new event:

```solidity
event OrderFlipped(
    uint128 indexed orderId,
    address indexed maker,
    address indexed token,
    uint128 amount,
    bool isBid,
    int16 tick,
    bool isFlipOrder,
    int16 flipTick
);
```

`OrderFlipped` has the same payload shape as `OrderPlaced`, but is emitted only when a fully filled flip order is rewritten to its new resting state.

Initial order placement behavior is unchanged:

- user-submitted order placement emits `OrderPlaced`
- automatic flip transitions emit `OrderFlipped`

No new errors are introduced.

## Compatibility

This TIP changes both event semantics and `orderId` semantics for flip orders.

### Indexers

Indexers that currently treat `OrderPlaced` as the signal that new resting liquidity has appeared MUST also process `OrderFlipped`. Otherwise they will miss flipped liquidity entirely.

Indexers that currently model one `orderId` as one open-to-close order lifecycle MUST update that model. After this TIP, a flip order's `orderId` identifies a logical order that may change side and tick across flips while remaining live.

Indexers that maintain order-history tables keyed by `orderId` may need to support multiple phases of a single order ID. In particular, a terminal `OrderFilled(..., false)` for a flip order is no longer necessarily the end of that order ID's life if it is followed by `OrderFlipped`.

### Market makers and trading systems

Market makers that currently track flip orders by listening for `OrderFilled` and then replacing the old order ID with the new `OrderPlaced` order ID MUST update that logic. After this TIP, the same `orderId` remains active across flips and `OrderFlipped` is the signal that the order changed side.

Trading systems that submit cancellations against the most recently observed replacement order ID must instead treat the original flip-order `orderId` as the stable cancellation handle.

### Analytics and monitoring

Systems that use `nextOrderId` as a proxy for how many orders have been created will observe lower growth, because automatic flips no longer allocate new IDs.

Systems that count new resting orders by counting `OrderPlaced` events will undercount unless they also count `OrderFlipped`.

### Summary

External systems remain compatible if they adopt the following rules:

- `OrderPlaced` means user-submitted liquidity began resting
- `OrderFlipped` means existing flip liquidity began resting on the opposite side
- `orderId` for a flip order is a stable logical identifier across flips
- `cancel(orderId)` targets the currently active state of that logical order

---

# Invariants

- Fully filling a flip order does not allocate a new order ID.
- Fully filling a flip order does not increment `nextOrderId`.
- After a flip, `getOrder(orderId)` returns the flipped resting order under the same `orderId`.
- `OrderPlaced` is not emitted for automatically flipped liquidity.
- `OrderFlipped` is emitted exactly once for each successful flip transition.
- A flipped order receives fresh queue priority at its destination tick level.
