Evolution SDK
Smart Contracts

Parameterized Scripts

Apply parameters to Plutus scripts at runtime

Parameterized Scripts

Parameterized scripts are Plutus validators that accept configuration at deployment time. Instead of hardcoding values like an owner's key hash or a deadline into the script, you leave them as parameters and apply them before using the script on-chain.

This is the standard pattern for reusable smart contracts — write one validator, deploy it with different parameters for different use cases.

How It Works

A parameterized Plutus script is compiled with "holes" — lambda abstractions at the top level. When you apply parameters, Evolution SDK:

  1. Decodes the double-CBOR-encoded script to UPLC
  2. Wraps each parameter as a UPLC Constant node
  3. Creates Apply nodes to fill in the lambda parameters
  4. Re-encodes the result back to double-CBOR hex

The result is a fully applied script ready to use in transactions.

Apply with Raw Data

Use UPLC.applyParamsToScript when you have parameters as Data.Data values:

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

// Your compiled parameterized script (double-CBOR hex from Aiken, Plutarch, etc.)
declare const : string

// Apply a key hash and deadline as parameters
const  = .(, [
  .("abc123def456abc123def456abc123def456abc123def456abc123de"), // owner key hash
  .(1735689600000n), // deadline
])

// appliedScript is now a fully applied script ready for transactions

Each parameter is applied in order, matching the lambda bindings in the compiled script.

Apply with Type-Safe Schemas

Use UPLC.applyParamsToScriptWithSchema for type-safe parameter conversion via a toData function:

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

// Define the parameter schema
const  = .({
  : .,
  : .,
})

const  = .()

declare const : string

// Apply with type-safe conversion — each struct field becomes one parameter
const  = .(
  ,
  [
    .({
      : .("abc123def456abc123def456abc123def456abc123def456abc123de"),
      : 1735689600000n,
    }),
  ],
  () => , // Already converted to Data
)

Full Example: Parameterized Vesting Contract

Here's a complete workflow — apply parameters to a vesting script, then lock funds to it:

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

const  = .()
  .({
    : "https://cardano-preprod.blockfrost.io/api/v0",
    : ..!
  })
  .({ : ..!, : 0 })

// 1. Start with the compiled parameterized script
declare const : string

// 2. Apply parameters: beneficiary key hash and unlock deadline
const  = .(, [
  .("abc123def456abc123def456abc123def456abc123def456abc123de"),
  .(1735689600000n),
])

// 3. Use the applied script in a transaction
const  = await 
  .()
  .({
    : .("addr_test1wrm9x2dgvdau8vckj4duc89m638t8djmluqw5pdrFollw8qnmqsyu"),
    : .(50_000_000n),
    : new .({ : .(0n, []) }),
  })
  .()

const  = await .()
await .()

CBOR Encoding Options

By default, applyParamsToScript uses Aiken-compatible encoding (indefinite-length arrays and maps). If your script was compiled with a different tool, you can pass a different CBOR preset:

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

declare const : string

// With default Aiken encoding (indefinite-length arrays/maps)
const  = .(, [
  .(42n),
])

// With CML-compatible encoding (definite-length)
const  = .(
  ,
  [.(42n)],
  .,
)

When to Use Parameterized Scripts

ScenarioApproach
Same logic, different config per deploymentParameterized script
One-off validator with fixed logicNon-parameterized script
Config changes at runtime (per-transaction)Use datum fields instead

Common parameters include:

  • Owner/admin key hashes — Who can perform admin actions
  • Deadlines — Time-based unlock conditions
  • Token policy IDs — Which tokens the contract manages
  • Oracle addresses — External data feed references

Next Steps