This script will be a parameterised minting policy script that allows the minting of tokens if a certain amount of ADA is paid to the store address as part of the minting transaction.
Writing the validator
We can start by creating the TokenSaleParams that will consist of the store address, the token name, and the token price:
dataTokenSaleParams= TokenSaleParams { store :: PlutusV2.Address, -- public address of the token store tName :: PlutusV2.TokenName, -- name of the token to be minted tPrice ::Integer-- price in ADA per token } PlutusTx.unstableMakeIsData ''TokenSaleParamsPlutusTx.makeLift ''TokenSaleParams
The next part is writing the mkPolicy function that will represent our minting logic. We will need quite a bit of helper functions here. First, define the main behaviour of the function which is to use the checkMint function in order to determine whether minting is allowed and write a trace Invalid mint if it is not. Then, we start writing our helper functions for deconstructing info and txOuts from the transaction context.
mkPolicy::TokenSaleParams->BuiltinData-> PlutusV2.ScriptContext->BoolmkPolicy tsp _ ctx = traceIfFalse "Invalid mint" checkMintwhereinfo:: PlutusV2.TxInfo info = PlutusV2.scriptContextTxInfo ctxtxOuts:: [PlutusV2.TxOut] txOuts = PlutusV2.txInfoOutputs info
We want a checkMint function that will look at the minting field of the transaction (PlutusV2.txInfoMint info) and validate only if there is only one particular token being minted, that token matches our CurrencySymbol and TokenName and the amount minted is valid for the amount of ADA being paid to the store address.
We will call this last variable canMintAmount and it will simply divide (using integer division) the amount of ADA paid to the store (storeTxOutAdaValue) by the price of the token. Getting the storeTxOutAdaValue will be slightly complicated. First, we will need to look at all the UTxOs of the transactions that go to the store address via storeTxOuts :: [PlutusV2.TxOut]. That will give us a list of UTxOs that we then have to filter for their values storeTxOutValue :: PlutusV2.Value and then filter that for only the ADA (Lovelace) values and also sum them all together via storeTxOutLovelaceValue :: Integer. We then simply need to turn that into the corresponding ADA value by dividing it by a million.
To use the underscore format for large numbers to make them more readable such as 1_000_000, we have to activate the extension {-# LANGUAGE NumericUnderscores #-}. Here is the full list of extensions and imports used for this validator for reference:
{-# LANGUAGENoImplicitPrelude #-}{-# LANGUAGETemplateHaskell #-}{-# LANGUAGEOverloadedStrings #-}{-# LANGUAGETypeApplications #-}{-# LANGUAGETypeFamilies #-}{-# LANGUAGEScopedTypeVariables #-}{-# LANGUAGEMultiParamTypeClasses #-}{-# LANGUAGEDataKinds #-}{-# LANGUAGENumericUnderscores #-}module MintingPolicy (scriptSerialised,writeSerialisedScript, )whereimportqualified PlutusTximport PlutusTx.Preludeimportqualified Plutus.V2.Ledger.Api as Plutusimport Cardano.Api.Shelley (PlutusScript (PlutusScriptSerialised),PlutusScriptV2,writeFileTextEnvelope)import Cardano.Api (FileError)importqualified Data.ByteString.Lazy as LBSimportqualified Data.ByteString.Short as SBSimport Codec.Serialiseimportqualified Ledger.Typed.Scripts as Scriptsimport Plutus.Script.Utils.V2.Contexts (ownCurrencySymbol)importqualified Plutus.V1.Ledger.Value as PlutusV1import Prelude (IO)
Once our logic is complete, we need to apply our parameter to the function and create a MintingPolicy via PlutusV2.mkMintingPolicyScript. Since we are creating a parameterised script, we need to lift and apply our tsp parameter to our mkPolicy function as we did with our StakingValidator:
To use this MintingPolicy, we need to serialise it as a script first. We can get the Script type via unMintingPolicyScript. We can create and apply the parameter here directly (using the 02.addr as the store address):
Finally, we can load the module in cabal repl from our nix-shell and compile the minting policy.
Prelude>:l src/MintingPolicy.hs[1 of 1] Compiling MintingPolicy ( src/MintingPolicy.hs, /home/plutus/hpm-plutus/hpm-validators/dist-newstyle/build/x86_64-linux/ghc-8.10.7/hpm-validators-0.1.0.0/build/MintingPolicy.o )
Ok, one module loaded.Prelude MintingPolicy> writeSerialisedScriptRight ()
Testing the validator
To create the transactions for testing, we need to first get the policy ID from the policy script. From a new directory under testnet/MintingPolicy/ the command looks like this:
Also, cardano-cli accepts only hex token names, so before we can use it as an argument, we need to hexlify our token name via:
echo-n"HPM"|xxd-ps48504d
Finally, to test this minting policy we can write our usual testing scripts. The check-utxos.sh scripts lists UTxOs available at 01.addr (customer) and 02.addr (store).
We can first test the minting policy by trying to mint an invalid number of tokens for the price. Let's say we want 10 tokens (price 100 ADA), but we only pay 90 ADA in our mint-tokens-invalid.sh script. To create minting transactions, we use the --mint argument which has the following syntax: