---
id: TIP-1062
title: Versioned DEX order storage
description: Introduce a versioned Stablecoin DEX order storage layout that keeps the existing orders mapping while allowing future order structs to be decoded by version.
authors: Dan Robinson
status: Draft
related: TIP-1056
---

# TIP-1062: Versioned DEX order storage

## Abstract

This TIP changes the Stablecoin DEX order storage model from a single fixed `Order` struct layout to a versioned layout behind the existing `orders` mapping starting at the T6 hardfork. The mapping key remains the canonical order ID, and the first storage word for each T6+ order carries a version discriminator. Legacy orders remain readable as version `0`; T6+ orders use version `1`.

## Motivation

The current DEX order layout uses 6 storage slots per order. A packed layout can store the same active order state in 4 slots by using the mapping key as the canonical `orderId` instead of storing `orderId` inside the value, and by rearranging the remaining fields:

```solidity
mapping(uint128 orderId => Order order) orders;
```

Putting `prev` and `next` in the same slot keeps the linked-list pointers together when reading or clearing an order, and leaves future paired pointer updates confined to one storage slot. Adding a version number makes the layout change backwards-compatible and gives future order layouts a clear upgrade path. The DEX also has several hot paths that read and update individual order fields (`remaining`, `prev`, and `next`) directly, so order storage is accessed through a dedicated abstraction rather than scattering version checks through matching, cancellation, and flip-order logic.

## Specification

The top-level storage slot of `orders` is unchanged. The mapping key remains `uint128 orderId`, and `orderId` is no longer stored in new order values.

### Legacy layout

Existing orders are version `0`. Version `0` is detected by reading byte offset `31` of the order's first storage slot. In the current layout, slot `0` stores only a `uint128 orderId` at byte offset `0`, so byte offset `31` is guaranteed to be zero for all legacy orders.

Legacy layout:

```text
slot 0: orderId (uint128)
slot 1: maker (address)
slot 2: bookKey (bytes32)
slot 3: isBid (bool), tick (int16), amount (uint128)
slot 4: remaining (uint128), prev (uint128)
slot 5: next (uint128), isFlip (bool), flipTick (int16)
```

### Version 1 layout

T6+ orders use version `1`, stored at byte offset `31` of slot `0`.

```text
slot 0:
  offset 0   maker      address  20 bytes
  offset 20  isBid      bool      1 byte
  offset 21  tick       int16     2 bytes
  offset 23  isFlip     bool      1 byte
  offset 24  flipTick   int16     2 bytes
  offset 26  unused               5 bytes
  offset 31  version    uint8     1 byte

slot 1:
  offset 0   bookKey    bytes32   32 bytes

slot 2:
  offset 0   amount     uint128   16 bytes
  offset 16  remaining  uint128   16 bytes

slot 3:
  offset 0   prev       uint128   16 bytes
  offset 16  next       uint128   16 bytes
```

### Read behavior

All order reads MUST use the order storage abstraction. Before T6, reads MUST use the legacy layout directly. Starting at T6, reads MUST use the versioned dispatch below.

1. Compute the existing mapping value base slot from `orderId`.
2. Read slot `0`.
3. Decode byte offset `31` as `version`.
4. If `version == 0`, decode the legacy layout.
5. If `version == 1`, decode the version 1 layout and synthesize `orderId` from the mapping key.
6. Unknown versions MUST fail.

`getOrder(uint128)` keeps returning the existing ABI shape for compatibility. The returned `orderId` is synthesized from the function argument for versioned layouts.

### Write behavior

Before T6, new orders MUST be written using the legacy layout. Starting at T6, new orders MUST be written using the latest versioned layout. Mutations to active order fields (`remaining`, `prev`, `next`) MUST go through order storage methods rather than direct generated field accessors; before T6 those methods use legacy field access, and starting at T6 they are version-aware.

### Interaction with TIP-1056 flip rewrites

TIP-1056 changes automatic flip execution so that a fully filled flip order is rewritten in place under the same mapping key instead of allocating a new `orderId`.

After T6 activates, any order record newly written by user order placement or by a TIP-1056 flip rewrite MUST use the latest order storage layout. Concretely:

- user-submitted flip orders created by `placeFlip` use version `1`
- a version `0` flip order that flips after T6 activates is rewritten under the same `orderId` as version `1`, including clearing the two legacy-only slots that are not used by the version `1` layout
- a version `1` flip order that flips remains version `1`

The storage-version upgrade does not change TIP-1056 order identity: the mapping key remains the canonical `orderId`, and the flipped order still receives fresh queue priority at its destination tick.

### Deletion

Deleting an order clears the storage slots used by that order's versioned layout. After deletion, `getOrder` continues to report `OrderDoesNotExist` because the decoded maker is zero.

## Rationale

Keeping one mapping avoids a migration of order IDs, orderbook linked lists, and external references. A discriminator in slot `0` allows the DEX to support old and new order values indefinitely. Placing the discriminator in the currently unused high byte avoids ambiguity with existing `orderId` values.

The abstraction requirement is part of the protocol design: mixed-version linked lists are expected. A legacy order may point to a version 1 order, and a version 1 order may point to a legacy order. Therefore, updating a neighbor's `prev` or `next` pointer must be based on the neighbor's own version, not the current order's version.

## Invariants

- The `orders` mapping slot and mapping key type remain unchanged.
- Existing version `0` orders decode exactly as before.
- New version `1` orders synthesize `orderId` from the mapping key.
- Direct order storage access is not used outside the versioned order abstraction.
- Mixed-version linked lists remain valid: `prev` and `next` updates are version-aware per target order.
- `getOrder` remains ABI-compatible.

## Backwards Compatibility

Existing onchain orders remain readable and cancellable. Existing clients using `getOrder(uint128)` continue to receive the same return fields.

Indexers that reconstruct orders solely from events are unaffected by the storage layout. Tools that read raw storage must add version-aware decoding.
