Skip to content

Declarative CBOR schema via annotations (CborId) #189

@solidsnakedev

Description

@solidsnakedev

Summary

Replace hand-written FromCDDL transforms with declarative schema annotations that describe CBOR wire format directly on Effect Schema types.

Motivation

Currently each module (TransactionBody, TransactionWitnessSet, Transaction, etc.) has:

  • A CDDLSchema (intermediate CBOR AST type)
  • A FromCDDL transform (hand-written 200-500 line encode/decode)
  • A FromCBORBytes that composes FromBytes with FromCDDL

This creates a two-hop pipeline (Uint8Array → CBOR AST → domain) with a validation boundary that clones objects and strips Symbol metadata (e.g. kEncoding for encoding preservation).

Proposal

A CborId Symbol annotation on Effect Schemas that carries CBOR layout metadata:

const CborId = Symbol.for("evolution/CborId")

const Address = Schema.Uint8ArrayFromSelf.annotations({
  [CborId]: { tag: 259 }
})

const Value = Schema.Struct({
  coin: Schema.BigIntFromSelf,
  multiasset: Schema.optional(MultiAsset),
}).annotations({
  [CborId]: { encoding: "array", fieldOrder: ["coin", "multiasset"] }
})

const TransactionOutput = Schema.Struct({
  address: Address,
  value: Value,
  datum: Schema.optional(Datum),
  scriptRef: Schema.optional(ScriptRef),
}).annotations({
  [CborId]: { encoding: "map", keys: { address: 0n, value: 1n, datum: 2n, scriptRef: 3n } }
})

A generic CBOR codec engine reads [CborId] annotations to:

  • Map struct fields to integer-keyed CBOR map entries or positional array slots
  • Wrap values in CBOR tags (e.g. #6.258 for sets)
  • Handle optional fields (omit from map when undefined)
  • Thread kEncoding preservation metadata automatically

Benefits

  • Eliminates FromCDDL — no hand-written encode/decode per module
  • Eliminates CDDLSchema — domain schema IS the codec schema, no intermediate type
  • Encoding preservation is free — single transform boundary, no clone/Symbol stripping
  • Self-documenting — annotations describe the wire format directly
  • Less code — TransactionBody goes from ~500 lines of transform to ~25 lines of annotated schema

Scope

  • Build CborId annotation types and codec engine in CBOR.ts (~200-400 lines)
  • Convert all modules with FromCDDL transforms to use annotated schemas
  • Handle edge cases: Tag-258 sets, redeemer map/array duality, nested maps, etc.
  • Maintain backward compatibility with existing domain types

Context

Discovered during CBOR encoding preservation work. The immediate fix (replacing MapFromSelf/Tuple CDDLSchemas with non-cloning Schema.declare) unblocks encoding preservation. This issue tracks the larger architectural improvement.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions