imports
{-# LANGUAGE QualifiedDo #-}
{-# LANGUAGE OverloadedRecordDot #-}
module Plutarch.Docs.ValidatorExample (alwaysSucceeds, checkSignatory, res', res, alwaysFails) where
import Plutarch.Prelude
import Plutarch.Api.V1 (PDatum, PRedeemer, PScriptContext)
import Plutarch.Api.V1.Crypto (PPubKeyHash)
import Plutarch.Api.V1.Contexts (PScriptPurpose(PSpending))
import Plutarch.Docs.Run (evalWithArgsT)
import Plutarch.Script (Script)
import qualified PlutusTx
import PlutusCore.Evaluation.Machine.ExBudget (ExBudget)
import qualified Plutarch.Monadic as P
import Data.Text (Text)
Note: If you spot any mistakes/have any related questions that this guide lacks the answer to, please don't hesitate to raise an issue. The goal is to have high quality documentation for Plutarch users!
- Validator that always succeeds
- Validator that always fails
- Validator that checks whether a value is present within signatories
- Using custom datum/redeemer in your Validator
Aside: Be sure to check out Compiling and Running first!
alwaysSucceeds :: Term s (PAsData PDatum :--> PAsData PRedeemer :--> PAsData PScriptContext :--> PUnit)
alwaysSucceeds = plam $ \_datm _redm _ctx -> pconstant ()
All the arguments are ignored.
Execution:
res' :: Either Text (Script, ExBudget, [Text])
res' = alwaysSucceeds `evalWithArgsT` [PlutusTx.toData (), PlutusTx.toData (), PlutusTx.toData ()]
-- >>> res'
-- Right (Program () (Version () 1 0 0) (Constant () (Some (ValueOf unit ()))))
alwaysFails :: Term s (PAsData PDatum :--> PAsData PRedeemer :--> PAsData PScriptContext :--> PUnit)
alwaysFails = plam $ \_datm _redm _ctx -> perror
Similar to the example above.
Execution:
res :: Either Text (Script, ExBudget, [Text])
res = alwaysFails `evalWithArgsT` [PlutusTx.toData (), PlutusTx.toData (), PlutusTx.toData ()]
-- >>> res
-- Left (EvaluationError [] "(CekEvaluationFailure,Nothing)")
checkSignatory :: Term s (PPubKeyHash :--> PAsData PDatum :--> PAsData PRedeemer :--> PAsData PScriptContext :--> PUnit)
checkSignatory = plam $ \ph _ _ ctx' -> P.do
ctx <- pletFields @["txInfo", "purpose"] ctx'
PSpending _ <- pmatch ctx.purpose
let signatories = pfield @"signatories" # ctx.txInfo
pif
(pelem # pdata ph # pfromData signatories)
-- Success!
(pconstant ())
-- Signature not present.
perror
Note: The above snippet uses GHC 9 features (
QualifiedDo
andOverloadedRecordDot
). Be sure to check out Do syntax withTermCont
and alternatives toOverloadedRecordDot
.
We match on the script purpose to see if its actually for spending - and we get the signatories field from txInfo
(the 7th field), check if the given pub key hash is present within the signatories and that's it!
It's important that we pass a PPubKeyHash
prior to treating checkSignatory
as a validator script.
{-# LANGUAGE OverloadedStrings #-}
import Plutus.V1.Ledger.Api
import Plutus.V1.Ledger.Interval
import qualified PlutusTx
hashStr :: PubKeyHash
hashStr = "abce0f123e"
pubKeyHash :: Term s PPubKeyHash
pubKeyHash = pconstant hashStr
mockCtx :: ScriptContext
mockCtx =
ScriptContext
(TxInfo
mempty
mempty
mempty
mempty
mempty
mempty
(interval (POSIXTime 1) (POSIXTime 2))
[fromString hashStr, "f013", "ab45"]
mempty
""
)
(Spending (TxOutRef "" 1))
> evalWithArgsT (checkSignatory # pubKeyHash) [PlutusTx.toData (), PlutusTx.toData (), PlutusTx.toData mockCtx]
Right (Program () (Version () 1 0 0) (Constant () (Some (ValueOf unit ()))))
All you have to do is implement PIsDataRepr
and friends for your custom datum/redeemer and you can use it just like PScriptContext
in your validators!