Skip to Content
Scalus Club is now open! Join us to get an early access to new features 🎉
DocumentationDesign PatternsMerkelized Validator

Merkelized Validator Pattern

Extend the Stake Validator Pattern to allow spending validators to read verified data from the stake validator’s redeemer.

The Challenge

When processing multiple UTxOs in a batch operation:

  • Each spending validator runs independently
  • Expensive computations (e.g., calculating a clearing price) would run N times
  • No built-in way to share verified results between spending validator executions

Even with the Stake Validator pattern, spending validators can only verify that the stake validator ran—they cannot access its computation results.

How It Works

  1. Off-chain code computes expensive values (e.g., clearing price, settlement amounts)
  2. Values are included in the stake validator’s redeemer
  3. Stake validator verifies the values are correct (runs once)
  4. Spending validators read the verified values via MerkelizedValidator (runs per UTxO)

This reduces complexity from O(N²) to O(N) for batch operations while giving each spending validator access to shared, verified data.

When to Use This Pattern

Best for:

  • Batch auctions where all bids need the same clearing price
  • Settlement operations with shared computation results
  • Any scenario where spending validators need verified global state

Not ideal for:

  • Simple validation that doesn’t need shared data (use Stake Validator instead)
  • Single-input transactions
  • When each input needs completely independent validation
PatternUse Case
StakeValidator.spendMinimalOnly need to check stake validator ran
MerkelizedValidator.verifyAndGetRedeemerNeed to read verified data

API Reference

FunctionDescription
getStakeRedeemer(hash, txInfo)Retrieves the stake validator’s redeemer
verifyAndGetRedeemer(hash, txInfo)Verifies withdrawal exists AND returns redeemer

Implementation Guide

Stake Validator Redeemer

Define a redeemer type that carries the verified computation results:

case class AuctionSettlementRedeemer( clearingPrice: BigInt, totalUnitsAvailable: BigInt ) derives ToData, FromData

Spending Validator

Read the verified data using MerkelizedValidator:

import scalus.patterns.MerkelizedValidator @Compile object BatchAuctionValidator extends Validator { inline override def spend( datum: Option[Data], redeemer: Data, tx: TxInfo, ownRef: TxOutRef ): Unit = { val ownScriptHash = tx.findOwnInputOrFail(ownRef).resolved.address.credential .scriptOption.getOrFail("Own address must be Script") // Read verified settlement data from stake validator val stakeRedeemer = MerkelizedValidator.verifyAndGetRedeemer(ownScriptHash, tx) val settlement = stakeRedeemer.to[AuctionSettlementRedeemer] // Use the verified clearing price val bid = datum.getOrFail("Missing datum").to[BidDatum] if bid.bidPrice >= settlement.clearingPrice then // Fill the bid - verify bidder receives tokens else // Refund the bid - verify bidder receives ADA back } }

Stake Validator (Reward Endpoint)

Verify the computation results are correct:

inline override def reward(redeemer: Redeemer, stakingKey: Credential, tx: TxInfo): Unit = { val settlement = redeemer.to[AuctionSettlementRedeemer] // Verify clearing price calculation require(settlement.clearingPrice > BigInt(0), "Clearing price must be positive") // Verify supply/demand balance val totalDemand = calculateTotalDemand(tx, settlement.clearingPrice) require(totalDemand <= settlement.totalUnitsAvailable, "Demand exceeds supply") // ... additional verification logic }

Performance Benefit: When spending N UTxOs with iteration-heavy logic:

  • Without pattern: O(N²) - each spending validator iterates all inputs/outputs
  • With pattern: O(N) - stake validator iterates once, spending validators just read

Run BatchAuctionTest to see actual memory/CPU savings.

Script Configuration: The spending script address must have a staking credential that points to your withdrawal validator. Configure this when deploying scripts.

Example: Batch Auction

See scalus.examples.BatchAuctionValidator for a complete implementation where:

  • Stake validator: Verifies the clearing price calculation once
  • Spending validator: Reads the verified clearing price to determine if each bid is filled or refunded

Resources

Last updated on