# How Data Is Serialized in the Amadeus Protocol: A Complete Guide to Binary Encoding

> Master binary data serialization in the Amadeus Protocol. Discover the custom AssemblyScript scheme, canonical Serializer class, and deterministic payloads for efficient on-chain storage and exchange.

- Repository: [Amadeus Protocol/node](https://github.com/amadeusprotocol/node)
- Tags: deep-dive
- Published: 2026-08-20

---

**The Amadeus Protocol uses a custom binary serialization scheme built in AssemblyScript, centered on a canonical `Serializer` class that produces deterministic, sorted-key binary payloads for on-chain storage and cross-environment data exchange.**

Data serialization in the Amadeus Protocol is implemented through a purpose-built binary format designed for deterministic encoding, compact storage, and seamless interoperability. The system lives in the `amadeusprotocol/node` repository, with core logic implemented in AssemblyScript smart contract samples. This guide examines the complete serialization pipeline from low-level encoding primitives to high-level domain object persistence.

## Core Serialization Architecture

The Amadeus Protocol's serialization system is built around two complementary abstractions: low-level encoding helpers for primitive types and a high-level `Serializer` class that orchestrates field ordering and final output. Both are defined in [`contract_samples/assemblyscript/sdk_vecpak.ts`](https://github.com/amadeusprotocol/node/blob/main/contract_samples/assemblyscript/sdk_vecpak.ts).

### Low-Level Encoding Primitives

At the foundation, the protocol provides type-tagged encoding functions that write primitive values into a growing `Array<u8>`. Each function prefixes the value with a type tag and uses varint encoding for length-prefixed data:

- `encodeVarint` – Writes variable-length integers
- `encodeU16` / `encodeI16` – Fixed-width 16-bit integers
- `encodeString` – Length-prefixed UTF-8 strings
- `encodeBytes` – Length-prefixed raw byte arrays

These helpers use explicit type constants such as `TYPE_INT`, `TYPE_BYTES`, and `TYPE_MAP` to ensure unambiguous decoding. The varint approach minimizes byte overhead for small values while accommodating larger numbers when needed.

### The Serializer Class

The **`Serializer`** class provides the primary interface for building serialized payloads. It accumulates fields as key-value pairs, then produces a canonical binary representation on demand.

Field addition follows a consistent pattern:

```assemblyscript
import { Serializer } from "./sdk_vecpak";

const s = new Serializer();
s.addString("name", "Excalibur");
s.addU16("damage", 42);
s.addI16("modifier", -3);

```

Each `add*` method performs three operations:
1. Encodes the value using the appropriate low-level helper
2. Creates a `Field` struct containing the key (as bytes) and value bytes
3. Stores the field in an internal collection

When `finish()` is called, the serializer sorts all fields by their canonical binary key representation, writes a `TYPE_MAP` header, and concatenates the sorted key-value pairs into a finalized `Uint8Array`. This sorting guarantees **deterministic encoding**—critical for blockchain applications where byte-exact equality affects state validation.

## Deserialization with the Deserializer Class

The counterpart to `Serializer` is the **`Deserializer`** class, also defined in [`sdk_vecpak.ts`](https://github.com/amadeusprotocol/node/blob/main/sdk_vecpak.ts). It reconstructs objects from binary streams through a cursor-based API:

```assemblyscript
import { Deserializer } from "./sdk_vecpak";

const d = new Deserializer(data);
while (d.hasNext()) {
  const key = d.nextKey();
  // Branch on key to read appropriate type
}

```

Key methods include:
- `hasNext()` – Checks if additional map entries remain
- `nextKey()` – Advances to and returns the next field's key
- `readU16()`, `readI16()`, `readString()`, `readBytes()` – Type-specific value extraction
- `skip()` – Advances past unknown fields for forward compatibility

The `DecodeRef` pointer tracks position through the byte array, enabling safe sequential access without exposing raw offsets to callers.

## Domain Object Serialization Pattern

Real-world Amadeus contracts implement serialization through a consistent interface: instance method `serialize(): Uint8Array` and static `deserialize(data: Uint8Array)`. The RPG sample demonstrates this pattern in [`contract_samples/assemblyscript/5_rpg/model.ts`](https://github.com/amadeusprotocol/node/blob/main/contract_samples/assemblyscript/5_rpg/model.ts).

### Example: Weapon Class Implementation

```assemblyscript
import { Serializer, Deserializer } from "./sdk_vecpak";

export class Weapon {
  name: string = "";
  damage: u16 = 0;

  serialize(): Uint8Array {
    const s = new Serializer();
    s.addString("name", this.name);
    s.addU16("damage", this.damage);
    return s.finish();
  }

  static deserialize(data: Uint8Array): Weapon {
    const d = new Deserializer(data);
    const w = new Weapon();

    while (d.hasNext()) {
      const key = d.nextKey();
      if (key == "name") {
        w.name = d.readString();
      } else if (key == "damage") {
        w.damage = d.readU16();
      } else {
        d.skip(); // Forward compatibility for new fields
      }
    }
    return w;
  }
}

```

The `skip()` call in the `else` branch enables **forward compatibility**—contracts can read data written by newer code versions without failing on unrecognized fields.

## On-Chain Persistence

Serialized data moves between memory and persistent storage through the Amadeus SDK's key-value API. The sample entry point [`contract_samples/assemblyscript/5_rpg/main.ts`](https://github.com/amadeusprotocol/node/blob/main/contract_samples/assemblyscript/5_rpg/main.ts) demonstrates this workflow:

```assemblyscript
import { sdk } from "amadeus-sdk";
import { Weapon } from "./weapon";

export function storeWeapon(key: string, w: Weapon): void {
  sdk.kv_put(key, w.serialize());
}

export function loadWeapon(key: string): Weapon | null {
  const bytes = sdk.kv_get(key);
  if (bytes) {
    return Weapon.deserialize(bytes);
  }
  return null;
}

```

The `sdk.kv_put` and `sdk.kv_get` functions handle the boundary between contract logic and chain state. Binary serialization minimizes storage costs compared to text formats like JSON, while the canonical sorting ensures that identical logical objects produce identical bytes—preventing state divergence across validator implementations.

## Design Rationale and Trade-offs

The Amadeus Protocol's custom serialization prioritizes three properties:

- **Determinism** – Sorted field keys and explicit type tags eliminate encoding ambiguity
- **Compactness** – Varint encoding and binary format reduce on-chain storage costs
- **Simplicity** – Single-file implementation in [`sdk_vecpak.ts`](https://github.com/amadeusprotocol/node/blob/main/sdk_vecpak.ts) enables auditability and cross-language porting

These choices reflect blockchain-specific constraints: storage is expensive, consensus requires byte-exact agreement, and contracts must run in resource-constrained environments like WebAssembly.

## Summary

- The **`Serializer`** class in [`sdk_vecpak.ts`](https://github.com/amadeusprotocol/node/blob/main/sdk_vecpak.ts) produces canonical binary maps with sorted keys
- Low-level helpers (`encodeU16`, `encodeString`, etc.) handle type-tagged varint encoding
- The **`Deserializer`** class reconstructs objects through sequential cursor-based access
- Domain objects implement `serialize()` and static `deserialize()` for type-safe persistence
- On-chain storage uses `sdk.kv_put()` and `sdk.kv_get()` with binary payloads
- Forward compatibility is supported via the `skip()` method for unknown fields

## Frequently Asked Questions

### What programming language does the Amadeus Protocol use for serialization?

The Amadeus Protocol implements its serialization system in **AssemblyScript**, a TypeScript-like language that compiles to WebAssembly. This choice enables deterministic execution in blockchain environments while maintaining familiar syntax for developers. The core implementation resides in [`contract_samples/assemblyscript/sdk_vecpak.ts`](https://github.com/amadeusprotocol/node/blob/main/contract_samples/assemblyscript/sdk_vecpak.ts).

### Why does the Amadeus Protocol use a custom binary format instead of JSON or Protocol Buffers?

The protocol requires **deterministic encoding** where identical logical values always produce identical byte sequences. JSON lacks guaranteed field ordering and number encoding precision. Protocol Buffers, while binary, don't guarantee deterministic serialization across implementations. The custom format in [`sdk_vecpak.ts`](https://github.com/amadeusprotocol/node/blob/main/sdk_vecpak.ts) ensures sorted keys, explicit type tags, and consistent varint encoding—essential for blockchain consensus.

### How does the Amadeus Protocol handle schema evolution and backward compatibility?

The `Deserializer` class provides the `skip()` method to advance past unrecognized fields. When deserializing, contracts can iterate through map entries with `hasNext()` and `nextKey()`, processing known fields while calling `skip()` for unknown keys. This allows older contract code to read data written by newer versions without breaking.