How to export scripts, datums and redeemers

Note

This guide uses the scripts from the basic validators tutorial.

Since scripts must match their on-chain hashes exactly, it is important that the scripts which an application uses do not accidentally change. For example, changing the source code or updating dependencies or tooling may lead to small changes in the script - but since the hashes must match exactly, even small changes can be problematic.

For this reason, once you expect that you will not modify the on-chain part of your application more, it is sensible to freeze it by saving the final Plutus Core to a file.

Additionally, while most Plutus Application s use scripts by directly submitting them as part of transactions from the application itself, it can be useful to be able to export a serialized script. For example, you might want to submit it as part of a manually created transaction with the Cardano node CLI, or send it to another party for them to use.

Fortunately, it is quite simple to do this. Most of the types have typeclass instances for Serialise which allows translating directly into CBOR. This applies to Validator, Redeemer, and Datum types. If you want to create values that you can pass to the Cardano CLI, you will need to convert them to the appropriate types from cardano-api and use serialiseToTextEnvelope.

-- We can serialize a 'Validator's, 'Datum's, and 'Redeemer's directly to CBOR
serializedDateValidator :: BSL.ByteString
serializedDateValidator = serialise dateValidator
serializedDate :: Date -> BSL.ByteString
serializedDate d = serialise (Datum $ toBuiltinData d)
serializedEndDate :: EndDate -> BSL.ByteString
serializedEndDate d = serialise (Redeemer $ toBuiltinData d)

-- The serialized forms can be written or read using normal Haskell IO functionality.
showSerialised :: IO ()
showSerialised = do
  print serializedDateValidator
  print $ serializedDate (Date 0)
  print $ serializedEndDate Never

CompiledCode has a different serialization method, Flat, but the principle is the same.

The serialized form of CompiledCode can also be dumped using a plugin option:

{-# OPTIONS_GHC -fplugin-opt PlutusTx.Plugin:dump-uplc #-}

This will dump the output to a temporary file with a name based on the module name. The filename will be printed to the console when compiling the source file. You can then move it to a more permanent location.

It can be read in conveniently with loadFromFile as an alternative to compile.

-- We can serialize 'CompiledCode' also
serializedCompiledCode :: BS.ByteString
serializedCompiledCode = Flat.flat $ $$(compile [|| validateDateTyped ||])

-- The 'loadFromFile' function is a drop-in replacement for 'compile', but
-- takes the file path instead of the code to compile.
validatorCodeFromFile :: CompiledCode (() -> () -> ScriptContext -> Bool)
validatorCodeFromFile = $$(loadFromFile "plutus/howtos/myscript.uplc")