Install
$ agentstack add skill-cardano-foundation-cardano-dev-skills-write-validator ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Write Cardano Validator
Guide the development of a Cardano smart contract from specification to implementation, with security built in from the start.
When to use
- User wants to write a new Cardano validator, minting policy, or staking script
- User has a specification or requirements for on-chain logic
- User asks "how do I build a contract that does X?"
- User wants to implement a specific DeFi pattern (escrow, DEX, lending, etc.)
When NOT to use
- User wants to review an existing contract (use review-contract)
- User wants to optimize an existing contract (use optimize-validator)
- User wants to build off-chain/transaction building code only
- User wants infrastructure or node setup help
Key principles
- Security first: Design for security from the start. Every design decision should consider the vulnerability checklist.
- Minimal on-chain logic: Keep validators simple. Complex logic increases attack surface and execution costs. Move complexity off-chain where possible.
- Explicit over implicit: Every validator check should be explicit and readable. Avoid clever tricks that obscure intent.
- Test-driven design: Define expected behavior before writing code. Think about which transactions should succeed and which should fail.
- eUTxO-native design: Design for the eUTxO model, not account-based patterns. Think in terms of UTxOs, datums, and transaction shapes.
Workflow
Step 1: Define the datum type
The datum represents the state stored at the script address.
- List every piece of information the validator needs for decisions
- Separate mutable fields (change between transactions) from immutable fields (set once)
- Keep datum small; avoid unbounded collections (vulnerability #3 in checklist)
- Include authentication data: owner key hash, protocol NFT policy ID
- Consider versioning if the protocol may need upgrades
Questions to answer:
- What state needs to persist between transactions?
- Who is authorized to perform each action?
- Are there time-based conditions? Store deadlines in the datum.
- Does the protocol need a state machine? Define the state enum.
See references/datum-redeemer-design.md for detailed guidance, and the eUTxO Design Decisions section of references/aiken-patterns.md for how to choose between parameter, datum, and redeemer — the central design axis.
Step 2: Search Bundled Documentation
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/aiken-examples/- Aiken example projects${CLAUDE_SKILL_DIR}/../../docs/sources/aiken-design-patterns/- Aiken design patterns${CLAUDE_SKILL_DIR}/../../docs/sources/plutus/- Plutus docs
Step 3: Define the redeemer type
The redeemer represents the action the user wants to perform.
- Use an enum:
type Redeemer { Action1 | Action2 { field: Type } } - Keep redeemer data minimal -- use it as a hint, verify independently
- Common patterns: Create, Update, Delete, Claim, Cancel, Admin
For each redeemer variant, define:
- Who can submit it (signer requirements)
- What time constraints apply
- What value changes are allowed
- What datum transitions are valid
- What outputs must be produced
Step 4: Write the validator logic
Structure the validator clearly:
validator my_validator(params: Params) {
spend(
datum: Option,
redeemer: Redeemer,
own_ref: OutputReference,
tx: Transaction,
) {
expect Some(datum) = datum
when redeemer is {
Action1 -> handle_action1(datum, tx)
Action2 { field } -> handle_action2(datum, field, tx)
}
}
}
For each action handler:
- Check authorization (signer, NFT, etc.)
- Check time constraints
- Find and validate outputs (by criteria, never by index)
- Check value preservation
- Check datum transitions (immutable fields unchanged, valid state change)
- Return True only if all checks pass
See references/aiken-patterns.md for worked patterns (vesting, marketplace, multisig, one-shot minting, withdraw-zero, state machine) and references/advanced-patterns.md for when a straightforward validator won't fit (UTxO indexers, merkelized validators, linked lists).
Step 5: Security checklist
Before considering the validator complete, verify each item:
- [ ] Double satisfaction: Inputs are uniquely identified (NFT or unique datum field)
- [ ] Datum hijacking: Only authenticated datums are trusted
- [ ] Value preservation: Output values match expected amounts
- [ ] Signer checks: Every restricted action checks
extra_signatories - [ ] Staking credentials: Output addresses include correct staking part
- [ ] Output lookup: Outputs found by criteria, not by index
- [ ] Time handling: Validity range bounds checked correctly
- [ ] Datum transitions: Only allowed fields change per action
- [ ] Minting controls: If minting, quantity and conditions are constrained
- [ ] State token: Protocol NFT preserved in exactly one output
Step 6: Plan off-chain interaction
For each redeemer action, document the transaction shape:
- Required inputs (script inputs, fee inputs, reference inputs)
- Required outputs (script output with datum, change, recipient)
- Required signers
- Validity range constraints
- Minting/burning (if any)
- Metadata (if any)
This becomes the specification for the off-chain transaction building code.
Step 7: Write test cases
Define tests for each redeemer action:
Positive tests (should succeed):
- Normal execution of each action with valid parameters
- Edge cases with minimum/maximum valid values
Negative tests (should fail):
- Wrong signer attempts each restricted action
- Wrong time (before/after deadline)
- Insufficient value in output
- Invalid datum transition (wrong fields changed)
- Missing authentication token
- Double satisfaction attempt (two script inputs, one output)
Use Aiken's built-in test framework. Call the validator handler directly with a constructed transaction (transaction.placeholder + record-update overrides), and use the boolean-toggle methodology: one success test with all conditions valid, then one test ... fail per validation condition, each flipping exactly one condition — so every guard is individually proven to guard. See references/testing-validators.md for the full manual-vs-mocktail guidance, the mocktail builder API, and the include: Bool pass/fail-matrix idiom.
fn tx_with(signers, range) -> Transaction {
Transaction { ..transaction.placeholder, extra_signatories: signers, validity_range: range }
}
test claim_succeeds() {
vesting.spend(Some(datum), Claim, oref, tx_with([beneficiary], interval.entirely_after(deadline)))
}
test claim_before_deadline_fails() fail {
vesting.spend(Some(datum), Claim, oref, tx_with([beneficiary], interval.entirely_before(deadline)))
}
Plutus/Haskell notes:
- Use
plutus-simple-modelorcardano-testnetfor testing - Use
PlutusTx.compilefor on-chain compilation - Define datum/redeemer with
PlutusTx.unstableMakeIsData
OpShin notes:
- Use Python dataclasses for datum/redeemer types
- Use
build()function fromopshin.builder(e.g.,from opshin.builder import build; contract = build("path/to/contract.py")) - Use
opshin evalfor local testing
References
references/aiken-patterns.md-- Validator patterns with code structure, plus eUTxO design decisionsreferences/advanced-patterns.md-- Advanced/scaling patterns (UTxO indexers, merkelized validators, linked lists)references/datum-redeemer-design.md-- Designing datums and redeemersreferences/testing-validators.md-- How to test validators (manual placeholder vs mocktail)- Search
${CLAUDE_SKILL_DIR}/../../docs/sources/for existing protocol specifications and design documents - Aiken language docs: https://aiken-lang.org
- Plutus docs: https://plutus.cardano.intersectmbo.org
- OpShin docs: https://opshin.dev
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: cardano-foundation
- Source: cardano-foundation/cardano-dev-skills
- License: Apache-2.0
- Homepage: https://cardano-foundation.github.io/cardano-dev-skills/
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.