> ## Documentation Index
> Fetch the complete documentation index at: https://docs.biconomy.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Message reference

> The exact EIP-712 structs, validation and nonce rules, wire format, error codes and limits of the price stream.

The four messages a maker signs, as the executor hashes them. Walkthrough: [Stream prices](/propamm/makers/streaming).

## Domain

| Field               | Value                                        |
| ------------------- | -------------------------------------------- |
| `name`              | `PropAMMExecutor`                            |
| `version`           | `2`                                          |
| `chainId`           | the chain the board lives on                 |
| `verifyingContract` | `0x000000fFA5f8Ae192Ab65204f9B7E062CbF4e05D` |

| Item            | Value                                                                                                                                                        |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Digest          | `keccak256(0x1901 \|\| domainSeparator \|\| structHash)`                                                                                                     |
| EOA signer      | 65-byte ECDSA signature                                                                                                                                      |
| Contract signer | EIP-1271 `isValidSignature`                                                                                                                                  |
| Onchain helpers | `DOMAIN_SEPARATOR()`, `hashLadder`, `hashOffsetLadder`, `hashAnchor`, `hashControls`, `ladderDigest`, `offsetLadderDigest`, `anchorDigest`, `controlsDigest` |

## Structs

Field order is encoding order.

```solidity theme={null}
struct Level {
    uint256 size;   // cumulative tokenIn volume available up to this level
    uint256 price;  // 1e18-scaled tokenOut per tokenIn for volume landing in this level
}

struct PriceLadder {
    address mm;        // signing address; the board key
    address provider;  // the IMMProvider contract that fills it; signed, so callers cannot swap it
    address tokenIn;
    address tokenOut;
    Level[] levels;    // ascending cumulative sizes, prices non-improving with depth
    uint256 nonce;     // depth nonce, strictly increasing per (mm, tokenIn, tokenOut)
    uint256 expiresAt; // unix seconds
}

struct Rung {
    uint256 size;       // cumulative tokenIn volume available up to this rung
    uint256 offsetPpm;  // discount below the anchor, parts per million
}

struct OffsetLadder {
    address mm;
    address provider;
    address tokenIn;
    address tokenOut;
    Rung[] rungs;               // ascending cumulative sizes, offsets non-decreasing with depth
    uint256 nonce;              // depth nonce, shared with price ladders for the same board
    uint256 expiresAt;          // unix seconds
    uint256 driftPpmPerSecond;  // offset widening per second of anchor age; 0 for none
}

struct Anchor {
    address mm;
    address tokenIn;
    address tokenOut;
    uint256 price;      // 1e18-scaled tokenOut per tokenIn
    uint256 nonce;      // anchor nonce, independent of the depth nonce
    uint256 timestamp;  // unix seconds the maker priced this anchor; the drift origin
    uint256 expiresAt;  // unix seconds; the board is dark past this
}

struct BoardControls {
    address mm;
    address tokenIn;
    address tokenOut;
    uint256 nonce;                  // controls nonce, its own sequence
    uint256 blockCap;               // most tokenIn one block may fill from this board; 0 for no cap
    uint256 widenPpmPerSqrtSecond;  // extra discount per square root of a second of quote age; 0 for none
    uint256 premiumPpm;             // extra discount inside the premium window; 0 for none
    uint256 premiumBlocks;          // blocks the premium covers after a commit; 0 for none
}
```

Type strings, verbatim from the contracts:

```
PriceLadder(address mm,address provider,address tokenIn,address tokenOut,Level[] levels,uint256 nonce,uint256 expiresAt)Level(uint256 size,uint256 price)
OffsetLadder(address mm,address provider,address tokenIn,address tokenOut,Rung[] rungs,uint256 nonce,uint256 expiresAt,uint256 driftPpmPerSecond)Rung(uint256 size,uint256 offsetPpm)
Anchor(address mm,address tokenIn,address tokenOut,uint256 price,uint256 nonce,uint256 timestamp,uint256 expiresAt)
BoardControls(address mm,address tokenIn,address tokenOut,uint256 nonce,uint256 blockCap,uint256 widenPpmPerSqrtSecond,uint256 premiumPpm,uint256 premiumBlocks)
```

The `types` object for viem or ethers `signTypedData`:

```ts theme={null}
const types = {
  Level:        [{ name: "size", type: "uint256" }, { name: "price", type: "uint256" }],
  PriceLadder:  [
    { name: "mm", type: "address" }, { name: "provider", type: "address" },
    { name: "tokenIn", type: "address" }, { name: "tokenOut", type: "address" },
    { name: "levels", type: "Level[]" }, { name: "nonce", type: "uint256" },
    { name: "expiresAt", type: "uint256" },
  ],
  Rung:         [{ name: "size", type: "uint256" }, { name: "offsetPpm", type: "uint256" }],
  OffsetLadder: [
    { name: "mm", type: "address" }, { name: "provider", type: "address" },
    { name: "tokenIn", type: "address" }, { name: "tokenOut", type: "address" },
    { name: "rungs", type: "Rung[]" }, { name: "nonce", type: "uint256" },
    { name: "expiresAt", type: "uint256" }, { name: "driftPpmPerSecond", type: "uint256" },
  ],
  Anchor:       [
    { name: "mm", type: "address" }, { name: "tokenIn", type: "address" },
    { name: "tokenOut", type: "address" }, { name: "price", type: "uint256" },
    { name: "nonce", type: "uint256" }, { name: "timestamp", type: "uint256" },
    { name: "expiresAt", type: "uint256" },
  ],
  BoardControls: [
    { name: "mm", type: "address" }, { name: "tokenIn", type: "address" },
    { name: "tokenOut", type: "address" }, { name: "nonce", type: "uint256" },
    { name: "blockCap", type: "uint256" }, { name: "widenPpmPerSqrtSecond", type: "uint256" },
    { name: "premiumPpm", type: "uint256" }, { name: "premiumBlocks", type: "uint256" },
  ],
} as const;
const domain = { name: "PropAMMExecutor", version: "2", chainId, verifyingContract: executor };
```

| Message                       | `provider`                            | `expiresAt`                             |
| ----------------------------- | ------------------------------------- | --------------------------------------- |
| `PriceLadder`, `OffsetLadder` | yes, binds the provider for the board | yes                                     |
| `Anchor`                      | no, uses the board's provider         | yes                                     |
| `BoardControls`               | no, uses the board's provider         | no, holds until a higher controls nonce |

## Prices and sizes

| Item   | Rule                                                  |
| ------ | ----------------------------------------------------- |
| Output | `amountOut = amountIn * price / 1e18`, smallest units |
| Price  | `humanPrice * 1e18 * 10^(decimalsOut - decimalsIn)`   |
| Size   | cumulative `tokenIn`, smallest units                  |

| Example                        | Price               |
| ------------------------------ | ------------------- |
| WETH (18) to USDC (6) at 2,000 | `2000e6`            |
| USDC (6) to WETH (18) at 2,000 | `5e26`              |
| same decimals                  | `humanPrice * 1e18` |

Offset rung price, evaluated onchain at fill time:

```
price = anchor.price * (1e6 - offsetPpm - driftPpmPerSecond * (block.timestamp - anchor.timestamp)) / 1e6, floored
```

A rung whose discount reaches `1e6` drops off with every deeper rung. Without its first rung the board stops quoting.

| Anchor age | Discount (offset 500 ppm, drift 2 ppm/s) | First rung, anchor `2000e18` |
| ---------- | ---------------------------------------- | ---------------------------- |
| 0 s        | 500 ppm                                  | `1999e18`                    |
| 30 s       | 560 ppm                                  | `1998.88e18`                 |
| 300 s      | 1100 ppm                                 | `1997.8e18`                  |

## Validation rules

The stream and the executor both reject a message that fails any of these.

| Rule                                                                                                                            | Applies to           |
| ------------------------------------------------------------------------------------------------------------------------------- | -------------------- |
| `tokenIn != 0`, `tokenOut != 0`, `tokenIn != tokenOut`, `provider != 0`                                                         | ladders              |
| `nonce` fits `uint128`, `expiresAt` fits `uint40`                                                                               | ladders              |
| `expiresAt` in the future at commit time, at most 1 hour ahead                                                                  | ladders and anchors  |
| 1 to 20 levels or rungs                                                                                                         | ladders              |
| sizes strictly ascending, first size above zero, every size fits `uint128`                                                      | ladders              |
| prices above zero, each at most the previous price, every price fits `uint128`                                                  | price ladders        |
| offsets at least the previous offset and below `1_000_000`                                                                      | offset ladders       |
| `driftPpmPerSecond` fits `uint32`                                                                                               | offset ladders       |
| `nonce` fits `uint48`                                                                                                           | anchors and controls |
| `price` above zero and fits `uint128`                                                                                           | anchors              |
| `timestamp <= expiresAt`                                                                                                        | anchors              |
| `blockCap` fits `uint128`, `widenPpmPerSqrtSecond` fits `uint32`, `premiumPpm` below `1_000_000`, `premiumBlocks` fits `uint16` | controls             |
| signature verifies for `mm` under the executor's domain for `chainId`                                                           | all                  |
| `provider` equals the provider registered for you on that pair                                                                  | ladders, stream only |

## Nonce rules

| Nonce                                             | Scope                                      | Rule                                                           |
| ------------------------------------------------- | ------------------------------------------ | -------------------------------------------------------------- |
| depth (`PriceLadder.nonce`, `OffsetLadder.nonce`) | one per board, shared by both ladder forms | strictly greater than the stored depth nonce; fits `uint128`   |
| anchor (`Anchor.nonce`)                           | one per board, independent                 | strictly greater than the stored anchor nonce; fits `uint48`   |
| controls (`BoardControls.nonce`)                  | one per board, independent                 | strictly greater than the stored controls nonce; fits `uint48` |

A message with a nonce at or below the stored one is skipped onchain and returns `STALE_NONCE` on the stream, including after expiry. Unix milliseconds fit every limit. Nonces are per board, so one value can be reused across pairs and directions. A depth commit resets the fill count; anchors and controls leave it unchanged.

## Wire format

| Item      | Format                                   |
| --------- | ---------------------------------------- |
| Frames    | JSON text                                |
| Numbers   | decimal strings (JSON integers accepted) |
| Addresses | hex                                      |
| `chainId` | number                                   |

```json theme={null}
{ "type": "subscribe", "data": { "type": "price-ledger", "mm": "0x1111111111111111111111111111111111111111" } }
```

<Tabs>
  <Tab title="price-ladder">
    ```json theme={null}
    {
      "type": "price-ladder",
      "payload": {
        "mm": "0x1111111111111111111111111111111111111111",
        "provider": "0x2222222222222222222222222222222222222222",
        "tokenIn": "0x8b414aD7005EeFd315aF2A16538885Eae229bab7",
        "tokenOut": "0xAbbdbbbd6d56593A9c5656c06cB30D61E4a544Df",
        "levels": [
          { "size": "1000000000000000000", "price": "1998000000000000000000" },
          { "size": "3000000000000000000", "price": "1995000000000000000000" }
        ],
        "nonce": "1753290000123",
        "expiresAt": "1753290030",
        "signature": "0x…65 bytes…",
        "chainId": 84532
      }
    }
    ```
  </Tab>

  <Tab title="offsets">
    ```json theme={null}
    {
      "type": "offsets",
      "payload": {
        "mm": "0x1111111111111111111111111111111111111111",
        "provider": "0x2222222222222222222222222222222222222222",
        "tokenIn": "0x8b414aD7005EeFd315aF2A16538885Eae229bab7",
        "tokenOut": "0xAbbdbbbd6d56593A9c5656c06cB30D61E4a544Df",
        "rungs": [
          { "size": "1000000000000000000", "offsetPpm": "1000" },
          { "size": "3000000000000000000", "offsetPpm": "2500" }
        ],
        "nonce": "1753290000124",
        "expiresAt": "1753293600",
        "driftPpmPerSecond": "10",
        "signature": "0x…65 bytes…",
        "chainId": 84532
      }
    }
    ```
  </Tab>

  <Tab title="anchor">
    ```json theme={null}
    {
      "type": "anchor",
      "payload": {
        "mm": "0x1111111111111111111111111111111111111111",
        "tokenIn": "0x8b414aD7005EeFd315aF2A16538885Eae229bab7",
        "tokenOut": "0xAbbdbbbd6d56593A9c5656c06cB30D61E4a544Df",
        "price": "2000000000000000000000",
        "nonce": "88213",
        "timestamp": "1753290000",
        "expiresAt": "1753290030",
        "signature": "0x…65 bytes…",
        "chainId": 84532
      }
    }
    ```
  </Tab>

  <Tab title="board-controls">
    ```json theme={null}
    {
      "type": "board-controls",
      "payload": {
        "mm": "0x1111111111111111111111111111111111111111",
        "tokenIn": "0x8b414aD7005EeFd315aF2A16538885Eae229bab7",
        "tokenOut": "0xAbbdbbbd6d56593A9c5656c06cB30D61E4a544Df",
        "nonce": "88214",
        "blockCap": "5000000000000000000",
        "widenPpmPerSqrtSecond": "100",
        "premiumPpm": "500",
        "premiumBlocks": "1",
        "signature": "0x…65 bytes…",
        "chainId": 84532
      }
    }
    ```
  </Tab>
</Tabs>

| Field                                 | Type                   | Notes                                                                                |
| ------------------------------------- | ---------------------- | ------------------------------------------------------------------------------------ |
| `mm`                                  | address                | signing key; the board key. Must equal the subscribed maker and the recovered signer |
| `provider`                            | address                | ladders and offsets only; must equal the provider registered for you on the pair     |
| `tokenIn`, `tokenOut`                 | address                | real ERC20 addresses, distinct                                                       |
| `levels[].size`, `rungs[].size`       | uint128 as string      | cumulative tokenIn depth, strictly ascending                                         |
| `levels[].price`                      | uint128 as string      | 1e18-scaled tokenOut per tokenIn, non-increasing with depth                          |
| `rungs[].offsetPpm`                   | string                 | discount below the anchor in ppm, non-decreasing with depth, below 1e6               |
| `driftPpmPerSecond`                   | uint32 as string       | offset widening per second of anchor age; `"0"` disables                             |
| `nonce`                               | string                 | depth nonce (uint128) for ladders and offsets, uint48 for anchors and controls       |
| `timestamp`                           | unix seconds as string | anchors only; when the maker priced it; not after `expiresAt`                        |
| `expiresAt`                           | unix seconds as string | uint40; at most 1 hour ahead                                                         |
| `price`                               | uint128 as string      | anchors only; positive                                                               |
| `blockCap`                            | uint128 as string      | controls only; `"0"` for no cap                                                      |
| `widenPpmPerSqrtSecond`, `premiumPpm` | uint32 as string       | controls only; ppm, below `1e6`; `"0"` disables                                      |
| `premiumBlocks`                       | uint16 as string       | controls only; `"0"` disables                                                        |
| `signature`                           | hex                    | 65-byte EIP-712 signature by `mm`                                                    |
| `chainId`                             | number                 | the chain the board lives on                                                         |

## Responses and error codes

| Response                                 | Meaning                                                                                 |
| ---------------------------------------- | --------------------------------------------------------------------------------------- |
| `{ "type": "ack" }`                      | validated and stored; Biconomy commits it onchain                                       |
| `{ "type": "error", "code", "message" }` | rejected; the last accepted message for that board stays in force until its `expiresAt` |

| Code                        | Meaning                                                | Fix                                                          |
| --------------------------- | ------------------------------------------------------ | ------------------------------------------------------------ |
| `INVALID_JSON`              | frame is not valid JSON                                | check encoding                                               |
| `INVALID_MESSAGE`           | schema mismatch, or a validation rule above failed     | see the rules                                                |
| `NOT_SUBSCRIBED`            | a board message before the subscribe `ack`             | subscribe first                                              |
| `MARKET_MAKER_MISMATCH`     | payload `mm` differs from the subscribed `mm`          | one signer per connection                                    |
| `RATE_LIMITED`              | over 300 board messages per second on the connection   | back off                                                     |
| `UNSUPPORTED_CHAIN`         | `chainId` not served by this environment               | check the endpoint                                           |
| `UPDATE_EXPIRED`            | `expiresAt` already in the past                        | check clock skew; `expiresAt` is in seconds                  |
| `INVALID_TOKEN_PAIR`        | `tokenIn` equals `tokenOut`                            | fix the pair                                                 |
| `UNREGISTERED_MARKET_MAKER` | signer not registered                                  | complete registration                                        |
| `INVALID_SIGNATURE`         | recovered signer is not `mm`                           | check domain version `2`, executor address, `chainId`, types |
| `UNSUPPORTED_PAIR`          | pair or direction not registered for you on that chain | register it                                                  |
| `PROVIDER_MISMATCH`         | signed `provider` differs from the registered one      | sign the registered provider                                 |
| `STALE_NONCE`               | nonce at or below the last accepted one for this board | strictly increasing nonces                                   |
| `STORE_FAILED`              | transient server-side failure                          | safe to continue                                             |

## Limits

| Limit                       | Value                                                                                |
| --------------------------- | ------------------------------------------------------------------------------------ |
| Rate limit                  | 300 board messages per second per connection (token bucket, 300 burst)               |
| Frame size                  | 64 KB                                                                                |
| Idle timeout                | 60 s without inbound traffic; server pings every 10 s; `{ "type": "ping" }` accepted |
| Levels or rungs per message | 20                                                                                   |
| `expiresAt`                 | in the future, at most 1 hour ahead                                                  |
