Skip to main content

PlutusData

PlutusData is the serialization format for all on-chain data in Cardano smart contracts. Every datum attached to a UTxO, every redeemer that unlocks a script, and every parameter passed to a validator must be encoded as PlutusData.

The Evolution SDK's Data module gives you type-safe PlutusData creation without touching raw CBOR bytes.

The Five Types

PlutusData consists of five primitive types:

TypeTypeScriptUse For
IntegerbigintAmounts, indices, timestamps, quantities
ByteArrayUint8ArrayHashes, addresses, policy IDs, asset names
Constructor{ index: bigint, fields: Data[] }Variants, tagged unions, structured data
MapMap<Data, Data>Metadata, key-value stores
ListReadonlyArray<Data>Arrays of values

Quick Start

import { Bytes, Data, Text } from "@evolution-sdk/evolution"

// Integer (bigint)
const lovelaceAmount: Data.Data = 5000000n

// ByteArray (Uint8Array)
const tokenName = Text.toBytes("HOSKY")
const policyId = Bytes.fromHex("a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8")

// Constructor (variant with fields)
const unlockAction = Data.constr(0n, [])

// Map (key-value pairs)
const metadata = Data.map([
[Text.toBytes("name"), Text.toBytes("My NFT")],
[Text.toBytes("image"), Text.toBytes("ipfs://Qm...")]
])

// List (array)
const quantities: Data.Data = [100n, 200n, 300n]

Integers

Use bigint directly—no wrapper needed:

import { Data } from "@evolution-sdk/evolution"

const fee: Data.Data = 170000n
const deposit: Data.Data = 2000000n
const nftQuantity: Data.Data = 1n
const ftQuantity: Data.Data = 1000000n
const delta: Data.Data = -500n
const totalSupply: Data.Data = 45000000000000000n

Byte Arrays

import { Bytes, Data, Text } from "@evolution-sdk/evolution"

const txHash = Bytes.fromHex(
"a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
)

const policyId = Bytes.fromHex(
"1234567890abcdef1234567890abcdef1234567890abcdef12345678"
)

const assetName = Text.toBytes("MyToken")
const adaPolicyId = new Uint8Array()

When to use Bytes.fromHex vs Text.toBytes:

  • Bytes.fromHex: For hashes, policy IDs, credential hashes (hexadecimal data)
  • Text.toBytes: For asset names, metadata values (human-readable strings)

Constructors

import { Bytes, Data } from "@evolution-sdk/evolution"

// Simple variant (no data)
const claimAction = Data.constr(0n, [])
const cancelAction = Data.constr(1n, [])

// Variant with single field
const verificationKeyCred = Data.constr(0n, [
Bytes.fromHex("abc123def456abc123def456abc123def456abc123def456abc123de")
])

const scriptCred = Data.constr(1n, [
Bytes.fromHex("def456abc123def456abc123def456abc123def456abc123def456ab")
])

// Multiple fields
const outputRef = Data.constr(0n, [
Bytes.fromHex("a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"),
2n
])

Maps

import { Bytes, Data, Text } from "@evolution-sdk/evolution"

const nftMetadata = Data.map([
[Text.toBytes("name"), Text.toBytes("CryptoKitty #1234")],
[Text.toBytes("image"), Text.toBytes("ipfs://QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco")],
[Text.toBytes("description"), Text.toBytes("A rare cryptokitty with rainbow fur")]
])

const tokenMetadata = Data.map([
[Text.toBytes("name"), Text.toBytes("MyToken")],
[Text.toBytes("ticker"), Text.toBytes("MTK")],
[Text.toBytes("decimals"), 6n],
[Text.toBytes("properties"), Data.map([
[Text.toBytes("mintable"), 1n],
[Text.toBytes("burnable"), 1n]
])]
])

Lists

import { Bytes, Data } from "@evolution-sdk/evolution"

const prices: Data.Data = [100n, 250n, 500n, 1000n]

const approvedSigners: Data.Data = [
Bytes.fromHex("abc123def456abc123def456abc123def456abc123def456abc123de"),
Bytes.fromHex("def456abc123def456abc123def456abc123def456abc123def456ab"),
Bytes.fromHex("123456789abc123456789abc123456789abc123456789abc12345678")
]

const actions: Data.Data = [
Data.constr(0n, []),
Data.constr(1n, []),
Data.constr(2n, [5000000n])
]

CBOR Encoding

import { Bytes, Data, Text } from "@evolution-sdk/evolution"

const datum = Data.constr(0n, [
Data.map([
[Text.toBytes("beneficiary"), Text.toBytes("addr1...")],
[Text.toBytes("deadline"), 1735689600000n]
]),
5000000n,
1n
])

// Encode to hex string
const cborHex = Data.toCBORHex(datum)

// Encode to bytes
const cborBytes = Data.toCBORBytes(datum)

// Decode from CBOR
const decoded = Data.fromCBORHex(cborHex)

Equality Comparison

import { Data, Text } from "@evolution-sdk/evolution"

const map1 = Data.map([
[Text.toBytes("name"), Text.toBytes("Alice")],
[Text.toBytes("age"), 30n]
])

const map2 = Data.map([
[Text.toBytes("name"), Text.toBytes("Alice")],
[Text.toBytes("age"), 30n]
])

const isEqual = Data.equals(map1, map2)
// true

Real-World Examples

Escrow Datum

import { Bytes, Data } from "@evolution-sdk/evolution"

const escrowDatum = Data.constr(0n, [
Bytes.fromHex("abc123def456abc123def456abc123def456abc123def456abc123de"),
1735689600000n,
10000000n
])

const cborHex = Data.toCBORHex(escrowDatum)

CIP-68 NFT Metadata

import { Data, Text } from "@evolution-sdk/evolution"

const metadata = Data.map([
[Text.toBytes("name"), Text.toBytes("SpaceAce #4242")],
[Text.toBytes("image"), Text.toBytes("ipfs://QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco")],
[Text.toBytes("rarity"), Text.toBytes("legendary")],
[Text.toBytes("attributes"), Data.map([
[Text.toBytes("class"), Text.toBytes("explorer")],
[Text.toBytes("power"), 9000n]
])]
])

const cip68Datum = Data.constr(0n, [
metadata,
1n,
[]
])

Redeemer with Multiple Actions

import { Data } from "@evolution-sdk/evolution"

const claim = Data.constr(0n, [])
const cancel = Data.constr(1n, [])
const update = Data.constr(2n, [
1735776000000n
])

const redeemer = claim

Multi-Sig Validator Redeemer

import { Bytes, Data } from "@evolution-sdk/evolution"

const multiSigRedeemer = Data.constr(0n, [
[
Bytes.fromHex("abc123def456abc123def456abc123def456abc123def456abc123de"),
Bytes.fromHex("def456abc123def456abc123def456abc123def456abc123def456ab")
] as Data.Data,
2n
])

When to Use PlutusData Directly

Use the Data module directly when:

  • Quick prototyping or testing
  • Working with dynamic data structures
  • Debugging CBOR encoding issues
  • Building tooling or explorers

For production smart contract integration, use TSchema for type-safe schema definitions with automatic validation.

Next Steps

  • TSchema — Type-safe schema definitions with automatic validation
  • Plutus Types — Pre-built types for addresses, credentials, values, and CIP-68
  • CBOR — Low-level CBOR encoding and decoding