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:
- Decodes the double-CBOR-encoded script to UPLC
- Wraps each parameter as a UPLC
Constantnode - Creates
Applynodes to fill in the lambda parameters - 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 transactionsEach 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
| Scenario | Approach |
|---|---|
| Same logic, different config per deployment | Parameterized script |
| One-off validator with fixed logic | Non-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
- Locking to Script — Lock funds to your parameterized script
- Spending from Script — Spend with redeemers
- Datums — Attach state to script outputs
- TSchema — Type-safe data encoding