From cardano-dev-skills
Optimizes Aiken validators for lower execution costs and smaller script size. Covers CPU/memory reduction, data structures, and benchmarking.
How this skill is triggered — by the user, by Claude, or both
Slash command
/cardano-dev-skills:optimize-validatorThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
<!-- Documentation lookup path: ${CLAUDE_SKILL_DIR}/../../docs/sources/ -->
Guide optimization of Aiken validators for lower execution costs (CPU/memory) and smaller compiled script size. Optimization should only be applied to validators that are already correct and tested.
aiken build to check script size and aiken bench to measure execution units. Without baseline numbers, you cannot know if changes helped.Read the validator code and gather current metrics.
Suggest the user run:
# Build and check script sizes
aiken build
# Run benchmarks if available
aiken bench
# Check compiled script sizes
cat plutus.json | jq '.validators[] | {title, size: (.compiledCode | length / 2)}'
Note the following:
plutus.json or build output)aiken bench)Search the bundled documentation for relevant content:
${CLAUDE_SKILL_DIR}/../../docs/sources/aiken/ - Aiken language docs${CLAUDE_SKILL_DIR}/../../docs/sources/aiken-stdlib/ - Aiken standard library docs${CLAUDE_SKILL_DIR}/../../docs/sources/plutus/ - Plutus docsSearch the validator code for known cost centers:
High CPU cost:
assets.greater_or_equal) -- Values are nested mapsextra_signatories instead)High memory cost:
Script size bloat:
Order pattern matching by frequency:
// Most common action first -- UPLC checks branches sequentially
when redeemer is {
Swap -> check_swap(...) // 90% of transactions
AddLiquidity -> check_add(...) // 8%
RemoveLiquidity -> check_remove(...) // 1.5%
UpdateParams -> check_update(...) // 0.5%
}
Fail fast -- check cheap conditions before expensive ones:
// GOOD: Cheap signer check (O(small n)) before expensive output scan (O(large n))
list.has(tx.extra_signatories, datum.owner) &&
check_outputs(tx.outputs, expected_value)
Extract common sub-expressions:
// BAD: Resolves own input twice
list.any(tx.outputs, fn(o) { o.address == resolve_own_address(tx, own_ref) }) &&
list.all(tx.outputs, fn(o) {
o.address != resolve_own_address(tx, own_ref) || check_value(o)
})
// GOOD: Compute once, reuse
expect Some(own_input) = transaction.find_input(tx.inputs, own_ref)
let own_addr = own_input.output.address
list.any(tx.outputs, fn(o) { o.address == own_addr }) &&
list.all(tx.outputs, fn(o) { o.address != own_addr || check_value(o) })
Reduce list traversals:
// BAD: Two separate passes over outputs
let has_script_output = list.any(tx.outputs, fn(o) { o.address == script_addr })
let has_payment = list.any(tx.outputs, fn(o) { o.address == seller })
// GOOD: Single pass with combined check
let (has_script, has_pay) =
list.foldl(tx.outputs, (False, False), fn(o, acc) {
let (s, p) = acc
(s || o.address == script_addr, p || o.address == seller)
})
Use expect instead of when for single-variant destructuring:
// LARGER (compiled): when with explicit fail
when datum is {
MyDatum { owner, amount } -> use(owner, amount)
_ -> fail
}
// SMALLER: expect compiles to direct destructure
expect MyDatum { owner, amount } = datum
use(owner, amount)
Remove traces for production builds:
# Development (with traces for debugging)
aiken build
# Production (traces removed, 10-30% smaller)
aiken build --trace-level silent
Extract shared helper functions:
// BAD: Duplicated logic in each branch
when redeemer is {
Claim -> {
expect Some(out) = list.find(tx.outputs, fn(o) { o.address == addr })
value.lovelace_of(out.value) >= amount && list.has(tx.extra_signatories, owner)
}
Update -> {
expect Some(out) = list.find(tx.outputs, fn(o) { o.address == addr })
value.lovelace_of(out.value) >= amount && list.has(tx.extra_signatories, owner)
}
}
// GOOD: Shared function
fn check_output_and_signer(tx, addr, amount, signer) {
expect Some(out) = list.find(tx.outputs, fn(o) { o.address == addr })
value.lovelace_of(out.value) >= amount && list.has(tx.extra_signatories, signer)
}
Remove dead code and unused imports: Search for functions, types, and imports that are not referenced. Unused code still contributes to script size.
Pair<a, b> instead of 2-element tuples when possible (smaller UPLC representation)For scripts over approximately 4 KB that will be used in multiple transactions:
After applying optimizations, the user should verify:
aiken checkaiken bench and compareaiken bench and compareProvide before/after comparison format:
Before After Delta
cpu: 2,345,678 1,890,123 -19.4%
mem: 234,567 198,432 -15.4%
size: 4,567 3,890 -14.8%
If any optimization involves a trade-off (e.g., increased size for lower CPU), document:
references/uplc-cost-model.md -- UPLC cost model basics, operation costs, and budget limits${CLAUDE_SKILL_DIR}/../../docs/sources/ for benchmark results and performance requirementsaiken build --trace-level silent for production buildsaiken bench for execution unit measurementsnpx claudepluginhub cardano-foundation/cardano-dev-skills --plugin cardano-dev-skillsProvides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.