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

# Datum building

> How to construct the order datum, calculate prices, serialize addresses, and compute order beacons.

Every on-chain order carries a datum that encodes the order parameters. This page covers how to build each field.

## Datum structure

```typescript theme={null} theme={null}
import { Data } from "@lucid-evolution/lucid";

const LimitOrderConfigSchema = {
  title: "LimitOrderConfig",
  anyOf: [
    {
      title: "LimitOrderConfig",
      dataType: "constructor",
      index: 0,
      fields: [
        { dataType: "bytes", title: "tag" },
        {
          title: "redeemerAddress",
          anyOf: [
            {
              title: "Address",
              dataType: "constructor",
              index: 0,
              fields: [
                {
                  title: "paymentCredential",
                  anyOf: [
                    {
                      title: "VerificationKey",
                      dataType: "constructor",
                      index: 0,
                      fields: [{ dataType: "bytes" }],
                    },
                    {
                      title: "Script",
                      dataType: "constructor",
                      index: 1,
                      fields: [{ dataType: "bytes" }],
                    },
                  ],
                },
                {
                  title: "stakeCredential",
                  anyOf: [
                    {
                      title: "Some",
                      dataType: "constructor",
                      index: 0,
                      fields: [
                        {
                          anyOf: [
                            {
                              title: "Inline",
                              dataType: "constructor",
                              index: 0,
                              fields: [
                                {
                                  anyOf: [
                                    {
                                      title: "VerificationKey",
                                      dataType: "constructor",
                                      index: 0,
                                      fields: [{ dataType: "bytes" }],
                                    },
                                    {
                                      title: "Script",
                                      dataType: "constructor",
                                      index: 1,
                                      fields: [{ dataType: "bytes" }],
                                    },
                                  ],
                                },
                              ],
                            },
                            {
                              title: "Pointer",
                              dataType: "constructor",
                              index: 1,
                              fields: [
                                { dataType: "integer", title: "slotNumber" },
                                { dataType: "integer", title: "transactionIndex" },
                                { dataType: "integer", title: "certificateIndex" },
                              ],
                            },
                          ],
                        },
                      ],
                    },
                    {
                      title: "None",
                      dataType: "constructor",
                      index: 1,
                      fields: [],
                    },
                  ],
                },
              ],
            },
          ],
        },
        {
          title: "input",
          dataType: "list",
          items: [
            { title: "PolicyId", dataType: "bytes" },
            { title: "AssetName", dataType: "bytes" },
          ],
        },
        { dataType: "integer", title: "tradableInput" },
        { dataType: "integer", title: "feePerExStep" },
        {
          title: "output",
          dataType: "list",
          items: [
            { title: "PolicyId", dataType: "bytes" },
            { title: "AssetName", dataType: "bytes" },
          ],
        },
        {
          title: "basePrice",
          dataType: "list",
          items: [{ dataType: "integer" }, { dataType: "integer" }],
        },
        { title: "cancellationPkh", dataType: "bytes" },
        { dataType: "bytes", title: "beacon" },
      ],
    },
  ],
} as const;

type BuildDatumParams = {
  paymentKeyHash: string;
  stakeKeyHash: string;
  input: [string, string];
  tradableInput: bigint;
  output: [string, string];
  basePrice: [bigint, bigint];
  cancellationPkh: string;
  beacon: string;
  feePerExStep?: bigint;
  tag?: string;
};

const buildDatum = ({
  paymentKeyHash,
  stakeKeyHash,
  input,
  tradableInput,
  output,
  basePrice,
  cancellationPkh,
  beacon,
  feePerExStep = 600000n,
  tag = "09",
}: BuildDatumParams) => {
  const datum = Data.to(
    {
      tag,
      redeemerAddress: {
        paymentCredential: {
          VerificationKey: [paymentKeyHash],
        },
        stakeCredential: {
          Inline: [
            {
              VerificationKey: [stakeKeyHash],
            },
          ],
        },
      },
      input,
      tradableInput,
      feePerExStep,
      output,
      basePrice,
      cancellationPkh,
      beacon,
    },
    LimitOrderConfigSchema
  );
  return datum;
};
```

## Field reference

| Field             | Type    | Description                                                          |
| ----------------- | ------- | -------------------------------------------------------------------- |
| `tag`             | bytes   | `"09"` in hex format                                                 |
| `redeemerAddress` | constr  | User's address for receiving funds after execution                   |
| `input`           | list    | `[policyId, tokenName]` - what the user is selling. ADA = `["", ""]` |
| `tradableInput`   | integer | Amount of input asset in base units                                  |
| `feePerExStep`    | integer | `600000` (0.6 ADA)                                                   |
| `output`          | list    | `[policyId, tokenName]` - what the user wants to receive             |
| `basePrice`       | list    | `[numerator, denominator]` - price ratio                             |
| `cancellationPkh` | bytes   | User's payment key hash for order cancellation                       |
| `beacon`          | bytes   | 20-byte order identifier                                             |

## Price calculation

Price is the `output / input` ratio, stored as `[numerator, denominator]`.

<Tabs>
  <Tab title="Buy order">
    Buying token with ADA:

    * Input: ADA
    * Output: Token
    * Price: `tokenAmount / adaAmount` = `[tokenAmount, adaAmount]`

    **Example:** Buy 100 tokens for 50 ADA -> `[100, 50]` = 2 tokens per ADA
  </Tab>

  <Tab title="Sell order">
    Selling token for ADA:

    * Input: Token
    * Output: ADA
    * Price: `adaAmount / tokenAmount` = `[adaAmount, tokenAmount]`

    **Example:** Sell 100 tokens for 50 ADA -> `[50, 100]` = 0.5 ADA per token
  </Tab>

  <Tab title="Market order">
    Set `numerator = 1` to accept any price:

    Price: `[1, denominator]`
  </Tab>
</Tabs>

## Input amount

* **Buy order:** `tradableInput` = ADA amount the user is willing to spend
* **Sell order:** `tradableInput` = token amount the user is selling

## Beacon calculation

The order beacon is a 20-byte unique identifier that prevents beacon collisions. Its primary purpose is to prevent beacon collisions - ensuring that every order placed on-chain has a globally unique identifier.

**Structure:**

* Byte 0: order type (`0` = limit, `1` = market)
* Bytes 1-19: first 19 bytes of a blake2b-224 hash

<Warning>
  All numeric indices must be encoded as **big-endian** uint64 (8 bytes).
</Warning>

### Algorithm

<Steps>
  <Step title="Create empty beacon">
    28 bytes of zeros: `0000...0000`
  </Step>

  <Step title="Serialize datum with empty beacon">
    Convert the datum (with empty beacon) to CBOR format.
  </Step>

  <Step title="Hash the datum">
    Apply blake2b-224 to the serialized datum CBOR bytes.
  </Step>

  <Step title="Build beacon preimage">
    Concatenate in order:

    * Transaction hash (32 bytes) of one of the transaction inputs
    * Output index (8 bytes, big-endian uint64)
    * Order index (8 bytes, big-endian uint64)
    * Datum hash (28 bytes) from step 3
  </Step>

  <Step title="Calculate final beacon">
    Apply blake2b-224 to the preimage, take the first 19 bytes, and prefix with the order type byte (`0` or `1`). Result: 20 bytes.
  </Step>
</Steps>

### Implementation

```typescript theme={null} theme={null}
import { Data, fromHex, toHex, CML, } from "@lucid-evolution/lucid";
import { blake2b } from "hash-wasm";
import { Uint64BE } from "int64-buffer";

const EMPTY_BEACON = toHex(new Uint8Array(28).fill(0));

// Step 1: Build datum with empty beacon (same shape as order datum, beacon = EMPTY_BEACON), serialize with LimitOrderConfigSchema
const orderDatumFields = {
  // All order datum fields except beacon (tag, redeemerAddress, input, tradableInput, feePerExStep, output, basePrice, cancellationPkh)
  ...buildDatumInput,
};
const orderDatumFieldsWithEmptyBeacon = {
  ...orderDatumFields,
  beacon: EMPTY_BEACON,
};
const datumCborHex = Data.to(orderDatumFieldsWithEmptyBeacon, LimitOrderConfigSchema, {
  canonical: true,
});
const datumCborBytes = CML.PlutusData.from_cbor_hex(datumCborHex).to_cbor_bytes();

// Step 2: Hash datum (blake2b-224 over serialized CBOR bytes)
const datumHashHex = await blake2b(datumCborBytes, 224);
const datumHash = fromHex(datumHashHex);

// Step 3: Beacon preimage (txHash 32 bytes + outputIndex 8 BE + orderIndex 8 BE + datumHash 28 bytes)
const txHashBytes = fromHex(outputReference.txHash);
const beaconPreimage = new Uint8Array([
  ...txHashBytes,
  ...new Uint64BE(Number(outputReference.outputIndex)).toArray(),
  ...new Uint64BE(Number(orderIndex)).toArray(),
  ...datumHash,
]);

// Step 4: Hash and prefix with order type (first 19 bytes of blake2b-224)
const beaconHashHex = await blake2b(beaconPreimage, 224);
const beaconHashFirst19 = fromHex(beaconHashHex.slice(0, 38));
const beacon = toHex(
  new Uint8Array([
    orderType === "limit" ? 0 : 1,
    ...beaconHashFirst19,
  ])
);
```

Where:

* `outputReference` - one of the transaction inputs (UTxO reference)
* `orderIndex` - index of the order output in the transaction (`BigInt`)
* `orderType` - `'limit'` or `'market'`

## Real example

Buy order: 50 ADA for NIGHT token at \~4.2 tokens per ADA.

<Expandable title="Full datum JSON">
  ```json theme={null} theme={null}
  {
    "constructor": "0",
    "fields": [
      { "bytes": "09" },
      {
        "constructor": "0",
        "fields": [
          {
            "constructor": "0",
            "fields": [
              { "bytes": "3533ded9539c6ed7dce55b29a5fd341d78d3fca4bebabc4fb9f1894b" }
            ]
          },
          {
            "constructor": "0",
            "fields": [
              {
                "constructor": "0",
                "fields": [
                  {
                    "constructor": "0",
                    "fields": [
                      { "bytes": "f985dec9000fe612fc20520200f91df5520aa5444059a5cebe411295" }
                    ]
                  }
                ]
              }
            ]
          }
        ]
      },
      { "list": [{ "bytes": "" }, { "bytes": "" }] },
      { "int": "50000000" },
      { "int": "600000" },
      {
        "list": [
          { "bytes": "0691b2fecca1ac4f53cb6dfb00b7013e561d1f34403b957cbb5af1fa" },
          { "bytes": "4e49474854" }
        ]
      },
      { "list": [{ "int": "209790827" }, { "int": "50000000" }] },
      { "bytes": "3533ded9539c6ed7dce55b29a5fd341d78d3fca4bebabc4fb9f1894b" },
      { "bytes": "016b44032ba4b5e00b0e883e37b20e66e6ba34ca" }
    ]
  }
  ```
</Expandable>

[View transaction on CardanoScan](https://cardanoscan.io/transaction/c77d82d0592252d86a175576665bcf92cbf689ddf92a496cd106cdad75930e79?tab=utxo)
