This project provides Zod schemas for parsing and validating JSON inputs similar to Cardano native scripts, with input normalization (hex strings → Uint8Array via fromHex from @midnight-ntwrk/compact-runtime).
NativeScriptSchema is the top-level entry point for validation. It accepts all clause types — commitment, time locks (after, before), and composites (any, all, atLeast) — at any level, matching the full native script BNF.
The schemas are organized into three layers, and this document follows the same structure:
- Base — primitive types used by other schemas (e.g.
Uint8ArraySchemafor hex normalization) - Leaf — terminal clauses that cannot contain other clauses (e.g.
CommitmentSchema,AfterClauseSchema,BeforeClauseSchema) - Composite — scripts that contain a
scriptsarray of nested clauses or scripts, enabling arbitrary nesting
Purpose: Normalize various input types to Uint8Array.
Accepted inputs:
Uint8Arrayinstances (passed through)- 64-character lowercase hex strings → transformed to 32-byte
Uint8ArrayviafromHex
Not accepted: number[], uppercase hex, wrong length.
Purpose: Commitment verification against a commitment hash, functioning as a signature-equivalent verifier. Rather than checking a cryptographic signature, it validates that the transaction was authorized by proving knowledge of a secret corresponding to the commitment hash.
Shape:
{
type: "cmt",
hash: Uint8Array // 32 bytes (64 hex chars)
}Fields:
type: Literal"cmt"hash: AUint8ArraySchemavalue (32-byte commitment hash as Uint8Array)
Purpose: Validates that the transaction is valid at or after the specified block. Corresponds to RequireTimeAfter in the Cardano native script BNF.
Shape:
{
type: "after",
block: number // non-negative integer
}Fields:
type: Literal"after"block: Non-negative integer — the transaction must have a validity interval lower bound at or after this block
Purpose: Validates that the transaction is valid before the specified block. Corresponds to RequireTimeBefore in the Cardano native script BNF.
Shape:
{
type: "before",
block: number // non-negative integer
}Fields:
type: Literal"before"block: Non-negative integer — the transaction must have a validity interval upper bound at or before this block (exclusive)
All composite scripts contain a scripts array that can hold nested scripts (leaf or composite).
Purpose: At least one contained script must be satisfied.
Shape:
{
type: "any",
scripts: BaseScriptSchema[] // any one must satisfy
}Purpose: All contained scripts must be satisfied.
Shape:
{
type: "all",
scripts: BaseScriptSchema[] // all must satisfy
}Purpose: At least N contained scripts must be satisfied.
Shape:
{
type: "atLeast",
required: number, // minimum number of scripts (integer >= 1)
scripts: BaseScriptSchema[] // at least N must satisfy
}Fields:
required: Integer ≥ 1 and ≤ the number of scripts. Specifies the minimum number of scripts that must be satisfied. Validation fails ifrequiredexceeds the length ofscripts.
Purpose: Union of all script types for use inside scripts arrays.
Includes: CommitmentSchema, AfterClauseSchema, BeforeClauseSchema, AnyScriptSchema, AllScriptSchema, AtLeastScriptSchema
Type: Recursive (scripts arrays can contain BaseScriptSchema instances)
Purpose: Top-level schema — accepts all clause types (leaf or composite).
Includes: CommitmentSchema, AfterClauseSchema, BeforeClauseSchema, AnyScriptSchema, AllScriptSchema, AtLeastScriptSchema
{
"type": "any",
"scripts": [
{
"type": "cmt",
"hash": "a1b2c3d4e5f6789012345678abcdef0123456789abcdef0123456789abcdef01"
},
{ "type": "cmt", "hash": "b2c3d4e5f6789012345678abcdef0123456789abcdef0123456789abcdef01" }
]
}{
"type": "atLeast",
"required": 2,
"scripts": [
{ "type": "cmt", "hash": "d4e5f6789012345678abcdef0123456789abcdef0123456789abcdef012345" },
{ "type": "cmt", "hash": "e5f6789012345678abcdef0123456789abcdef0123456789abcdef01234501" },
{ "type": "cmt", "hash": "f6789012345678abcdef0123456789abcdef0123456789abcdef0123456789" }
]
}{
"type": "all",
"scripts": [
{ "type": "after", "block": 1000 },
{ "type": "cmt", "hash": "966e394a544f242081e41d1965137b1bb412ac230d40ed5407821c3700000000" }
]
}{
"type": "any",
"scripts": [
{ "type": "cmt", "hash": "b275b08c999097247f7c17e77007c7010cd19f20cc086ad99d39853800000000" },
{
"type": "all",
"scripts": [
{ "type": "before", "block": 3000 },
{
"type": "cmt",
"hash": "966e394a544f242081e41d1965137b1bb412ac230d40ed5407821c3700000000"
}
]
}
]
}{
"type": "atLeast",
"required": 2,
"scripts": [
{
"type": "all",
"scripts": [
{
"type": "cmt",
"hash": "a1b2c3d4e5f6789012345678abcdef0123456789abcdef0123456789abcdef01"
},
{
"type": "cmt",
"hash": "b2c3d4e5f6789012345678abcdef0123456789abcdef0123456789abcdef01"
}
]
},
{ "type": "cmt", "hash": "c3d4e5f6789012345678abcdef0123456789abcdef0123456789abcdef01" }
]
}After validation with NativeScriptSchema or BaseScriptSchema:
- All
hashfields areUint8Array(32 bytes) scriptsarrays contain nested script objects- Hex strings are transformed via
fromHexfrom@midnight-ntwrk/compact-runtime
- Recursion:
BaseScriptSchemausesz.lazy()to handle circular references inscriptsarrays - Code reuse: Composite schemas (
AnyScriptSchema, etc.) are defined once and reused in bothBaseScriptSchemaandNativeScriptSchema - Discriminated union: Uses
typefield to discriminate between script variants - Input normalization: Hex strings and
Uint8Arrayinputs are normalized toUint8Arrayinstances