agora/agora/Agora/Utils.hs
Seungheon Oh 55defea912
Use Liqwid-Labs/plutarch
- Removed `Utils.Value` -- not being used/better is provided with
  liqwid-plutarch-extra
- uses `Liqwid-Labs/plutarch`
- uses `Liqwid-Labs/plutarch-numeric`
- uses `Liqwid-Labs/plutarch-safemoney`
- uses `Liqwid-Labs/liqwid-plutarch-extra`
2022-05-27 13:43:55 -05:00

692 lines
23 KiB
Haskell

{- |
Module : Agora.Utils
Maintainer : emi@haskell.fyi
Description: Plutarch utility functions that should be upstreamed or don't belong anywhere else.
Plutarch utility functions that should be upstreamed or don't belong anywhere else.
-}
module Agora.Utils (
-- * TermCont-based combinators. Some of these will live in plutarch eventually.
tcassert,
tclet,
tcmatch,
tctryFrom,
-- * Validator-level utility functions
pfind',
pfindDatum,
ptryFindDatum,
pvalueSpent,
ptxSignedBy,
paddValue,
plookup,
pfromMaybe,
psymbolValueOf,
pgeqByClass,
pgeqBySymbol,
pgeqByClass',
pfindTxInByTxOutRef,
psingletonValue,
pfindMap,
pnotNull,
pisJust,
ptokenSpent,
pkeysEqual,
pnubSortBy,
pisUniq,
pisUniqBy,
pisDJust,
pisUTXOSpent,
pmsortBy,
pmsort,
pnubSort,
pupdate,
pmapMap,
pmapMaybe,
-- * Functions which should (probably) not be upstreamed
findTxOutByTxOutRef,
scriptHashFromAddress,
findOutputsToAddress,
findTxOutDatum,
validatorHashToTokenName,
pvalidatorHashToTokenName,
getMintingPolicySymbol,
hasOnlyOneTokenOfCurrencySymbol,
mustFindDatum',
mustBePJust,
mustBePDJust,
validatorHashToAddress,
pmergeBy,
phalve,
isScriptAddress,
isPubKey,
) where
--------------------------------------------------------------------------------
import Plutus.V1.Ledger.Api (
Address (..),
Credential (..),
CurrencySymbol,
TokenName (..),
ValidatorHash (..),
)
import Plutus.V1.Ledger.Value (AssetClass (..))
--------------------------------------------------------------------------------
import Plutarch.Api.V1 (
PAddress,
PCredential (PScriptCredential),
PCurrencySymbol,
PDatum,
PDatumHash,
PMap,
PMaybeData (PDJust),
PMintingPolicy,
PPubKeyHash,
PTokenName (PTokenName),
PTuple,
PTxInInfo (PTxInInfo),
PTxOut (PTxOut),
PTxOutRef,
PValidatorHash,
PValue,
mintingPolicySymbol,
mkMintingPolicy,
)
import Plutarch.Api.V1.AssocMap (PMap (PMap))
import Plutarch.Api.V1.AssetClass (PAssetClass, passetClassValueOf, pvalueOf)
import "plutarch" Plutarch.Api.V1.Value (PValue (PValue))
import Plutarch.Builtin (pforgetData, ppairDataBuiltin)
import Plutarch.Reducible (Reducible (Reduce))
import Plutarch.TryFrom (PTryFrom (PTryFromExcess))
import Plutarch.Extra.Map (pkeys)
--------------------------------------------------------------------------------
-- TermCont-based combinators. Some of these will live in plutarch eventually.
-- | Assert a particular 'PBool', trace if false.
tcassert :: forall r (s :: S). Term s PString -> Term s PBool -> TermCont @r s ()
tcassert errorMessage check = tcont $ \k -> pif check (k ()) (ptraceError errorMessage)
-- | 'plet' but for use in 'TermCont'.
tclet :: forall r (s :: S) (a :: PType). Term s a -> TermCont @r s (Term s a)
tclet = tcont . plet
-- | 'pmatch' but for use in 'TermCont'.
tcmatch :: forall (a :: PType) (s :: S). PlutusType a => Term s a -> TermCont s (a s)
tcmatch = tcont . pmatch
-- | 'ptryFrom' but for use in 'TermCont'.
tctryFrom :: forall b a s r. PTryFrom a b => Term s a -> TermCont @r s (Term s b, Reduce (PTryFromExcess a b s))
tctryFrom = tcont . ptryFrom
-- | Escape with a particular value on expecting 'Just'. For use in monadic context.
tcexpectJust ::
forall r (a :: PType) (s :: S).
Term s r ->
Term s (PMaybe a) ->
TermCont @r s (Term s a)
tcexpectJust escape ma = tcont $ \f ->
pmatch ma $ \case
PJust v -> f v
PNothing -> escape
--------------------------------------------------------------------------------
-- Validator-level utility functions
-- | Find a datum with the given hash.
pfindDatum :: Term s (PDatumHash :--> PBuiltinList (PAsData (PTuple PDatumHash PDatum)) :--> PMaybe PDatum)
pfindDatum = phoistAcyclic $
plam $ \datumHash datums -> plookupTuple # datumHash # datums
-- | Find a datum with the given hash, and `ptryFrom` it.
ptryFindDatum :: forall (a :: PType) (s :: S). PTryFrom PData a => Term s (PDatumHash :--> PBuiltinList (PAsData (PTuple PDatumHash PDatum)) :--> PMaybe a)
ptryFindDatum = phoistAcyclic $
plam $ \datumHash inputs ->
pmatch (pfindDatum # datumHash # inputs) $ \case
PNothing -> pcon PNothing
PJust datum -> unTermCont $ do
(datum', _) <- tctryFrom (pto datum)
pure $ pcon (PJust datum')
-- | Check if a PubKeyHash signs this transaction.
ptxSignedBy :: Term s (PBuiltinList (PAsData PPubKeyHash) :--> PAsData PPubKeyHash :--> PBool)
ptxSignedBy = phoistAcyclic $
plam $ \sigs sig -> pelem # sig # sigs
-- | Get the first element that matches a predicate or return Nothing.
pfind' ::
PIsListLike list a =>
(Term s a -> Term s PBool) ->
Term s (list a :--> PMaybe a)
pfind' p =
precList
(\self x xs -> pif (p x) (pcon (PJust x)) (self # xs))
(const $ pcon PNothing)
-- | Get the first element that maps to a PJust in a list.
pfindMap ::
PIsListLike list a =>
Term s ((a :--> PMaybe b) :--> list a :--> PMaybe b)
pfindMap =
phoistAcyclic $
plam $ \p ->
precList
( \self x xs ->
-- In the future, this should use `pmatchSum`, I believe?
pmatch (p # x) $ \case
PNothing -> self # xs
PJust v -> pcon (PJust v)
)
(const $ pcon PNothing)
-- | Find the value for a given key in an associative list.
plookup ::
(PEq a, PIsListLike list (PBuiltinPair a b)) =>
Term s (a :--> list (PBuiltinPair a b) :--> PMaybe b)
plookup =
phoistAcyclic $
plam $ \k xs ->
pmatch (pfind' (\p -> pfstBuiltin # p #== k) # xs) $ \case
PNothing -> pcon PNothing
PJust p -> pcon (PJust (psndBuiltin # p))
-- | Find the value for a given key in an assoclist which uses 'PTuple's.
plookupTuple ::
(PEq a, PIsListLike list (PAsData (PTuple a b)), PIsData a, PIsData b) =>
Term s (a :--> list (PAsData (PTuple a b)) :--> PMaybe b)
plookupTuple =
phoistAcyclic $
plam $ \k xs ->
pmatch (pfind' (\p -> (pfield @"_0" # pfromData p) #== k) # xs) $ \case
PNothing -> pcon PNothing
PJust p -> pcon (PJust (pfield @"_1" # pfromData p))
-- | Extract a Maybe by providing a default value in case of Just.
pfromMaybe :: forall a s. Term s (a :--> PMaybe a :--> a)
pfromMaybe = phoistAcyclic $
plam $ \e a ->
pmatch a $ \case
PJust a' -> a'
PNothing -> e
-- | Yield True if a given PMaybe is of form @'PJust' _@.
pisJust :: forall a s. Term s (PMaybe a :--> PBool)
pisJust = phoistAcyclic $
plam $ \v' ->
pmatch v' $ \case
PJust _ -> pconstant True
PNothing -> pconstant False
-- | Get the sum of all values belonging to a particular CurrencySymbol.
psymbolValueOf :: Term s (PCurrencySymbol :--> PValue :--> PInteger)
psymbolValueOf =
phoistAcyclic $
plam $ \sym value'' -> unTermCont $ do
PValue value' <- tcmatch value''
PMap value <- tcmatch value'
m' <- tcexpectJust 0 (plookup # pdata sym # value)
PMap m <- tcmatch (pfromData m')
pure $ pfoldr # plam (\x v -> pfromData (psndBuiltin # x) + v) # 0 # m
-- | Extract amount from PValue belonging to a Haskell-level AssetClass.
passetClassValueOf' :: AssetClass -> Term s (PValue :--> PInteger)
passetClassValueOf' (AssetClass (sym, token)) =
phoistAcyclic $ plam $ \value -> pvalueOf # value # pconstant sym # pconstant token
-- | Return '>=' on two values comparing by only a particular AssetClass.
pgeqByClass :: Term s (PCurrencySymbol :--> PTokenName :--> PValue :--> PValue :--> PBool)
pgeqByClass =
phoistAcyclic $
plam $ \cs tn a b ->
pvalueOf # b # cs # tn #<= pvalueOf # a # cs # tn
-- | Return '>=' on two values comparing by only a particular CurrencySymbol.
pgeqBySymbol :: Term s (PCurrencySymbol :--> PValue :--> PValue :--> PBool)
pgeqBySymbol =
phoistAcyclic $
plam $ \cs a b ->
psymbolValueOf # cs # b #<= psymbolValueOf # cs # a
-- | Return '>=' on two values comparing by only a particular Haskell-level AssetClass.
pgeqByClass' :: AssetClass -> Term s (PValue :--> PValue :--> PBool)
pgeqByClass' ac =
phoistAcyclic $
plam $ \a b ->
passetClassValueOf' ac # b #<= passetClassValueOf' ac # a
-- | Union two maps using a merge function on collisions.
pmapUnionWith :: forall k v s. PIsData v => Term s ((v :--> v :--> v) :--> PMap k v :--> PMap k v :--> PMap k v)
pmapUnionWith = phoistAcyclic $
-- TODO: this function is kinda suspect. I feel like a lot of optimizations could be done here
plam $ \f xs' ys' -> unTermCont $ do
PMap xs <- tcmatch xs'
PMap ys <- tcmatch ys'
let ls =
pmap
# plam
( \p -> unTermCont $ do
pf <- tclet $ pfstBuiltin # p
pure $
pmatch (plookup # pf # ys) $ \case
PJust v ->
-- Data conversions here are silly, aren't they?
ppairDataBuiltin # pf # pdata (f # pfromData (psndBuiltin # p) # pfromData v)
PNothing -> p
)
# xs
rs =
pfilter
# plam
( \p ->
pnot #$ pany # plam (\p' -> pfstBuiltin # p' #== pfstBuiltin # p) # xs
)
# ys
pure $ pcon (PMap $ pconcat # ls # rs)
-- | A special version of `pmap` which allows list elements to be thrown out.
pmapMaybe :: forall s a list. (PIsListLike list a) => Term s ((a :--> PMaybe a) :--> list a :--> list a)
pmapMaybe = phoistAcyclic $
pfix #$ plam $ \self f l -> pif (pnull # l) pnil $
unTermCont $ do
x <- tclet $ phead # l
xs <- tclet $ ptail # l
pure $
pmatch (f # x) $ \case
PJust ux -> pcons # ux #$ self # f # xs
_ -> self # f # xs
-- | / O(n) /. Update the value at a given key in a `PMap`, have the same functionalities as 'Data.Map.update'.
pupdate :: forall s k v. (PIsData k, PIsData v) => Term s ((v :--> PMaybe v) :--> k :--> PMap k v :--> PMap k v)
pupdate = phoistAcyclic $
plam $ \f (pdata -> tk) (pto -> (ps :: Term _ (PBuiltinList _))) ->
pcon $
PMap $
pmapMaybe
# plam
( \kv ->
let k = pfstBuiltin # kv
v = pfromData $ psndBuiltin # kv
in pif
(k #== tk)
-- 'PBuiltinPair' doesn't have 'PFunctor', so:
( pmatch (f # v) $
\case
PJust uv -> pcon $ PJust $ ppairDataBuiltin # k # pdata uv
_ -> pcon PNothing
)
(pcon $ PJust kv)
)
# ps
-- | / O(n) /. Map a function over all values in a 'PMap'.
pmapMap :: forall s k a b. (PIsData k, PIsData a, PIsData b) => Term s ((a :--> b) :--> PMap k a :--> PMap k b)
pmapMap = phoistAcyclic $
plam $ \f (pto -> (ps :: Term _ (PBuiltinList _))) ->
pcon $
PMap $
pmap
# plam
( \kv ->
let k = pfstBuiltin # kv
v = psndBuiltin # kv
nv = pdata $ f # pfromData v
in ppairDataBuiltin # k # nv
)
# ps
-- | Add two 'PValue's together.
paddValue :: forall s. Term s (PValue :--> PValue :--> PValue)
paddValue = phoistAcyclic $
plam $ \a' b' -> unTermCont $ do
PValue a <- tcmatch a'
PValue b <- tcmatch b'
pure $
pcon
( PValue $
pmapUnionWith # plam (\a' b' -> pmapUnionWith # plam (+) # a' # b') # a # b
)
-- | Sum of all value at input.
pvalueSpent :: Term s (PBuiltinList (PAsData PTxInInfo) :--> PValue)
pvalueSpent = phoistAcyclic $
plam $ \inputs ->
pfoldr
# plam
( \txInInfo' v ->
pmatch
(pfromData txInInfo')
$ \(PTxInInfo txInInfo) ->
paddValue
# pmatch
(pfield @"resolved" # txInInfo)
(\(PTxOut o) -> pfromData $ pfield @"value" # o)
# v
)
# pconstant mempty
# inputs
-- | Find the TxInInfo by a TxOutRef.
pfindTxInByTxOutRef :: Term s (PTxOutRef :--> PBuiltinList (PAsData PTxInInfo) :--> PMaybe PTxInInfo)
pfindTxInByTxOutRef = phoistAcyclic $
plam $ \txOutRef inputs ->
pfindMap
# plam
( \txInInfo' ->
plet (pfromData txInInfo') $ \r ->
pmatch r $ \(PTxInInfo txInInfo) ->
pif
(pdata txOutRef #== pfield @"outRef" # txInInfo)
(pcon (PJust r))
(pcon PNothing)
)
#$ inputs
-- | True if a list is not empty.
pnotNull :: forall list a. PIsListLike list a => Term _ (list a :--> PBool)
pnotNull = phoistAcyclic $ plam $ pelimList (\_ _ -> pcon PTrue) (pcon PFalse)
{- | Check if a particular asset class has been spent in the input list.
When using this as an authority check, you __MUST__ ensure the authority
knows how to ensure its end of the contract.
-}
ptokenSpent :: forall {s :: S}. Term s (PAssetClass :--> PBuiltinList (PAsData PTxInInfo) :--> PBool)
ptokenSpent =
plam $ \tokenClass inputs ->
0
#< pfoldr @PBuiltinList
# plam
( \txInInfo' acc -> unTermCont $ do
PTxInInfo txInInfo <- tcmatch (pfromData txInInfo')
PTxOut txOut' <- tcmatch $ pfromData $ pfield @"resolved" # txInInfo
txOut <- tcont $ pletFields @'["value"] txOut'
let txOutValue = pfromData txOut.value
pure $ acc + passetClassValueOf # txOutValue # tokenClass
)
# 0
# inputs
{- | True if both maps have exactly the same keys.
Using @'#=='@ is not sufficient, because keys returned are not ordered.
-}
pkeysEqual :: (POrd k, PIsData k) => forall (s :: S) a b. Term s (PMap k a :--> PMap k b :--> PBool)
pkeysEqual = phoistAcyclic $
plam $ \p q -> unTermCont $ do
pks <- tclet $ pkeys # p
qks <- tclet $ pkeys # q
pure $
pif
(plength # pks #== plength # qks)
( unTermCont $ do
let comp = phoistAcyclic $ plam $ \(pfromData -> x) (pfromData -> y) -> x #< y
spks = pmsortBy # comp # pks
sqks = pmsortBy # comp # qks
pure $ plistEquals # spks # sqks
)
(pcon PFalse)
-- | / O(nlogn) /. Sort and remove dupicate elements in a list.
pnubSortBy ::
forall list a (s :: S).
(PIsListLike list a) =>
Term s ((a :--> a :--> PBool) :--> (a :--> a :--> PBool) :--> list a :--> list a)
pnubSortBy = phoistAcyclic $
plam $ \eq comp l -> pif (pnull # l) l $
unTermCont $ do
sl <- tclet $ pmsortBy # comp # l
let x = phead # sl
xs = ptail # sl
return $ pgo # eq # x # xs
where
pgo = phoistAcyclic pfix #$ plam pgo'
pgo' self eq seen l =
pif (pnull # l) (psingleton # seen) $
unTermCont $ do
x <- tclet $ phead # l
xs <- tclet $ ptail # l
return $
pif
(eq # x # seen)
(self # eq # seen # xs)
(pcons # seen #$ self # eq # x # xs)
-- | Special version of 'pnubSortBy', which requires elements have 'POrd'.
pnubSort ::
forall list a (s :: S).
(PIsListLike list a, POrd a) =>
Term s (list a :--> list a)
pnubSort = phoistAcyclic $ pnubSortBy # eq # comp
where
eq = phoistAcyclic $ plam (#==)
comp = phoistAcyclic $ plam (#<)
-- | / O(nlogn) /. Check if a list contains no duplicates.
pisUniqBy ::
forall list a (s :: S).
(PIsListLike list a) =>
Term s ((a :--> a :--> PBool) :--> (a :--> a :--> PBool) :--> list a :--> PBool)
pisUniqBy = phoistAcyclic $
plam $ \eq comp xs ->
let nubbed = pnubSortBy # eq # comp # xs
in plength # xs #== plength # nubbed
-- | A special case of 'pisUniqBy' which requires elements have 'POrd' instance.
pisUniq :: forall list a (s :: S). (POrd a, PIsListLike list a) => Term s (list a :--> PBool)
pisUniq = phoistAcyclic $ pisUniqBy # eq # comp
where
eq = phoistAcyclic $ plam (#==)
comp = phoistAcyclic $ plam (#<)
-- | Yield True if a given PMaybeData is of form @'PDJust' _@.
pisDJust :: Term s (PMaybeData a :--> PBool)
pisDJust = phoistAcyclic $
plam $ \x ->
pmatch
x
( \case
PDJust _ -> pconstant True
_ -> pconstant False
)
-- | Determines if a given UTXO is spent.
pisUTXOSpent :: Term s (PTxOutRef :--> PBuiltinList (PAsData PTxInInfo) :--> PBool)
pisUTXOSpent = phoistAcyclic $
plam $ \oref inputs -> P.do
pisJust #$ pfindTxInByTxOutRef # oref # inputs
-- | / O(n) /. Merge two lists which are assumed to be ordered, given a custom comparator.
pmergeBy :: (PIsListLike l a) => Term s ((a :--> a :--> PBool) :--> l a :--> l a :--> l a)
pmergeBy = phoistAcyclic $ pfix #$ plam pmergeBy'
where
pmergeBy' self comp a b =
pif (pnull # a) b $
pif (pnull # b) a $
unTermCont $ do
ah <- tclet $ phead # a
at <- tclet $ ptail # a
bh <- tclet $ phead # b
bt <- tclet $ ptail # b
pure $
pif
(comp # ah # bh)
(pcons # ah #$ self # comp # at # b)
(pcons # bh #$ self # comp # a # bt)
{- | / O(nlogn) /. Merge sort, bottom-up version, given a custom comparator.
Elements are arranged from lowest to highest,
keeping duplicates in the order they appeared in the input.
-}
pmsortBy :: (PIsListLike l a) => Term s ((a :--> a :--> PBool) :--> l a :--> l a)
pmsortBy = phoistAcyclic $ pfix #$ plam pmsortBy'
where
pmsortBy' self comp xs = pif (pnull # xs) pnil $
pif (pnull #$ ptail # xs) xs $
pmatch (phalve # xs) $ \(PPair fh sh) ->
let sfh = self # comp # fh
ssh = self # comp # sh
in pmergeBy # comp # sfh # ssh
-- | A special case of 'pmsortBy' which requires elements have 'POrd' instance.
pmsort :: (POrd a, PIsListLike l a) => Term s (l a :--> l a)
pmsort = phoistAcyclic $ pmsortBy # comp
where
comp = phoistAcyclic $ plam (#<)
-- | Split a list in half.
phalve :: (PIsListLike l a) => Term s (l a :--> PPair (l a) (l a))
phalve = phoistAcyclic $ plam $ \l -> go # l # l
where
go = phoistAcyclic $ pfix #$ plam go'
go' self xs ys =
pif
(pnull # ys)
(pcon $ PPair pnil xs)
( unTermCont $ do
yt <- tclet $ ptail # ys
xh <- tclet $ phead # xs
xt <- tclet $ ptail # xs
pure $
pif (pnull # yt) (pcon $ PPair (psingleton # xh) xt) $
unTermCont $ do
yt' <- tclet $ ptail # yt
pure $
pmatch (self # xt # yt') $ \(PPair first last) ->
pcon $ PPair (pcons # xh # first) last
)
--------------------------------------------------------------------------------
{- Functions which should (probably) not be upstreamed
All of these functions are quite inefficient.
-}
-- | Create a value with a single asset class.
psingletonValue :: forall s. Term s (PCurrencySymbol :--> PTokenName :--> PInteger :--> PValue)
psingletonValue = phoistAcyclic $
plam $ \sym tok int ->
let innerTup = pcon $ PMap $ psingleton #$ ppairDataBuiltin # pdata tok # pdata int
outerTup = pcon $ PMap $ psingleton #$ ppairDataBuiltin # pdata sym # pdata innerTup
res = pcon $ PValue outerTup
in res
-- | Finds the TxOut of an effect from TxInfo and TxOutRef
findTxOutByTxOutRef :: Term s (PTxOutRef :--> PBuiltinList (PAsData PTxInInfo) :--> PMaybe PTxOut)
findTxOutByTxOutRef = phoistAcyclic $
plam $ \txOutRef inputs ->
pmatch (pfindTxInByTxOutRef # txOutRef # inputs) $ \case
PJust ((pfield @"resolved" #) -> txOut) -> pcon $ PJust txOut
PNothing -> pcon PNothing
-- | Get script hash from an Address.
scriptHashFromAddress :: Term s (PAddress :--> PMaybe PValidatorHash)
scriptHashFromAddress = phoistAcyclic $
plam $ \addr ->
pmatch (pfromData $ pfield @"credential" # addr) $ \case
PScriptCredential ((pfield @"_0" #) -> h) -> pcon $ PJust h
_ -> pcon PNothing
-- | Return true if the given address is a script address.
isScriptAddress :: Term s (PAddress :--> PBool)
isScriptAddress = phoistAcyclic $
plam $ \addr -> pnot #$ isPubKey #$ pfromData $ pfield @"credential" # addr
-- | Return true if the given credential is a pub-key-hash.
isPubKey :: Term s (PCredential :--> PBool)
isPubKey = phoistAcyclic $
plam $ \cred ->
pmatch cred $ \case
PScriptCredential _ -> pconstant False
_ -> pconstant True
-- | Find all TxOuts sent to an Address
findOutputsToAddress :: Term s (PBuiltinList (PAsData PTxOut) :--> PAddress :--> PBuiltinList (PAsData PTxOut))
findOutputsToAddress = phoistAcyclic $
plam $ \outputs address' -> unTermCont $ do
address <- tclet $ pdata address'
pure $
pfilter # plam (\(pfromData -> txOut) -> pfield @"address" # txOut #== address)
# outputs
-- | Find the data corresponding to a TxOut, if there is one
findTxOutDatum :: Term s (PBuiltinList (PAsData (PTuple PDatumHash PDatum)) :--> PTxOut :--> PMaybe PDatum)
findTxOutDatum = phoistAcyclic $
plam $ \datums out -> unTermCont $ do
datumHash' <- tcmatch $ pfromData $ pfield @"datumHash" # out
pure $ case datumHash' of
PDJust ((pfield @"_0" #) -> datumHash) -> pfindDatum # datumHash # datums
_ -> pcon PNothing
{- | Safely convert a 'PValidatorHash' into a 'PTokenName'. This can be useful for tagging
tokens for extra safety.
-}
validatorHashToTokenName :: ValidatorHash -> TokenName
validatorHashToTokenName (ValidatorHash hash) = TokenName hash
-- | Plutarch level 'validatorHashToTokenName'.
pvalidatorHashToTokenName :: forall (s :: S). Term s PValidatorHash -> Term s PTokenName
pvalidatorHashToTokenName vh = pcon (PTokenName (pto vh))
-- | Get the CurrencySymbol of a PMintingPolicy.
getMintingPolicySymbol :: ClosedTerm PMintingPolicy -> CurrencySymbol
getMintingPolicySymbol v = mintingPolicySymbol $ mkMintingPolicy v
-- | The entire value only contains one token of the given currency symbol.
hasOnlyOneTokenOfCurrencySymbol :: Term s (PCurrencySymbol :--> PValue :--> PBool)
hasOnlyOneTokenOfCurrencySymbol = phoistAcyclic $
plam $ \cs vs -> P.do
psymbolValueOf # cs # vs #== 1
#&& (plength #$ pto $ pto $ pto vs) #== 1
-- | Find datum given a maybe datum hash
mustFindDatum' ::
forall (datum :: PType).
(PIsData datum, PTryFrom PData (PAsData datum)) =>
forall s.
Term
s
( PMaybeData PDatumHash
:--> PBuiltinList (PAsData (PTuple PDatumHash PDatum))
:--> datum
)
mustFindDatum' = phoistAcyclic $
plam $ \mdh datums -> unTermCont $ do
let dh = mustBePDJust # "Given TxOut dones't have a datum" # mdh
dt = mustBePJust # "Datum not found in the transaction" #$ plookupTuple # dh # datums
(d, _) <- tcont $ ptryFrom $ pforgetData $ pdata dt
pure $ pfromData d
{- | Extract the value stored in a PMaybe container.
If there's no value, throw an error with the given message.
-}
mustBePJust :: forall a s. Term s (PString :--> PMaybe a :--> a)
mustBePJust = phoistAcyclic $
plam $ \emsg mv' -> pmatch mv' $ \case
PJust v -> v
_ -> ptraceError emsg
{- | Extract the value stored in a PMaybeData container.
If there's no value, throw an error with the given message.
-}
mustBePDJust :: forall a s. (PIsData a) => Term s (PString :--> PMaybeData a :--> a)
mustBePDJust = phoistAcyclic $
plam $ \emsg mv' -> pmatch mv' $ \case
PDJust ((pfield @"_0" #) -> v) -> v
_ -> ptraceError emsg
validatorHashToAddress :: ValidatorHash -> Address
validatorHashToAddress vh = Address (ScriptCredential vh) Nothing