This guide shows how to produce a CIP-57
blueprint for one or more of your scripts
from off-chain Haskell. A blueprint is built as an ordinary value and written to
a JSON file; the helpers live in PlutusTx.Blueprint.
It assumes you already have a compiled validator or minting policy (a
CompiledCode) — see
The structure of a Plinth smart contract.
Step 1: Give your types schema instances
deriveDefinitions and definitionRef (below) need a blueprint schema for
each datum, redeemer, and parameter type. Derive it alongside the Data
instances with makeIsDataSchemaIndexed (see
The Plinth contract language):
PlutusTx.makeIsDataSchemaIndexed ''AuctionRedeemer [('NewBid, 0), ('Payout, 1)]
PlutusTx.makeIsDataSchemaIndexed ''AuctionParams [('AuctionParams, 0)]
Step 2: Describe the contract
Build the blueprint as a value. It has three parts: a preamble of metadata, a set of validators, and the schema definitions.
The preamble is plain metadata:
myPreamble :: Preamble
myPreamble = MkPreamble
{ preambleTitle = "Auction Validator"
, preambleDescription = Just "Blueprint for a Plutus auction script"
, preambleVersion = "1.0.0"
, preamblePlutusVersion = PlutusV3
, preambleLicense = Just "MIT"
}
Each validator names its arguments and carries the compiled code. The
argument schemas are references (definitionRef @T) into the definitions
registry rather than inline schemas, and the code is the
serialised script wrapped
with compiledValidator, which also computes the hash:
import PlutusLedgerApi.Common (serialiseCompiledCode)
import Data.ByteString.Short qualified as Short
myValidator :: ValidatorBlueprint referencedTypes
myValidator = MkValidatorBlueprint
{ validatorTitle = "Auction Validator"
, validatorDescription = Just "Validates auction transactions"
, validatorParameters =
[ MkParameterBlueprint
{ parameterTitle = Just "Parameters"
, parameterDescription = Just "Compile-time validator parameters"
, parameterPurpose = Set.singleton Spend
, parameterSchema = definitionRef @AuctionParams
}
]
, validatorRedeemer = MkArgumentBlueprint
{ argumentTitle = Just "Redeemer"
, argumentDescription = Just "Redeemer for the auction validator"
, argumentPurpose = Set.fromList [Spend]
, argumentSchema = definitionRef @AuctionRedeemer
}
, validatorDatum = Nothing
, validatorCompiled =
let code = Short.fromShort (serialiseCompiledCode (auctionValidatorScript params))
in Just (compiledValidator PlutusV3 code)
}
Step 3: Assemble the ContractBlueprint
Combine the preamble and validators, and generate the schema registry with
deriveDefinitions over every type the validators reference:
myContractBlueprint :: ContractBlueprint
myContractBlueprint = MkContractBlueprint
{ contractId = Just "auction-validator"
, contractPreamble = myPreamble
, contractValidators = Set.singleton myValidator
, contractDefinitions = deriveDefinitions @[AuctionParams, AuctionDatum, AuctionRedeemer]
}
The definitionRef @T in Step 2 and the type list given to deriveDefinitions
must agree: every type you reference must appear here so its schema is emitted.
Step 4: Write it to a file
writeBlueprint serialises the whole thing to JSON. Wrap it in a small
executable so you can regenerate the blueprint with cabal run:
main :: IO ()
main = do
[path] <- getArgs
writeBlueprint path myContractBlueprint
$ cabal run gen-auction-validator-blueprint -- auction-validator.json
Use encodeBlueprint :: ContractBlueprint -> LBS.ByteString instead if you want
the JSON in memory rather than on disk.
What is derived and what you fill in
A blueprint is part machine-derived, part hand-written:
- Derived from the script: the argument schemas (from your types’
makeIsDataSchemaIndexedinstances, viaderiveDefinitions/definitionRef), the compiled code, and its hash (viacompiledValidator). - Filled in explicitly: all the descriptive metadata — titles, descriptions, version, license, the Plutus version, and each argument’s purpose.
For several scripts in one blueprint, add more ValidatorBlueprint values to
contractValidators (it is a Set) and list all of their types in
deriveDefinitions.
One thing to check: the version passed to compiledValidator and set in
preamblePlutusVersion must match the Plutus
version your script actually targets.
See also
- Contract blueprints (CIP-57) — what a blueprint contains and how it is used.
- The structure of a Plinth smart contract — where blueprint generation sits in the off-chain code.
- The Plinth contract language
— deriving the
Dataand schema instances the blueprint needs.