TSchema
Type-safe schema definitions for Plutus data structures
TSchema
TSchema wraps Effect Schema with Plutus-specific encoding rules, giving you compile-time type safety and automatic CBOR serialization for smart contract data structures.
Define your schema once, get TypeScript types and CBOR encoding automatically.
Why TSchema?
Without TSchema (manual PlutusData):
- Easy to mismatch types between TypeScript and on-chain
- No compile-time validation
- Manual CBOR encoding prone to errors
- Field order mistakes cause validator failures
With TSchema:
- Types inferred from schema automatically
- Compiler catches type mismatches
- Correct CBOR encoding guaranteed
- Field order enforced by structure
Quick Start
Define a schema, extract types, create codec:
import { } from "@evolution-sdk/evolution"
// Define schema
const = ..({
: ..,
: ..
})
// Extract TypeScript type
type = typeof .
// {
// transaction_id: Uint8Array
// output_index: bigint
// }
// Create codec
const = ..()
// Use it
const : = {
: ..("a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"),
: 0n
}
const = .()
const = .()The Core Schemas
ByteArray
For hashes, policy IDs, addresses, and binary data:
import { } from "@evolution-sdk/evolution"
const = ..
type = typeof .
// Uint8Array
const : = ..(
"a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
)Integer
For amounts, quantities, timestamps, indices:
import { } from "@evolution-sdk/evolution"
const = ..
type = typeof .
// bigint
const : = 5000000nStruct
For records with named fields (encoded as Plutus Constructor):
import { } from "@evolution-sdk/evolution"
const = ..({
: ..,
: ..
})
type = typeof .
// {
// payment_credential: Uint8Array
// stake_credential: Uint8Array
// }
const = ..()
const : = {
: ..("abc123def456abc123def456abc123def456abc123def456abc123de"),
: ..("def456abc123def456abc123def456abc123def456abc123def456ab")
}
const = .()Variant
For sum types (multiple possible constructors):
import { } from "@evolution-sdk/evolution"
const = ..({
: {
: ..
},
: {
: ..
}
})
type = typeof .
// | { VerificationKey: { hash: Uint8Array } }
// | { Script: { hash: Uint8Array } }
const = ..()
// Verification key credential
const : = {
: {
: ..("abc123def456abc123def456abc123def456abc123def456abc123de")
}
}
// Script credential
const : = {
: {
: ..("def456abc123def456abc123def456abc123def456abc123def456ab")
}
}
const = .()
const = .()Array
For lists of values:
import { } from "@evolution-sdk/evolution"
const = ..(..)
type = typeof .
// ReadonlyArray<bigint>
const = ..()
const : = [100n, 200n, 300n]
const = .()Map
For key-value mappings:
import { } from "@evolution-sdk/evolution"
const = ..(
.., // AssetName
.. // Quantity
)
type = typeof .
// Map<Uint8Array, bigint>
const = ..()
const : = new ([
[..("546f6b656e"), 100n], // "Token" in hex
[..("4e4654"), 1n] // "NFT" in hex
])
const = .()UndefinedOr
For optional fields (Maybe pattern):
import { } from "@evolution-sdk/evolution"
const = ..({
: ..,
: ..(..)
})
type = typeof .
// {
// name: Uint8Array
// nickname: Uint8Array | undefined
// }
const = ..()
const : = {
: ..("416c696365"), // "Alice"
:
}
const : = {
: ..("426f62"), // "Bob"
: ..("426f62627920") // "Bobby"
}Creating Codecs
Use Data.withSchema() to create a codec from any schema:
import { } from "@evolution-sdk/evolution"
const = ..({
: ..,
: ..
})
const = ..()
// Codec provides:
// - toData(value) → PlutusData
// - fromData(data) → value
// - toCBORHex(value) → string
// - toCBORBytes(value) → Uint8Array
// - fromCBORHex(hex) → value
// - fromCBORBytes(bytes) → valueReal-World Examples
Payment Credential
import { } from "@evolution-sdk/evolution"
const = ..({
: {
: ..
},
: {
: ..
}
})
export type = typeof .
export const = ..()
// Use in your app
const : = {
: {
: ..("abc123def456abc123def456abc123def456abc123def456abc123de")
}
}
const = .()Escrow Datum
import { } from "@evolution-sdk/evolution"
const = ..({
: ..,
: ..,
: ..
})
export type = typeof .
export const = ..()
// Create datum
const : = {
: ..("abc123def456abc123def456abc123def456abc123def456abc123de"),
: 1735689600000n,
: 10000000n
}
// Encode for transaction
const = .()Multi-Action Redeemer
import { } from "@evolution-sdk/evolution"
const = ..({
: {},
: {},
: {
: ..,
: ..
}
})
export type = typeof .
export const = ..()
// Different actions
const : = { : {} }
const : = { : {} }
const : = {
: {
: ..("def456abc123def456abc123def456abc123def456abc123def456ab"),
: 1735776000000n
}
}
const = .()CIP-68 Metadata Datum
For CIP-68 metadata with arbitrary PlutusData fields, you can build the schema manually or use the pre-built types from the plutus directory. See the Plutus Types documentation for ready-to-use CIP-68 schemas.
Nested Schemas
Schemas compose naturally:
import { } from "@evolution-sdk/evolution"
// Base schemas
const = ..({
: { : .. },
: { : .. }
})
const = ..({
: { : },
: {
: ..,
: ..,
: ..
}
})
// Composed schema
const = ..({
: ,
: ..()
})
export type = typeof .
export const = ..()Type Extraction
Get TypeScript types from any schema:
import { } from "@evolution-sdk/evolution"
const = ..({
: ..,
: ..,
: ..(..)
})
// Extract type
type = typeof .
// {
// id: Uint8Array
// amount: bigint
// metadata: Uint8Array | undefined
// }
// Use in functions
function (: ) {
.("Processing amount:", .)
}Schema vs Direct PlutusData
| Approach | Type Safety | Validation | CBOR Encoding | Use When |
|---|---|---|---|---|
| TSchema | ✅ Compile-time | ✅ Automatic | ✅ Guaranteed correct | Production smart contracts |
| Direct Data | ❌ Runtime only | ❌ Manual | ⚠️ Manual | Prototyping, debugging, tools |
Rule of thumb: Use TSchema for all production smart contract integration.
Best Practices
Define schemas once: Create a schema for each datum/redeemer type and reuse it.
Match validator exactly: Your TSchema definitions must match your Plutus validator types exactly, including field names and order.
Use type extraction: Let TypeScript infer types from schemas—don't duplicate type definitions.
Test round-trips: Always verify encode → decode returns the original value.
Export types and codecs: Make both the type and codec available:
import { } from "@evolution-sdk/evolution"
const = ..({
: ..
})
export type = typeof .
export const = ..()