Writing basic minting policies
Minting policy scripts are the programs that can be used to control the minting of new assets on the chain. Minting policy scripts are much like validator scripts, and they are written similarly, so check out the basic validators tutorial before reading this one.
Minting policy arguments
Minting policies, like validators, receive some information from the validating node:
The redeemer, which is some script-specific data specified by the party performing the minting.
The script context, which contains a representation of the spending transaction, as well as the hash of the minting policy which is currently being run.
The minting policy is a function which receives these two inputs as arguments.
The validating node is responsible for passing them in and running the minting policy.
As with validator scripts, the arguments are passed encoded as PlutusCore.Data.Data
.
Plutus script context versions
Minting policies have access to the script context as their second argument.
Each version of Plutus minting policy scripts are differentiated only by their ScriptContext
argument.
See this example from the file
MustSpendScriptOutput.hs
(lines 340 to 422) showing code addressing Versioned Policies for both Plutus V1 and Plutus V2.
Minting policies tend to be particularly interested in the mint
field, since the point of a minting policy is to control which tokens are minted.
It is also important for a minting policy to look at the tokens in the mint
field that use its own currency symbol i.e. policy hash.
Note that checking only a specific token name is usually not correct.
The minting policy must check for correct minting (or lack there of) of all token names under its currency symbol.
This requires the policy to refer to its own hash — fortunately this is provided for us in the script context of a minting policy.
Writing minting policies
Here is an example that puts this together to make a simple policy that allows anyone to mint the token so long as they do it one token at a time. To begin with, we’ll write a version that works with structured types.
oneAtATimePolicy :: () -> ScriptContext -> Bool
oneAtATimePolicy _ ctx =
-- 'ownCurrencySymbol' lets us get our own hash (= currency symbol)
-- from the context
let ownSymbol = ownCurrencySymbol ctx
txinfo = scriptContextTxInfo ctx
minted = txInfoMint txinfo
-- Here we're looking at some specific token name, which we
-- will assume we've got from elsewhere for now.
in currencyValueOf minted ownSymbol == singleton ownSymbol tname 1
{-# INLINABLE currencyValueOf #-}
-- | Get the quantities of just the given 'CurrencySymbol' in the 'Value'.
currencyValueOf :: Value -> CurrencySymbol -> Value
currencyValueOf (Value m) c = case Map.lookup c m of
Nothing -> mempty
Just t -> Value (Map.singleton c t)
However, scripts are actually given their arguments as type Data
, and must signal failure with error
, so we need to wrap up our typed version to use it on-chain.
-- The 'plutus-ledger' package from 'plutus-apps' provides helper functions to automate
-- some of this boilerplate.
oneAtATimePolicyUntyped :: BuiltinData -> BuiltinData -> ()
-- 'check' fails with 'error' if the argument is not 'True'.
oneAtATimePolicyUntyped r c =
check $ oneAtATimePolicy (unsafeFromBuiltinData r) (unsafeFromBuiltinData c)
-- We can use 'compile' to turn a minting policy into a compiled Plutus Core program,
-- just as for validator scripts.
oneAtATimeCompiled :: CompiledCode (BuiltinData -> BuiltinData -> ())
oneAtATimeCompiled = $$(compile [|| oneAtATimePolicyUntyped ||])
Other policy examples
Probably the simplest useful policy is one that requires a specific key to have signed the transaction in order to do any minting. This gives the key holder total control over the supply, but this is often sufficient for asset types where there is a centralized authority.
singleSignerPolicy :: () -> ScriptContext -> Bool
singleSignerPolicy _ ctx = txSignedBy (scriptContextTxInfo ctx) key
Note
We don’t need to check that this transaction actually mints any of our asset type: the ledger rules ensure that the minting policy will only be run if some of that asset is being minted.