Merge pull request #118 from Liqwid-Labs/connor/use-upstreamed-utils
Use upstreamed utilities
This commit is contained in:
commit
2d9bf1733e
20 changed files with 1736 additions and 1309 deletions
|
|
@ -12,7 +12,6 @@ import Agora.MultiSig (
|
||||||
PMultiSig,
|
PMultiSig,
|
||||||
pvalidatedByMultisig,
|
pvalidatedByMultisig,
|
||||||
)
|
)
|
||||||
import Agora.Utils (tclet)
|
|
||||||
import Data.Maybe (fromJust)
|
import Data.Maybe (fromJust)
|
||||||
import Data.Tagged (Tagged (Tagged))
|
import Data.Tagged (Tagged (Tagged))
|
||||||
import Data.Universe (Finite (..), Universe (..))
|
import Data.Universe (Finite (..), Universe (..))
|
||||||
|
|
@ -24,6 +23,7 @@ import Plutarch.Context.Spending (
|
||||||
signedWith,
|
signedWith,
|
||||||
spendingContext,
|
spendingContext,
|
||||||
)
|
)
|
||||||
|
import Plutarch.Extra.TermCont (pletC)
|
||||||
import PlutusLedgerApi.V1 (
|
import PlutusLedgerApi.V1 (
|
||||||
ScriptContext (scriptContextTxInfo),
|
ScriptContext (scriptContextTxInfo),
|
||||||
TxInfo (txInfoSignatories),
|
TxInfo (txInfoSignatories),
|
||||||
|
|
@ -98,8 +98,8 @@ expectedHs model = case classifyMultiSigProp model of
|
||||||
-- | Actual implementation of @pvalidatedByMultisig@.
|
-- | Actual implementation of @pvalidatedByMultisig@.
|
||||||
actual :: Term s (PBuiltinPair PMultiSig PScriptContext :--> PBool)
|
actual :: Term s (PBuiltinPair PMultiSig PScriptContext :--> PBool)
|
||||||
actual = plam $ \x -> unTermCont $ do
|
actual = plam $ \x -> unTermCont $ do
|
||||||
ms <- tclet $ pfstBuiltin # x
|
ms <- pletC $ pfstBuiltin # x
|
||||||
sc <- tclet $ psndBuiltin # x
|
sc <- pletC $ psndBuiltin # x
|
||||||
pure $ pvalidatedByMultisig # ms # (pfield @"txInfo" # sc)
|
pure $ pvalidatedByMultisig # ms # (pfield @"txInfo" # sc)
|
||||||
|
|
||||||
-- | Proposed property.
|
-- | Proposed property.
|
||||||
|
|
|
||||||
|
|
@ -155,8 +155,8 @@ specs =
|
||||||
]
|
]
|
||||||
datum2 =
|
datum2 =
|
||||||
TreasuryWithdrawalDatum
|
TreasuryWithdrawalDatum
|
||||||
[ (head users, asset1 4 <> asset2 5)
|
[ (head users, asset2 5 <> asset1 4)
|
||||||
, (users !! 1, asset1 2 <> asset2 1)
|
, (users !! 1, asset2 1 <> asset1 2)
|
||||||
, (users !! 2, asset1 1)
|
, (users !! 2, asset1 1)
|
||||||
]
|
]
|
||||||
[ head treasuries
|
[ head treasuries
|
||||||
|
|
|
||||||
|
|
@ -7,220 +7,9 @@ Tests for utility functions in 'Agora.Utils'.
|
||||||
-}
|
-}
|
||||||
module Spec.Utils (tests) where
|
module Spec.Utils (tests) where
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
import Agora.Utils (phalve, pisUniq, pmergeBy, pmsort, pnubSort, pupdate)
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
import Data.List (nub, sort)
|
|
||||||
import Data.Map qualified as M
|
|
||||||
import Data.Set qualified as S
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
import Control.Monad.Cont (cont, runCont)
|
|
||||||
import Test.Tasty (TestTree)
|
import Test.Tasty (TestTree)
|
||||||
import Test.Tasty.QuickCheck (
|
|
||||||
Arbitrary (arbitrary),
|
|
||||||
Property,
|
|
||||||
Testable (property),
|
|
||||||
elements,
|
|
||||||
forAll,
|
|
||||||
suchThat,
|
|
||||||
testProperty,
|
|
||||||
(.&&.),
|
|
||||||
)
|
|
||||||
import Test.Util (updateMap)
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
import PlutusTx.AssocMap qualified as AssocMap
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
tests :: [TestTree]
|
tests :: [TestTree]
|
||||||
tests =
|
tests = []
|
||||||
[ testProperty "'pmsort' sorts a list properly" prop_msortCorrect
|
|
||||||
, testProperty "'pmerge' merges two sorted lists into one sorted list" prop_mergeCorrect
|
|
||||||
, testProperty "'phalve' splits a list in half as expected" prop_halveCorrect
|
|
||||||
, testProperty "'pnubSort' sorts a list and remove duplicate elements" prop_nubSortProperly
|
|
||||||
, testProperty "'pisUniq' can tell whether all elements in a list are unique" prop_uniqueList
|
|
||||||
, testProperty "'pupdate' updates assoc maps as 'updateMap' does" prop_updateAssocMapParity
|
|
||||||
]
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-- | Yield true if 'Agora.Utils.pmsort' sorts a given list correctly.
|
|
||||||
prop_msortCorrect :: [Integer] -> Bool
|
|
||||||
prop_msortCorrect l = sorted == expected
|
|
||||||
where
|
|
||||||
-- Expected sorted list, using 'Data.List.sort'.
|
|
||||||
expected :: [Integer]
|
|
||||||
expected = sort l
|
|
||||||
|
|
||||||
--
|
|
||||||
|
|
||||||
psorted :: Term _ (PBuiltinList PInteger)
|
|
||||||
psorted = pmsort # pconstant l
|
|
||||||
|
|
||||||
sorted :: [Integer]
|
|
||||||
sorted = plift psorted
|
|
||||||
|
|
||||||
-- | Yield true if 'Agora.Utils.pmerge' merges two list into a ordered list correctly.
|
|
||||||
prop_mergeCorrect :: [Integer] -> [Integer] -> Bool
|
|
||||||
prop_mergeCorrect a b = merged == expected
|
|
||||||
where
|
|
||||||
-- Sorted list a and b
|
|
||||||
sa = sort a
|
|
||||||
sb = sort b
|
|
||||||
|
|
||||||
-- Merge two lists which are assumed to be ordered.
|
|
||||||
merge :: [Integer] -> [Integer] -> [Integer]
|
|
||||||
merge xs [] = xs
|
|
||||||
merge [] ys = ys
|
|
||||||
merge sx@(x : xs) sy@(y : ys)
|
|
||||||
| x <= y = x : merge xs sy
|
|
||||||
| otherwise = y : merge sx ys
|
|
||||||
|
|
||||||
expected :: [Integer]
|
|
||||||
expected = merge sa sb
|
|
||||||
|
|
||||||
--
|
|
||||||
|
|
||||||
pmerged :: Term _ (PBuiltinList PInteger)
|
|
||||||
pmerged = pmergeBy # plam (#<) # pconstant sa # pconstant sb
|
|
||||||
|
|
||||||
merged :: [Integer]
|
|
||||||
merged = plift pmerged
|
|
||||||
|
|
||||||
{- | Yield true if Plutarch level 'Agora.Utils.phalve' splits a given list
|
|
||||||
as its Haskell level counterpart does.
|
|
||||||
-}
|
|
||||||
prop_halveCorrect :: [Integer] -> Bool
|
|
||||||
prop_halveCorrect l = halved == expected
|
|
||||||
where
|
|
||||||
-- Halve a list.
|
|
||||||
halve :: [Integer] -> ([Integer], [Integer])
|
|
||||||
halve xs = go xs xs
|
|
||||||
where
|
|
||||||
go xs [] = ([], xs)
|
|
||||||
go (x : xs) [_] = ([x], xs)
|
|
||||||
go (x : xs) (_ : _ : ys) =
|
|
||||||
let (first, last) =
|
|
||||||
go xs ys
|
|
||||||
in (x : first, last)
|
|
||||||
go [] _ = ([], [])
|
|
||||||
|
|
||||||
expected :: ([Integer], [Integer])
|
|
||||||
expected = halve l
|
|
||||||
|
|
||||||
--
|
|
||||||
|
|
||||||
phalved :: Term _ (PPair (PBuiltinList PInteger) (PBuiltinList PInteger))
|
|
||||||
phalved = phalve # pconstant l
|
|
||||||
|
|
||||||
halved :: ([Integer], [Integer])
|
|
||||||
halved =
|
|
||||||
let f = plift $ pmatch phalved $ \(PPair x _) -> x
|
|
||||||
s = plift $ pmatch phalved $ \(PPair _ x) -> x
|
|
||||||
in (f, s)
|
|
||||||
|
|
||||||
{- | Yield true if 'Agora.Utils.pnubSort' sorts and removes
|
|
||||||
duplicate elements from a given list.
|
|
||||||
-}
|
|
||||||
prop_nubSortProperly :: [Integer] -> Bool
|
|
||||||
prop_nubSortProperly l = nubbed == expected
|
|
||||||
where
|
|
||||||
-- Sort and list and then nub it.
|
|
||||||
expected :: [Integer]
|
|
||||||
expected = nub $ sort l
|
|
||||||
|
|
||||||
--
|
|
||||||
|
|
||||||
pnubbed :: Term _ (PBuiltinList PInteger)
|
|
||||||
pnubbed = pnubSort # pconstant l
|
|
||||||
|
|
||||||
nubbed :: [Integer]
|
|
||||||
nubbed = plift pnubbed
|
|
||||||
|
|
||||||
{- | Yield true if 'Agora.Utils.isUnique' can correctly determine
|
|
||||||
whether a given list only contains unique elements or not.
|
|
||||||
-}
|
|
||||||
prop_uniqueList :: [Integer] -> Bool
|
|
||||||
prop_uniqueList l = isUnique == expected
|
|
||||||
where
|
|
||||||
-- Convert input list to a set.
|
|
||||||
-- If the set's size equals to list's size,
|
|
||||||
-- the list only contains unique elements.
|
|
||||||
expected :: Bool
|
|
||||||
expected = S.size (S.fromList l) == length l
|
|
||||||
|
|
||||||
--
|
|
||||||
|
|
||||||
isUnique = plift $ pisUniq # pconstant l
|
|
||||||
|
|
||||||
{- | Test the parity between 'updateMap' and 'pupdate',
|
|
||||||
also ensure they both work correctly.
|
|
||||||
-}
|
|
||||||
prop_updateAssocMapParity :: Property
|
|
||||||
prop_updateAssocMapParity =
|
|
||||||
runCont
|
|
||||||
( do
|
|
||||||
-- Generate a bunch unique keys.
|
|
||||||
keys <-
|
|
||||||
cont $
|
|
||||||
forAll $
|
|
||||||
arbitrary @(S.Set Integer) `suchThat` (not . S.null)
|
|
||||||
|
|
||||||
-- Generate key-value pairs.
|
|
||||||
kvPairs <- cont $ forAll $ mapM (\k -> (k,) <$> (arbitrary @Integer)) $ S.toList keys
|
|
||||||
|
|
||||||
let initialMap = AssocMap.fromList kvPairs
|
|
||||||
|
|
||||||
pinitialMap :: Term _ _
|
|
||||||
pinitialMap = phoistAcyclic $ pconstant initialMap
|
|
||||||
|
|
||||||
referenceMap = M.fromList kvPairs
|
|
||||||
|
|
||||||
let pupdatedValue :: Maybe Integer -> Term _ (PMaybe PInteger)
|
|
||||||
pupdatedValue updatedValue = phoistAcyclic $ case updatedValue of
|
|
||||||
Nothing -> pcon PNothing
|
|
||||||
Just v -> pcon $ PJust $ pconstant v
|
|
||||||
|
|
||||||
-- Given the key and the updated value, test the parity
|
|
||||||
parity key updatedValue =
|
|
||||||
let native = updateMap (const updatedValue) key initialMap
|
|
||||||
|
|
||||||
plutarch :: AssocMap.Map Integer Integer
|
|
||||||
plutarch =
|
|
||||||
plift $
|
|
||||||
pupdate
|
|
||||||
# plam (\_ -> pupdatedValue updatedValue)
|
|
||||||
# pconstant key
|
|
||||||
# pinitialMap
|
|
||||||
|
|
||||||
expected =
|
|
||||||
AssocMap.fromList $
|
|
||||||
M.toList $
|
|
||||||
M.update (const updatedValue) key referenceMap
|
|
||||||
in expected == native
|
|
||||||
&& expected == plutarch
|
|
||||||
|
|
||||||
-- Select a key, generate a maybe value.
|
|
||||||
-- The value at the key should be set to the new value or removed.
|
|
||||||
(targetKey, _) <- cont $ forAll $ elements kvPairs
|
|
||||||
updatedValue <- cont $ forAll $ arbitrary @(Maybe Integer)
|
|
||||||
|
|
||||||
-- Now what if the key doesn't exist in our map?
|
|
||||||
nonexistentKey <-
|
|
||||||
cont $
|
|
||||||
forAll $
|
|
||||||
arbitrary @Integer `suchThat` (\k -> not $ S.member k keys)
|
|
||||||
|
|
||||||
pure
|
|
||||||
( property (parity targetKey updatedValue)
|
|
||||||
.&&. property (parity nonexistentKey updatedValue)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
id
|
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,6 @@ common deps
|
||||||
, generics-sop
|
, generics-sop
|
||||||
, liqwid-plutarch-extra
|
, liqwid-plutarch-extra
|
||||||
, plutarch
|
, plutarch
|
||||||
, plutarch-extra
|
|
||||||
, plutarch-numeric
|
, plutarch-numeric
|
||||||
, plutarch-safe-money
|
, plutarch-safe-money
|
||||||
, plutus-core
|
, plutus-core
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,12 @@ import Plutarch.Api.V1 (
|
||||||
)
|
)
|
||||||
import Plutarch.Api.V1.AssetClass (passetClass, passetClassValueOf)
|
import Plutarch.Api.V1.AssetClass (passetClass, passetClassValueOf)
|
||||||
import Plutarch.Api.V1.AssocMap (PMap (PMap))
|
import Plutarch.Api.V1.AssocMap (PMap (PMap))
|
||||||
|
import Plutarch.Api.V1.ScriptContext (pisTokenSpent)
|
||||||
|
import "liqwid-plutarch-extra" Plutarch.Api.V1.Value (psymbolValueOf)
|
||||||
import "plutarch" Plutarch.Api.V1.Value (PValue (PValue))
|
import "plutarch" Plutarch.Api.V1.Value (PValue (PValue))
|
||||||
import Plutarch.Builtin (pforgetData)
|
import Plutarch.Builtin (pforgetData)
|
||||||
|
import Plutarch.Extra.List (plookup)
|
||||||
|
import Plutarch.Extra.TermCont (pguardC, pmatchC)
|
||||||
import PlutusLedgerApi.V1.Value (AssetClass (AssetClass))
|
import PlutusLedgerApi.V1.Value (AssetClass (AssetClass))
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
@ -38,16 +42,6 @@ import GHC.Generics qualified as GHC
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
import Agora.Utils (
|
|
||||||
plookup,
|
|
||||||
psymbolValueOf,
|
|
||||||
ptokenSpent,
|
|
||||||
tcassert,
|
|
||||||
tcmatch,
|
|
||||||
)
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
{- | An AuthorityToken represents a proof that a particular token
|
{- | An AuthorityToken represents a proof that a particular token
|
||||||
moved while this token was minted. In effect, this means that
|
moved while this token was minted. In effect, this means that
|
||||||
the validator that locked such a token must have approved
|
the validator that locked such a token must have approved
|
||||||
|
|
@ -74,11 +68,11 @@ newtype AuthorityToken = AuthorityToken
|
||||||
authorityTokensValidIn :: Term s (PCurrencySymbol :--> PTxOut :--> PBool)
|
authorityTokensValidIn :: Term s (PCurrencySymbol :--> PTxOut :--> PBool)
|
||||||
authorityTokensValidIn = phoistAcyclic $
|
authorityTokensValidIn = phoistAcyclic $
|
||||||
plam $ \authorityTokenSym txOut'' -> unTermCont $ do
|
plam $ \authorityTokenSym txOut'' -> unTermCont $ do
|
||||||
PTxOut txOut' <- tcmatch txOut''
|
PTxOut txOut' <- pmatchC txOut''
|
||||||
txOut <- tcont $ pletFields @'["address", "value"] $ txOut'
|
txOut <- tcont $ pletFields @'["address", "value"] $ txOut'
|
||||||
PAddress address <- tcmatch txOut.address
|
PAddress address <- pmatchC txOut.address
|
||||||
PValue value' <- tcmatch txOut.value
|
PValue value' <- pmatchC txOut.value
|
||||||
PMap value <- tcmatch value'
|
PMap value <- pmatchC value'
|
||||||
pure $
|
pure $
|
||||||
pmatch (plookup # pdata authorityTokenSym # value) $ \case
|
pmatch (plookup # pdata authorityTokenSym # value) $ \case
|
||||||
PJust (pfromData -> tokenMap') ->
|
PJust (pfromData -> tokenMap') ->
|
||||||
|
|
@ -87,7 +81,7 @@ authorityTokensValidIn = phoistAcyclic $
|
||||||
-- GATs should only be sent to Effect validators
|
-- GATs should only be sent to Effect validators
|
||||||
ptraceIfFalse "authorityTokensValidIn: GAT incorrectly lives at PubKey" $ pconstant False
|
ptraceIfFalse "authorityTokensValidIn: GAT incorrectly lives at PubKey" $ pconstant False
|
||||||
PScriptCredential ((pfromData . (pfield @"_0" #)) -> cred) -> unTermCont $ do
|
PScriptCredential ((pfromData . (pfield @"_0" #)) -> cred) -> unTermCont $ do
|
||||||
PMap tokenMap <- tcmatch tokenMap'
|
PMap tokenMap <- pmatchC tokenMap'
|
||||||
pure $
|
pure $
|
||||||
ptraceIfFalse "authorityTokensValidIn: GAT TokenName doesn't match ScriptHash" $
|
ptraceIfFalse "authorityTokensValidIn: GAT TokenName doesn't match ScriptHash" $
|
||||||
pall
|
pall
|
||||||
|
|
@ -121,7 +115,7 @@ singleAuthorityTokenBurned gatCs txInfo mint = unTermCont $ do
|
||||||
pall
|
pall
|
||||||
# plam
|
# plam
|
||||||
( \txInInfo' -> unTermCont $ do
|
( \txInInfo' -> unTermCont $ do
|
||||||
PTxInInfo txInInfo <- tcmatch (pfromData txInInfo')
|
PTxInInfo txInInfo <- pmatchC (pfromData txInInfo')
|
||||||
let txOut' = pfield @"resolved" # txInInfo
|
let txOut' = pfield @"resolved" # txInInfo
|
||||||
pure $ authorityTokensValidIn # gatCs # pfromData txOut'
|
pure $ authorityTokensValidIn # gatCs # pfromData txOut'
|
||||||
)
|
)
|
||||||
|
|
@ -134,15 +128,15 @@ authorityTokenPolicy params =
|
||||||
plam $ \_redeemer ctx' ->
|
plam $ \_redeemer ctx' ->
|
||||||
pmatch ctx' $ \(PScriptContext ctx') -> unTermCont $ do
|
pmatch ctx' $ \(PScriptContext ctx') -> unTermCont $ do
|
||||||
ctx <- tcont $ pletFields @'["txInfo", "purpose"] ctx'
|
ctx <- tcont $ pletFields @'["txInfo", "purpose"] ctx'
|
||||||
PTxInfo txInfo' <- tcmatch $ pfromData ctx.txInfo
|
PTxInfo txInfo' <- pmatchC $ pfromData ctx.txInfo
|
||||||
txInfo <- tcont $ pletFields @'["inputs", "mint", "outputs"] txInfo'
|
txInfo <- tcont $ pletFields @'["inputs", "mint", "outputs"] txInfo'
|
||||||
let inputs = txInfo.inputs
|
let inputs = txInfo.inputs
|
||||||
mintedValue = pfromData txInfo.mint
|
mintedValue = pfromData txInfo.mint
|
||||||
AssetClass (govCs, govTn) = params.authority
|
AssetClass (govCs, govTn) = params.authority
|
||||||
govAc = passetClass # pconstant govCs # pconstant govTn
|
govAc = passetClass # pconstant govCs # pconstant govTn
|
||||||
govTokenSpent = ptokenSpent # govAc # inputs
|
govTokenSpent = pisTokenSpent # govAc # inputs
|
||||||
|
|
||||||
PMinting ownSymbol' <- tcmatch $ pfromData ctx.purpose
|
PMinting ownSymbol' <- pmatchC $ pfromData ctx.purpose
|
||||||
|
|
||||||
let ownSymbol = pfromData $ pfield @"_0" # ownSymbol'
|
let ownSymbol = pfromData $ pfield @"_0" # ownSymbol'
|
||||||
mintedATs = passetClassValueOf # mintedValue # (passetClass # ownSymbol # pconstant "")
|
mintedATs = passetClassValueOf # mintedValue # (passetClass # ownSymbol # pconstant "")
|
||||||
|
|
@ -150,8 +144,8 @@ authorityTokenPolicy params =
|
||||||
pif
|
pif
|
||||||
(0 #< mintedATs)
|
(0 #< mintedATs)
|
||||||
( unTermCont $ do
|
( unTermCont $ do
|
||||||
tcassert "Parent token did not move in minting GATs" govTokenSpent
|
pguardC "Parent token did not move in minting GATs" govTokenSpent
|
||||||
tcassert "All outputs only emit valid GATs" $
|
pguardC "All outputs only emit valid GATs" $
|
||||||
pall
|
pall
|
||||||
# plam
|
# plam
|
||||||
( (authorityTokensValidIn # ownSymbol #)
|
( (authorityTokensValidIn # ownSymbol #)
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ Helpers for constructing effects.
|
||||||
module Agora.Effect (makeEffect) where
|
module Agora.Effect (makeEffect) where
|
||||||
|
|
||||||
import Agora.AuthorityToken (singleAuthorityTokenBurned)
|
import Agora.AuthorityToken (singleAuthorityTokenBurned)
|
||||||
import Agora.Utils (tcassert, tclet, tcmatch, tctryFrom)
|
|
||||||
import Plutarch.Api.V1 (PCurrencySymbol, PScriptPurpose (PSpending), PTxInfo, PTxOutRef, PValidator, PValue)
|
import Plutarch.Api.V1 (PCurrencySymbol, PScriptPurpose (PSpending), PTxInfo, PTxOutRef, PValidator, PValue)
|
||||||
|
import Plutarch.Extra.TermCont (pguardC, pletC, pmatchC, ptryFromC)
|
||||||
import Plutarch.TryFrom ()
|
import Plutarch.TryFrom ()
|
||||||
import PlutusLedgerApi.V1.Value (CurrencySymbol)
|
import PlutusLedgerApi.V1.Value (CurrencySymbol)
|
||||||
|
|
||||||
|
|
@ -30,16 +30,16 @@ makeEffect ::
|
||||||
makeEffect gatCs' f =
|
makeEffect gatCs' f =
|
||||||
plam $ \datum _redeemer ctx' -> unTermCont $ do
|
plam $ \datum _redeemer ctx' -> unTermCont $ do
|
||||||
ctx <- tcont $ pletFields @'["txInfo", "purpose"] ctx'
|
ctx <- tcont $ pletFields @'["txInfo", "purpose"] ctx'
|
||||||
txInfo' <- tclet ctx.txInfo
|
txInfo' <- pletC ctx.txInfo
|
||||||
|
|
||||||
-- convert input datum, PData, into desierable type
|
-- convert input datum, PData, into desierable type
|
||||||
-- the way this conversion is performed should be defined
|
-- the way this conversion is performed should be defined
|
||||||
-- by PTryFrom for each datum in effect script.
|
-- by PTryFrom for each datum in effect script.
|
||||||
(pfromData -> datum', _) <- tctryFrom datum
|
(pfromData -> datum', _) <- ptryFromC datum
|
||||||
|
|
||||||
-- ensure purpose is Spending.
|
-- ensure purpose is Spending.
|
||||||
PSpending txOutRef <- tcmatch $ pfromData ctx.purpose
|
PSpending txOutRef <- pmatchC $ pfromData ctx.purpose
|
||||||
txOutRef' <- tclet (pfield @"_0" # txOutRef)
|
txOutRef' <- pletC (pfield @"_0" # txOutRef)
|
||||||
|
|
||||||
-- fetch minted values to ensure single GAT is burned
|
-- fetch minted values to ensure single GAT is burned
|
||||||
txInfo <- tcont $ pletFields @'["mint"] txInfo'
|
txInfo <- tcont $ pletFields @'["mint"] txInfo'
|
||||||
|
|
@ -47,9 +47,9 @@ makeEffect gatCs' f =
|
||||||
mint = txInfo.mint
|
mint = txInfo.mint
|
||||||
|
|
||||||
-- fetch script context
|
-- fetch script context
|
||||||
gatCs <- tclet $ pconstant gatCs'
|
gatCs <- pletC $ pconstant gatCs'
|
||||||
|
|
||||||
tcassert "A single authority token has been burned" $ singleAuthorityTokenBurned gatCs txInfo' mint
|
pguardC "A single authority token has been burned" $ singleAuthorityTokenBurned gatCs txInfo' mint
|
||||||
|
|
||||||
-- run effect function
|
-- run effect function
|
||||||
pure $ f gatCs datum' txOutRef' txInfo'
|
pure $ f gatCs datum' txOutRef' txInfo'
|
||||||
|
|
|
||||||
|
|
@ -31,12 +31,14 @@ import Plutarch.Api.V1 (
|
||||||
PValidator,
|
PValidator,
|
||||||
PValue,
|
PValue,
|
||||||
)
|
)
|
||||||
|
import Plutarch.Api.V1.ScriptContext (ptryFindDatum)
|
||||||
import "liqwid-plutarch-extra" Plutarch.Api.V1.Value (pvalueOf)
|
import "liqwid-plutarch-extra" Plutarch.Api.V1.Value (pvalueOf)
|
||||||
import Plutarch.DataRepr (
|
import Plutarch.DataRepr (
|
||||||
DerivePConstantViaData (..),
|
DerivePConstantViaData (..),
|
||||||
PDataFields,
|
PDataFields,
|
||||||
PIsDataReprInstances (PIsDataReprInstances),
|
PIsDataReprInstances (PIsDataReprInstances),
|
||||||
)
|
)
|
||||||
|
import Plutarch.Extra.TermCont (pguardC)
|
||||||
import Plutarch.Lift (PConstantDecl, PLifted, PUnsafeLiftDecl)
|
import Plutarch.Lift (PConstantDecl, PLifted, PUnsafeLiftDecl)
|
||||||
import Plutarch.TryFrom (PTryFrom (..))
|
import Plutarch.TryFrom (PTryFrom (..))
|
||||||
import Plutarch.Unsafe (punsafeCoerce)
|
import Plutarch.Unsafe (punsafeCoerce)
|
||||||
|
|
@ -64,8 +66,6 @@ import Agora.Utils (
|
||||||
isScriptAddress,
|
isScriptAddress,
|
||||||
mustBePDJust,
|
mustBePDJust,
|
||||||
mustBePJust,
|
mustBePJust,
|
||||||
ptryFindDatum,
|
|
||||||
tcassert,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
@ -145,11 +145,11 @@ mutateGovernorValidator gov = makeEffect (authorityTokenSymbolFromGovernor gov)
|
||||||
let mint :: Term _ (PBuiltinList _)
|
let mint :: Term _ (PBuiltinList _)
|
||||||
mint = pto $ pto $ pto $ pfromData txInfoF.mint
|
mint = pto $ pto $ pto $ pfromData txInfoF.mint
|
||||||
|
|
||||||
tcassert "Nothing should be minted/burnt other than GAT" $
|
pguardC "Nothing should be minted/burnt other than GAT" $
|
||||||
plength # mint #== 1
|
plength # mint #== 1
|
||||||
|
|
||||||
-- Only two script inputs are alloed: one from the effect, one from the governor.
|
-- Only two script inputs are alloed: one from the effect, one from the governor.
|
||||||
tcassert "Only self and governor script inputs are allowed" $
|
pguardC "Only self and governor script inputs are allowed" $
|
||||||
pfoldr
|
pfoldr
|
||||||
# phoistAcyclic
|
# phoistAcyclic
|
||||||
( plam $ \inInfo count ->
|
( plam $ \inInfo count ->
|
||||||
|
|
@ -176,11 +176,11 @@ mutateGovernorValidator gov = makeEffect (authorityTokenSymbolFromGovernor gov)
|
||||||
govInInfo <- tcont $ pletFields @'["outRef", "resolved"] $ inputWithGST
|
govInInfo <- tcont $ pletFields @'["outRef", "resolved"] $ inputWithGST
|
||||||
|
|
||||||
-- The effect can only modify the governor UTXO referenced in the datum.
|
-- The effect can only modify the governor UTXO referenced in the datum.
|
||||||
tcassert "Can only modify the pinned governor" $
|
pguardC "Can only modify the pinned governor" $
|
||||||
govInInfo.outRef #== datumF.governorRef
|
govInInfo.outRef #== datumF.governorRef
|
||||||
|
|
||||||
-- The transaction can only have one output, which should be sent to the governor.
|
-- The transaction can only have one output, which should be sent to the governor.
|
||||||
tcassert "Only governor output is allowed" $
|
pguardC "Only governor output is allowed" $
|
||||||
plength # pfromData txInfoF.outputs #== 1
|
plength # pfromData txInfoF.outputs #== 1
|
||||||
|
|
||||||
let govAddress = pfield @"address" #$ govInInfo.resolved
|
let govAddress = pfield @"address" #$ govInInfo.resolved
|
||||||
|
|
@ -188,10 +188,10 @@ mutateGovernorValidator gov = makeEffect (authorityTokenSymbolFromGovernor gov)
|
||||||
|
|
||||||
govOutput <- tcont $ pletFields @'["address", "value", "datumHash"] govOutput'
|
govOutput <- tcont $ pletFields @'["address", "value", "datumHash"] govOutput'
|
||||||
|
|
||||||
tcassert "No output to the governor" $
|
pguardC "No output to the governor" $
|
||||||
govOutput.address #== govAddress
|
govOutput.address #== govAddress
|
||||||
|
|
||||||
tcassert "Governor output doesn't carry the GST" $
|
pguardC "Governor output doesn't carry the GST" $
|
||||||
gstValueOf # govOutput.value #== 1
|
gstValueOf # govOutput.value #== 1
|
||||||
|
|
||||||
let governorOutputDatumHash =
|
let governorOutputDatumHash =
|
||||||
|
|
@ -202,8 +202,8 @@ mutateGovernorValidator gov = makeEffect (authorityTokenSymbolFromGovernor gov)
|
||||||
#$ ptryFindDatum # governorOutputDatumHash # txInfoF.datums
|
#$ ptryFindDatum # governorOutputDatumHash # txInfoF.datums
|
||||||
|
|
||||||
-- Ensure the output governor datum is what we want.
|
-- Ensure the output governor datum is what we want.
|
||||||
tcassert "Unexpected governor datum" $ datumF.newDatum #== governorOutputDatum
|
pguardC "Unexpected governor datum" $ datumF.newDatum #== governorOutputDatum
|
||||||
tcassert "New governor datum should be valid" $ governorDatumValid # governorOutputDatum
|
pguardC "New governor datum should be valid" $ governorDatumValid # governorOutputDatum
|
||||||
|
|
||||||
return $ popaque $ pconstant ()
|
return $ popaque $ pconstant ()
|
||||||
where
|
where
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ import GHC.Generics qualified as GHC
|
||||||
import Generics.SOP (Generic, I (I))
|
import Generics.SOP (Generic, I (I))
|
||||||
|
|
||||||
import Agora.Effect (makeEffect)
|
import Agora.Effect (makeEffect)
|
||||||
import Agora.Utils (findTxOutByTxOutRef, isPubKey, paddValue, tcassert, tclet, tcmatch)
|
import Agora.Utils (findTxOutByTxOutRef, isPubKey)
|
||||||
import Plutarch.Api.V1 (
|
import Plutarch.Api.V1 (
|
||||||
AmountGuarantees (Positive),
|
AmountGuarantees (Positive),
|
||||||
KeyGuarantees (Sorted),
|
KeyGuarantees (Sorted),
|
||||||
|
|
@ -28,14 +28,15 @@ import Plutarch.Api.V1 (
|
||||||
PValue,
|
PValue,
|
||||||
ptuple,
|
ptuple,
|
||||||
)
|
)
|
||||||
import "plutarch" Plutarch.Api.V1.Value (pnormalize)
|
|
||||||
import Plutarch.Internal (punsafeCoerce)
|
import Plutarch.Internal (punsafeCoerce)
|
||||||
|
|
||||||
|
import "plutarch" Plutarch.Api.V1.Value (pnormalize)
|
||||||
import Plutarch.DataRepr (
|
import Plutarch.DataRepr (
|
||||||
DerivePConstantViaData (..),
|
DerivePConstantViaData (..),
|
||||||
PDataFields,
|
PDataFields,
|
||||||
PIsDataReprInstances (..),
|
PIsDataReprInstances (..),
|
||||||
)
|
)
|
||||||
|
import Plutarch.Extra.TermCont (pguardC, pletC, pmatchC)
|
||||||
import Plutarch.Lift (PConstantDecl, PUnsafeLiftDecl (..))
|
import Plutarch.Lift (PConstantDecl, PUnsafeLiftDecl (..))
|
||||||
import Plutarch.TryFrom (PTryFrom (..))
|
import Plutarch.TryFrom (PTryFrom (..))
|
||||||
import PlutusLedgerApi.V1.Credential (Credential)
|
import PlutusLedgerApi.V1.Credential (Credential)
|
||||||
|
|
@ -111,10 +112,10 @@ treasuryWithdrawalValidator currSymbol = makeEffect currSymbol $
|
||||||
\_cs (datum' :: Term _ PTreasuryWithdrawalDatum) txOutRef' txInfo' -> unTermCont $ do
|
\_cs (datum' :: Term _ PTreasuryWithdrawalDatum) txOutRef' txInfo' -> unTermCont $ do
|
||||||
datum <- tcont $ pletFields @'["receivers", "treasuries"] datum'
|
datum <- tcont $ pletFields @'["receivers", "treasuries"] datum'
|
||||||
txInfo <- tcont $ pletFields @'["outputs", "inputs"] txInfo'
|
txInfo <- tcont $ pletFields @'["outputs", "inputs"] txInfo'
|
||||||
PJust txOut <- tcmatch $ findTxOutByTxOutRef # txOutRef' # pfromData txInfo.inputs
|
PJust txOut <- pmatchC $ findTxOutByTxOutRef # txOutRef' # pfromData txInfo.inputs
|
||||||
effInput <- tcont $ pletFields @'["address", "value"] $ txOut
|
effInput <- tcont $ pletFields @'["address", "value"] $ txOut
|
||||||
outputValues <-
|
outputValues <-
|
||||||
tclet $
|
pletC $
|
||||||
pmap
|
pmap
|
||||||
# plam
|
# plam
|
||||||
( \(pfromData -> txOut') -> unTermCont $ do
|
( \(pfromData -> txOut') -> unTermCont $ do
|
||||||
|
|
@ -124,7 +125,7 @@ treasuryWithdrawalValidator currSymbol = makeEffect currSymbol $
|
||||||
)
|
)
|
||||||
# txInfo.outputs
|
# txInfo.outputs
|
||||||
inputValues <-
|
inputValues <-
|
||||||
tclet $
|
pletC $
|
||||||
pmap
|
pmap
|
||||||
# plam
|
# plam
|
||||||
( \((pfield @"resolved" #) . pfromData -> txOut') -> unTermCont $ do
|
( \((pfield @"resolved" #) . pfromData -> txOut') -> unTermCont $ do
|
||||||
|
|
@ -136,10 +137,13 @@ treasuryWithdrawalValidator currSymbol = makeEffect currSymbol $
|
||||||
let ofTreasury =
|
let ofTreasury =
|
||||||
pfilter
|
pfilter
|
||||||
# plam (\((pfield @"_0" #) . pfromData -> cred) -> pelem # cred # datum.treasuries)
|
# plam (\((pfield @"_0" #) . pfromData -> cred) -> pelem # cred # datum.treasuries)
|
||||||
sumValues =
|
sumValues = phoistAcyclic $
|
||||||
pfoldr
|
plam $ \v ->
|
||||||
# plam (\((pfield @"_1" #) . pfromData -> x) ((pnormalize #) -> y) -> paddValue # pfromData x # y)
|
pnormalize
|
||||||
# punsafeCoerce (pconstant (mempty :: Value))
|
#$ pfoldr
|
||||||
|
# plam (\(pfromData . (pfield @"_1" #) -> x) y -> x <> y)
|
||||||
|
# mempty
|
||||||
|
# v
|
||||||
treasuryInputValuesSum = sumValues #$ ofTreasury # inputValues
|
treasuryInputValuesSum = sumValues #$ ofTreasury # inputValues
|
||||||
treasuryOutputValuesSum = sumValues #$ ofTreasury # outputValues
|
treasuryOutputValuesSum = sumValues #$ ofTreasury # outputValues
|
||||||
receiverValuesSum = sumValues # datum.receivers
|
receiverValuesSum = sumValues # datum.receivers
|
||||||
|
|
@ -148,7 +152,7 @@ treasuryWithdrawalValidator currSymbol = makeEffect currSymbol $
|
||||||
pall # plam (\out -> pelem # out # outputValues)
|
pall # plam (\out -> pelem # out # outputValues)
|
||||||
#$ datum.receivers
|
#$ datum.receivers
|
||||||
excessShouldBePaidToInputs =
|
excessShouldBePaidToInputs =
|
||||||
pdata (paddValue # receiverValuesSum # treasuryOutputValuesSum) #== pdata treasuryInputValuesSum
|
treasuryOutputValuesSum <> receiverValuesSum #== treasuryInputValuesSum
|
||||||
shouldNotPayToEffect =
|
shouldNotPayToEffect =
|
||||||
pnot #$ pany
|
pnot #$ pany
|
||||||
# plam
|
# plam
|
||||||
|
|
@ -166,8 +170,8 @@ treasuryWithdrawalValidator currSymbol = makeEffect currSymbol $
|
||||||
)
|
)
|
||||||
# inputValues
|
# inputValues
|
||||||
|
|
||||||
tcassert "Transaction should not pay to effects" shouldNotPayToEffect
|
pguardC "Transaction should not pay to effects" shouldNotPayToEffect
|
||||||
tcassert "Transaction output does not match receivers" outputContentMatchesRecivers
|
pguardC "Transaction output does not match receivers" outputContentMatchesRecivers
|
||||||
tcassert "Remainders should be returned to the treasury" excessShouldBePaidToInputs
|
pguardC "Remainders should be returned to the treasury" excessShouldBePaidToInputs
|
||||||
tcassert "Transaction should only have treasuries specified in the datum as input" inputsAreOnlyTreasuriesOrCollateral
|
pguardC "Transaction should only have treasuries specified in the datum as input" inputsAreOnlyTreasuriesOrCollateral
|
||||||
pure . popaque $ pconstant ()
|
pure . popaque $ pconstant ()
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ module Agora.Governor (
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
import Control.Applicative (Const)
|
import Control.Applicative (Const)
|
||||||
|
import Data.Tagged (Tagged (..))
|
||||||
import GHC.Generics qualified as GHC
|
import GHC.Generics qualified as GHC
|
||||||
import Generics.SOP (Generic, I (I))
|
import Generics.SOP (Generic, I (I))
|
||||||
|
|
||||||
|
|
@ -44,18 +45,16 @@ import Agora.Proposal.Time (
|
||||||
ProposalTimingConfig,
|
ProposalTimingConfig,
|
||||||
)
|
)
|
||||||
import Agora.SafeMoney (GTTag)
|
import Agora.SafeMoney (GTTag)
|
||||||
import Agora.Utils (tclet)
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
import Data.Tagged (Tagged (..))
|
|
||||||
import Plutarch.DataRepr (
|
import Plutarch.DataRepr (
|
||||||
DerivePConstantViaData (..),
|
DerivePConstantViaData (..),
|
||||||
PDataFields,
|
PDataFields,
|
||||||
PIsDataReprInstances (PIsDataReprInstances),
|
PIsDataReprInstances (PIsDataReprInstances),
|
||||||
)
|
)
|
||||||
import Plutarch.Extra.Comonad (pextract)
|
import Plutarch.Extra.Comonad (pextract)
|
||||||
import Plutarch.Extra.TermCont (pmatchC)
|
import Plutarch.Extra.TermCont (pletC, pmatchC)
|
||||||
import Plutarch.Lift (PConstantDecl, PUnsafeLiftDecl (..))
|
import Plutarch.Lift (PConstantDecl, PUnsafeLiftDecl (..))
|
||||||
import Plutarch.SafeMoney (PDiscrete (..))
|
import Plutarch.SafeMoney (PDiscrete (..))
|
||||||
import Plutarch.TryFrom (PTryFrom (..))
|
import Plutarch.TryFrom (PTryFrom (..))
|
||||||
|
|
@ -196,9 +195,9 @@ governorDatumValid = phoistAcyclic $
|
||||||
PDiscrete draft' <- pmatchC thresholds.create
|
PDiscrete draft' <- pmatchC thresholds.create
|
||||||
PDiscrete vote' <- pmatchC thresholds.vote
|
PDiscrete vote' <- pmatchC thresholds.vote
|
||||||
|
|
||||||
execute <- tclet $ pextract # execute'
|
execute <- pletC $ pextract # execute'
|
||||||
draft <- tclet $ pextract # draft'
|
draft <- pletC $ pextract # draft'
|
||||||
vote <- tclet $ pextract # vote'
|
vote <- pletC $ pextract # vote'
|
||||||
|
|
||||||
pure $
|
pure $
|
||||||
foldr1
|
foldr1
|
||||||
|
|
|
||||||
|
|
@ -77,17 +77,7 @@ import Agora.Utils (
|
||||||
mustBePDJust,
|
mustBePDJust,
|
||||||
mustBePJust,
|
mustBePJust,
|
||||||
mustFindDatum',
|
mustFindDatum',
|
||||||
pfindTxInByTxOutRef,
|
|
||||||
pisDJust,
|
|
||||||
pisUTXOSpent,
|
|
||||||
psymbolValueOf,
|
|
||||||
ptryFindDatum,
|
|
||||||
ptxSignedBy,
|
|
||||||
pvalueSpent,
|
|
||||||
scriptHashFromAddress,
|
scriptHashFromAddress,
|
||||||
tcassert,
|
|
||||||
tclet,
|
|
||||||
tcmatch,
|
|
||||||
validatorHashToAddress,
|
validatorHashToAddress,
|
||||||
validatorHashToTokenName,
|
validatorHashToTokenName,
|
||||||
)
|
)
|
||||||
|
|
@ -125,6 +115,10 @@ import Plutarch.TryFrom ()
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
import Plutarch.Api.V1.ScriptContext (pfindTxInByTxOutRef, pisUTXOSpent, ptryFindDatum, ptxSignedBy, pvalueSpent)
|
||||||
|
import "liqwid-plutarch-extra" Plutarch.Api.V1.Value (psymbolValueOf)
|
||||||
|
import Plutarch.Extra.Maybe (pisDJust)
|
||||||
|
import Plutarch.Extra.TermCont
|
||||||
import PlutusLedgerApi.V1 (
|
import PlutusLedgerApi.V1 (
|
||||||
CurrencySymbol (..),
|
CurrencySymbol (..),
|
||||||
MintingPolicy,
|
MintingPolicy,
|
||||||
|
|
@ -166,21 +160,21 @@ governorPolicy gov =
|
||||||
plam $ \_ ctx' -> unTermCont $ do
|
plam $ \_ ctx' -> unTermCont $ do
|
||||||
let oref = pconstant gov.gstOutRef
|
let oref = pconstant gov.gstOutRef
|
||||||
|
|
||||||
PMinting ((pfield @"_0" #) -> ownSymbol) <- tcmatch (pfromData $ pfield @"purpose" # ctx')
|
PMinting ((pfield @"_0" #) -> ownSymbol) <- pmatchC (pfromData $ pfield @"purpose" # ctx')
|
||||||
let ownAssetClass = passetClass # ownSymbol # pconstant ""
|
let ownAssetClass = passetClass # ownSymbol # pconstant ""
|
||||||
txInfo = pfromData $ pfield @"txInfo" # ctx'
|
txInfo = pfromData $ pfield @"txInfo" # ctx'
|
||||||
|
|
||||||
txInfoF <- tcont $ pletFields @'["mint", "inputs", "outputs", "datums", "validRange"] txInfo
|
txInfoF <- tcont $ pletFields @'["mint", "inputs", "outputs", "datums", "validRange"] txInfo
|
||||||
|
|
||||||
tcassert "Referenced utxo should be spent" $
|
pguardC "Referenced utxo should be spent" $
|
||||||
pisUTXOSpent # oref # txInfoF.inputs
|
pisUTXOSpent # oref # txInfoF.inputs
|
||||||
|
|
||||||
tcassert "Exactly one token should be minted" $
|
pguardC "Exactly one token should be minted" $
|
||||||
psymbolValueOf # ownSymbol # txInfoF.mint #== 1
|
psymbolValueOf # ownSymbol # txInfoF.mint #== 1
|
||||||
#&& passetClassValueOf # txInfoF.mint # ownAssetClass #== 1
|
#&& passetClassValueOf # txInfoF.mint # ownAssetClass #== 1
|
||||||
|
|
||||||
govOutput <-
|
govOutput <-
|
||||||
tclet $
|
pletC $
|
||||||
mustBePJust
|
mustBePJust
|
||||||
# "Governor output not found"
|
# "Governor output not found"
|
||||||
#$ pfind
|
#$ pfind
|
||||||
|
|
@ -285,13 +279,13 @@ governorValidator gov =
|
||||||
(pfromData -> redeemer, _) <- tcont $ ptryFrom redeemer'
|
(pfromData -> redeemer, _) <- tcont $ ptryFrom redeemer'
|
||||||
ctxF <- tcont $ pletFields @'["txInfo", "purpose"] ctx'
|
ctxF <- tcont $ pletFields @'["txInfo", "purpose"] ctx'
|
||||||
|
|
||||||
txInfo' <- tclet $ pfromData $ ctxF.txInfo
|
txInfo' <- pletC $ pfromData $ ctxF.txInfo
|
||||||
txInfoF <- tcont $ pletFields @'["mint", "inputs", "outputs", "datums", "signatories", "validRange"] txInfo'
|
txInfoF <- tcont $ pletFields @'["mint", "inputs", "outputs", "datums", "signatories", "validRange"] txInfo'
|
||||||
|
|
||||||
PSpending (pfromData . (pfield @"_0" #) -> ownInputRef) <- tcmatch $ pfromData ctxF.purpose
|
PSpending (pfromData . (pfield @"_0" #) -> ownInputRef) <- pmatchC $ pfromData ctxF.purpose
|
||||||
|
|
||||||
((pfield @"resolved" #) -> ownInput) <-
|
((pfield @"resolved" #) -> ownInput) <-
|
||||||
tclet $
|
pletC $
|
||||||
mustBePJust # "Own input not found"
|
mustBePJust # "Own input not found"
|
||||||
#$ pfindTxInByTxOutRef # ownInputRef # txInfoF.inputs
|
#$ pfindTxInByTxOutRef # ownInputRef # txInfoF.inputs
|
||||||
ownInputF <- tcont $ pletFields @'["address", "value"] ownInput
|
ownInputF <- tcont $ pletFields @'["address", "value"] ownInput
|
||||||
|
|
@ -310,27 +304,27 @@ governorValidator gov =
|
||||||
|
|
||||||
-- Check that GST will be returned to the governor.
|
-- Check that GST will be returned to the governor.
|
||||||
let ownInputGSTAmount = psymbolValueOf # pgstSymbol # ownInputF.value
|
let ownInputGSTAmount = psymbolValueOf # pgstSymbol # ownInputF.value
|
||||||
tcassert "Own input should have exactly one state token" $
|
pguardC "Own input should have exactly one state token" $
|
||||||
ownInputGSTAmount #== 1
|
ownInputGSTAmount #== 1
|
||||||
|
|
||||||
ownOutputs <- tclet $ findOutputsToAddress # txInfoF.outputs # ownAddress
|
ownOutputs <- pletC $ findOutputsToAddress # txInfoF.outputs # ownAddress
|
||||||
tcassert "Exactly one utxo should be sent to the governor" $
|
pguardC "Exactly one utxo should be sent to the governor" $
|
||||||
plength # ownOutputs #== 1
|
plength # ownOutputs #== 1
|
||||||
|
|
||||||
ownOutput <- tcont $ pletFields @'["value", "datumHash"] $ phead # ownOutputs
|
ownOutput <- tcont $ pletFields @'["value", "datumHash"] $ phead # ownOutputs
|
||||||
let ownOuputGSTAmount = psymbolValueOf # pgstSymbol # ownOutput.value
|
let ownOuputGSTAmount = psymbolValueOf # pgstSymbol # ownOutput.value
|
||||||
tcassert "State token should stay at governor's address" $
|
pguardC "State token should stay at governor's address" $
|
||||||
ownOuputGSTAmount #== 1
|
ownOuputGSTAmount #== 1
|
||||||
|
|
||||||
-- Check that own output have datum of type 'GovernorDatum'.
|
-- Check that own output have datum of type 'GovernorDatum'.
|
||||||
let outputGovernorStateDatumHash =
|
let outputGovernorStateDatumHash =
|
||||||
mustBePDJust # "Governor output doesn't have datum" # ownOutput.datumHash
|
mustBePDJust # "Governor output doesn't have datum" # ownOutput.datumHash
|
||||||
newGovernorDatum <-
|
newGovernorDatum <-
|
||||||
tclet $
|
pletC $
|
||||||
pfromData $
|
pfromData $
|
||||||
mustBePJust # "Ouput governor state datum not found"
|
mustBePJust # "Ouput governor state datum not found"
|
||||||
#$ ptryFindDatum # outputGovernorStateDatumHash # txInfoF.datums
|
#$ ptryFindDatum # outputGovernorStateDatumHash # txInfoF.datums
|
||||||
tcassert "New datum is not valid" $ governorDatumValid # newGovernorDatum
|
pguardC "New datum is not valid" $ governorDatumValid # newGovernorDatum
|
||||||
|
|
||||||
pure $
|
pure $
|
||||||
pmatch redeemer $ \case
|
pmatch redeemer $ \case
|
||||||
|
|
@ -347,19 +341,19 @@ governorValidator gov =
|
||||||
.& #createProposalTimeRangeMaxWidth
|
.& #createProposalTimeRangeMaxWidth
|
||||||
.= oldGovernorDatumF.createProposalTimeRangeMaxWidth
|
.= oldGovernorDatumF.createProposalTimeRangeMaxWidth
|
||||||
)
|
)
|
||||||
tcassert "Unexpected governor state datum" $
|
pguardC "Unexpected governor state datum" $
|
||||||
newGovernorDatum #== expectedNewDatum
|
newGovernorDatum #== expectedNewDatum
|
||||||
|
|
||||||
-- Check that exactly one proposal token is being minted.
|
-- Check that exactly one proposal token is being minted.
|
||||||
|
|
||||||
tcassert "Exactly one proposal token must be minted" $
|
pguardC "Exactly one proposal token must be minted" $
|
||||||
hasOnlyOneTokenOfCurrencySymbol # ppstSymbol # txInfoF.mint
|
hasOnlyOneTokenOfCurrencySymbol # ppstSymbol # txInfoF.mint
|
||||||
|
|
||||||
-- Check that a stake is spent to create the propsal,
|
-- Check that a stake is spent to create the propsal,
|
||||||
-- and the value it contains meets the requirement.
|
-- and the value it contains meets the requirement.
|
||||||
|
|
||||||
stakeInput <-
|
stakeInput <-
|
||||||
tclet $
|
pletC $
|
||||||
mustBePJust # "Stake input not found" #$ pfind
|
mustBePJust # "Stake input not found" #$ pfind
|
||||||
# phoistAcyclic
|
# phoistAcyclic
|
||||||
( plam $
|
( plam $
|
||||||
|
|
@ -374,7 +368,7 @@ governorValidator gov =
|
||||||
|
|
||||||
stakeInputF <- tcont $ pletFields @'["datumHash", "value"] $ pfield @"resolved" # stakeInput
|
stakeInputF <- tcont $ pletFields @'["datumHash", "value"] $ pfield @"resolved" # stakeInput
|
||||||
|
|
||||||
tcassert "Stake input doesn't have datum" $
|
pguardC "Stake input doesn't have datum" $
|
||||||
pisDJust # stakeInputF.datumHash
|
pisDJust # stakeInputF.datumHash
|
||||||
|
|
||||||
let stakeInputDatum = mustFindDatum' @PStakeDatum # stakeInputF.datumHash # txInfoF.datums
|
let stakeInputDatum = mustFindDatum' @PStakeDatum # stakeInputF.datumHash # txInfoF.datums
|
||||||
|
|
@ -382,18 +376,18 @@ governorValidator gov =
|
||||||
stakeInputDatumF <-
|
stakeInputDatumF <-
|
||||||
tcont $ pletFields @["stakedAmount", "owner", "lockedBy"] stakeInputDatum
|
tcont $ pletFields @["stakedAmount", "owner", "lockedBy"] stakeInputDatum
|
||||||
|
|
||||||
tcassert "Required amount of stake GTs should be presented" $
|
pguardC "Required amount of stake GTs should be presented" $
|
||||||
stakeInputDatumF.stakedAmount #== (pgtValueOf # stakeInputF.value)
|
stakeInputDatumF.stakedAmount #== (pgtValueOf # stakeInputF.value)
|
||||||
|
|
||||||
-- TODO: Is this required?
|
-- TODO: Is this required?
|
||||||
tcassert "Tx should be signed by the stake owner" $
|
pguardC "Tx should be signed by the stake owner" $
|
||||||
ptxSignedBy # txInfoF.signatories # stakeInputDatumF.owner
|
ptxSignedBy # txInfoF.signatories # stakeInputDatumF.owner
|
||||||
|
|
||||||
-- Check that the newly minted PST is sent to the proposal validator,
|
-- Check that the newly minted PST is sent to the proposal validator,
|
||||||
-- and the datum it carries is legal.
|
-- and the datum it carries is legal.
|
||||||
|
|
||||||
outputsToProposalValidatorWithStateToken <-
|
outputsToProposalValidatorWithStateToken <-
|
||||||
tclet $
|
pletC $
|
||||||
pfilter
|
pfilter
|
||||||
# phoistAcyclic
|
# phoistAcyclic
|
||||||
( plam $
|
( plam $
|
||||||
|
|
@ -406,18 +400,18 @@ governorValidator gov =
|
||||||
)
|
)
|
||||||
# pfromData txInfoF.outputs
|
# pfromData txInfoF.outputs
|
||||||
|
|
||||||
tcassert "Exactly one UTXO with proposal state token should be sent to the proposal validator" $
|
pguardC "Exactly one UTXO with proposal state token should be sent to the proposal validator" $
|
||||||
plength # outputsToProposalValidatorWithStateToken #== 1
|
plength # outputsToProposalValidatorWithStateToken #== 1
|
||||||
|
|
||||||
outputDatumHash <- tclet $ pfield @"datumHash" #$ phead # outputsToProposalValidatorWithStateToken
|
outputDatumHash <- pletC $ pfield @"datumHash" #$ phead # outputsToProposalValidatorWithStateToken
|
||||||
|
|
||||||
proposalOutputDatum' <-
|
proposalOutputDatum' <-
|
||||||
tclet $
|
pletC $
|
||||||
mustFindDatum' @PProposalDatum
|
mustFindDatum' @PProposalDatum
|
||||||
# outputDatumHash
|
# outputDatumHash
|
||||||
# txInfoF.datums
|
# txInfoF.datums
|
||||||
|
|
||||||
tcassert "Proposal datum must be valid" $
|
pguardC "Proposal datum must be valid" $
|
||||||
proposalDatumValid' # proposalOutputDatum'
|
proposalDatumValid' # proposalOutputDatum'
|
||||||
|
|
||||||
proposalOutputDatum <-
|
proposalOutputDatum <-
|
||||||
|
|
@ -426,7 +420,7 @@ governorValidator gov =
|
||||||
@'["effects", "cosigners", "proposalId", "votes"]
|
@'["effects", "cosigners", "proposalId", "votes"]
|
||||||
proposalOutputDatum'
|
proposalOutputDatum'
|
||||||
|
|
||||||
tcassert "Proposal should have only one cosigner" $
|
pguardC "Proposal should have only one cosigner" $
|
||||||
plength # pfromData proposalOutputDatum.cosigners #== 1
|
plength # pfromData proposalOutputDatum.cosigners #== 1
|
||||||
|
|
||||||
let -- Votes should be empty at this point
|
let -- Votes should be empty at this point
|
||||||
|
|
@ -449,17 +443,17 @@ governorValidator gov =
|
||||||
.& #startingTime .= pdata expectedStartingTime
|
.& #startingTime .= pdata expectedStartingTime
|
||||||
)
|
)
|
||||||
|
|
||||||
tcassert "Datum correct" $ expectedProposalOut #== proposalOutputDatum'
|
pguardC "Datum correct" $ expectedProposalOut #== proposalOutputDatum'
|
||||||
|
|
||||||
let cosigner = phead # pfromData proposalOutputDatum.cosigners
|
let cosigner = phead # pfromData proposalOutputDatum.cosigners
|
||||||
|
|
||||||
tcassert "Cosigner should be the stake owner" $
|
pguardC "Cosigner should be the stake owner" $
|
||||||
pdata stakeInputDatumF.owner #== cosigner
|
pdata stakeInputDatumF.owner #== cosigner
|
||||||
|
|
||||||
-- Check the output stake has been proposly updated.
|
-- Check the output stake has been proposly updated.
|
||||||
|
|
||||||
stakeOutput <-
|
stakeOutput <-
|
||||||
tclet $
|
pletC $
|
||||||
mustBePJust
|
mustBePJust
|
||||||
# "Stake output not found"
|
# "Stake output not found"
|
||||||
#$ pfind
|
#$ pfind
|
||||||
|
|
@ -476,7 +470,7 @@ governorValidator gov =
|
||||||
|
|
||||||
stakeOutputF <- tcont $ pletFields @'["datumHash", "value"] $ stakeOutput
|
stakeOutputF <- tcont $ pletFields @'["datumHash", "value"] $ stakeOutput
|
||||||
|
|
||||||
tcassert "Staked GTs should be sent back to stake validator" $
|
pguardC "Staked GTs should be sent back to stake validator" $
|
||||||
stakeInputDatumF.stakedAmount #== (pgtValueOf # stakeOutputF.value)
|
stakeInputDatumF.stakedAmount #== (pgtValueOf # stakeOutputF.value)
|
||||||
|
|
||||||
let stakeOutputDatumHash = mustBePDJust # "Stake output should have datum" # stakeOutputF.datumHash
|
let stakeOutputDatumHash = mustBePDJust # "Stake output should have datum" # stakeOutputF.datumHash
|
||||||
|
|
@ -514,18 +508,18 @@ governorValidator gov =
|
||||||
.& #lockedBy .= pdata expectedProposalLocks
|
.& #lockedBy .= pdata expectedProposalLocks
|
||||||
)
|
)
|
||||||
|
|
||||||
tcassert "Unexpected stake output datum" $ expectedStakeOutputDatum #== stakeOutputDatum
|
pguardC "Unexpected stake output datum" $ expectedStakeOutputDatum #== stakeOutputDatum
|
||||||
|
|
||||||
pure $ popaque $ pconstant ()
|
pure $ popaque $ pconstant ()
|
||||||
|
|
||||||
--------------------------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
|
|
||||||
PMintGATs _ -> unTermCont $ do
|
PMintGATs _ -> unTermCont $ do
|
||||||
tcassert "Governor state should not be changed" $ newGovernorDatum #== oldGovernorDatum
|
pguardC "Governor state should not be changed" $ newGovernorDatum #== oldGovernorDatum
|
||||||
|
|
||||||
-- Filter out proposal inputs and ouputs using PST and the address of proposal validator.
|
-- Filter out proposal inputs and ouputs using PST and the address of proposal validator.
|
||||||
|
|
||||||
tcassert "The governor can only process one proposal at a time" $
|
pguardC "The governor can only process one proposal at a time" $
|
||||||
(psymbolValueOf # ppstSymbol #$ pvalueSpent # txInfoF.inputs) #== 1
|
(psymbolValueOf # ppstSymbol #$ pvalueSpent # txInfoF.inputs) #== 1
|
||||||
|
|
||||||
proposalInputF <-
|
proposalInputF <-
|
||||||
|
|
@ -561,17 +555,17 @@ governorValidator gov =
|
||||||
# pfromData txInfoF.outputs
|
# pfromData txInfoF.outputs
|
||||||
|
|
||||||
proposalInputDatum <-
|
proposalInputDatum <-
|
||||||
tclet $
|
pletC $
|
||||||
mustFindDatum' @PProposalDatum
|
mustFindDatum' @PProposalDatum
|
||||||
# proposalInputF.datumHash
|
# proposalInputF.datumHash
|
||||||
# txInfoF.datums
|
# txInfoF.datums
|
||||||
proposalOutputDatum <-
|
proposalOutputDatum <-
|
||||||
tclet $
|
pletC $
|
||||||
mustFindDatum' @PProposalDatum
|
mustFindDatum' @PProposalDatum
|
||||||
# proposalOutputF.datumHash
|
# proposalOutputF.datumHash
|
||||||
# txInfoF.datums
|
# txInfoF.datums
|
||||||
|
|
||||||
tcassert "Proposal datum must be valid" $
|
pguardC "Proposal datum must be valid" $
|
||||||
proposalDatumValid' # proposalInputDatum
|
proposalDatumValid' # proposalInputDatum
|
||||||
#&& proposalDatumValid' # proposalOutputDatum
|
#&& proposalDatumValid' # proposalOutputDatum
|
||||||
|
|
||||||
|
|
@ -582,7 +576,7 @@ governorValidator gov =
|
||||||
|
|
||||||
-- Check that the proposal state is advanced so that a proposal cannot be executed twice.
|
-- Check that the proposal state is advanced so that a proposal cannot be executed twice.
|
||||||
|
|
||||||
tcassert "Proposal must be in locked(executable) state in order to execute effects" $
|
pguardC "Proposal must be in locked(executable) state in order to execute effects" $
|
||||||
proposalInputDatumF.status #== pconstantData Locked
|
proposalInputDatumF.status #== pconstantData Locked
|
||||||
|
|
||||||
let expectedOutputProposalDatum =
|
let expectedOutputProposalDatum =
|
||||||
|
|
@ -598,7 +592,7 @@ governorValidator gov =
|
||||||
.& #startingTime .= proposalInputDatumF.startingTime
|
.& #startingTime .= proposalInputDatumF.startingTime
|
||||||
)
|
)
|
||||||
|
|
||||||
tcassert "Unexpected output proposal datum" $
|
pguardC "Unexpected output proposal datum" $
|
||||||
pdata proposalOutputDatum #== pdata expectedOutputProposalDatum
|
pdata proposalOutputDatum #== pdata expectedOutputProposalDatum
|
||||||
|
|
||||||
-- TODO: anything else to check here?
|
-- TODO: anything else to check here?
|
||||||
|
|
@ -609,16 +603,16 @@ governorValidator gov =
|
||||||
finalResultTag = pwinner # proposalInputDatumF.votes # quorum # neutralOption
|
finalResultTag = pwinner # proposalInputDatumF.votes # quorum # neutralOption
|
||||||
|
|
||||||
-- The effects of the winner outcome.
|
-- The effects of the winner outcome.
|
||||||
effectGroup <- tclet $ plookup' # finalResultTag #$ proposalInputDatumF.effects
|
effectGroup <- pletC $ plookup' # finalResultTag #$ proposalInputDatumF.effects
|
||||||
|
|
||||||
gatCount <- tclet $ plength #$ pto $ pto effectGroup
|
gatCount <- pletC $ plength #$ pto $ pto effectGroup
|
||||||
|
|
||||||
tcassert "Required amount of GATs should be minted" $
|
pguardC "Required amount of GATs should be minted" $
|
||||||
psymbolValueOf # patSymbol # txInfoF.mint #== gatCount
|
psymbolValueOf # patSymbol # txInfoF.mint #== gatCount
|
||||||
|
|
||||||
-- Ensure that every GAT goes to one of the effects in the winner effect group.
|
-- Ensure that every GAT goes to one of the effects in the winner effect group.
|
||||||
outputsWithGAT <-
|
outputsWithGAT <-
|
||||||
tclet $
|
pletC $
|
||||||
pfilter
|
pfilter
|
||||||
# phoistAcyclic
|
# phoistAcyclic
|
||||||
( plam
|
( plam
|
||||||
|
|
@ -628,7 +622,7 @@ governorValidator gov =
|
||||||
)
|
)
|
||||||
# pfromData txInfoF.outputs
|
# pfromData txInfoF.outputs
|
||||||
|
|
||||||
tcassert "Output GATs is more than minted GATs" $
|
pguardC "Output GATs is more than minted GATs" $
|
||||||
plength # outputsWithGAT #== gatCount
|
plength # outputsWithGAT #== gatCount
|
||||||
|
|
||||||
let gatOutputValidator' :: Term s (PMap _ PValidatorHash PDatumHash :--> PAsData PTxOut :--> PBool)
|
let gatOutputValidator' :: Term s (PMap _ PValidatorHash PDatumHash :--> PAsData PTxOut :--> PBool)
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,23 @@ module Agora.Proposal (
|
||||||
pneutralOption,
|
pneutralOption,
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
import Control.Applicative (Const)
|
||||||
|
import Control.Arrow (first)
|
||||||
|
import Data.Tagged (Tagged)
|
||||||
import GHC.Generics qualified as GHC
|
import GHC.Generics qualified as GHC
|
||||||
import Generics.SOP (Generic, I (I))
|
import Generics.SOP (Generic, I (I))
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
import PlutusLedgerApi.V1 (DatumHash, PubKeyHash, ValidatorHash)
|
||||||
|
import PlutusLedgerApi.V1.Value (AssetClass)
|
||||||
|
import PlutusTx qualified
|
||||||
|
import PlutusTx.AssocMap qualified as AssocMap
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
import Plutarch.Api.V1 (
|
import Plutarch.Api.V1 (
|
||||||
KeyGuarantees (Unsorted),
|
KeyGuarantees (Unsorted),
|
||||||
PDatumHash,
|
PDatumHash,
|
||||||
|
|
@ -44,18 +59,11 @@ import Plutarch.Api.V1 (
|
||||||
PPubKeyHash,
|
PPubKeyHash,
|
||||||
PValidatorHash,
|
PValidatorHash,
|
||||||
)
|
)
|
||||||
import PlutusTx qualified
|
|
||||||
import PlutusTx.AssocMap qualified as AssocMap
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
import Agora.Proposal.Time (PProposalStartingTime, PProposalTimingConfig, ProposalStartingTime, ProposalTimingConfig)
|
|
||||||
import Agora.SafeMoney (GTTag)
|
|
||||||
import Agora.Utils (mustBePJust, pkeysEqual, pmapMap, pnotNull, tclet)
|
|
||||||
import Control.Applicative (Const)
|
|
||||||
import Control.Arrow (first)
|
|
||||||
import Data.Tagged (Tagged)
|
|
||||||
import Plutarch.DataRepr (DerivePConstantViaData (..), PDataFields, PIsDataReprInstances (..))
|
import Plutarch.DataRepr (DerivePConstantViaData (..), PDataFields, PIsDataReprInstances (..))
|
||||||
|
import Plutarch.Extra.List (pnotNull)
|
||||||
|
import Plutarch.Extra.Map qualified as PM
|
||||||
|
import Plutarch.Extra.Map.Unsorted qualified as PUM
|
||||||
|
import Plutarch.Extra.TermCont (pletC)
|
||||||
import Plutarch.Lift (
|
import Plutarch.Lift (
|
||||||
DerivePConstantViaNewtype (..),
|
DerivePConstantViaNewtype (..),
|
||||||
PConstantDecl,
|
PConstantDecl,
|
||||||
|
|
@ -64,8 +72,12 @@ import Plutarch.Lift (
|
||||||
import Plutarch.SafeMoney (PDiscrete)
|
import Plutarch.SafeMoney (PDiscrete)
|
||||||
import Plutarch.TryFrom (PTryFrom (PTryFromExcess, ptryFrom'))
|
import Plutarch.TryFrom (PTryFrom (PTryFromExcess, ptryFrom'))
|
||||||
import Plutarch.Unsafe (punsafeCoerce)
|
import Plutarch.Unsafe (punsafeCoerce)
|
||||||
import PlutusLedgerApi.V1 (DatumHash, PubKeyHash, ValidatorHash)
|
|
||||||
import PlutusLedgerApi.V1.Value (AssetClass)
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
import Agora.Proposal.Time (PProposalStartingTime, PProposalTimingConfig, ProposalStartingTime, ProposalTimingConfig)
|
||||||
|
import Agora.SafeMoney (GTTag)
|
||||||
|
import Agora.Utils (mustBePJust)
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Haskell-land
|
-- Haskell-land
|
||||||
|
|
@ -278,6 +290,10 @@ instance PTryFrom PData (PAsData PResultTag) where
|
||||||
type PTryFromExcess PData (PAsData PResultTag) = PTryFromExcess PData (PAsData PInteger)
|
type PTryFromExcess PData (PAsData PResultTag) = PTryFromExcess PData (PAsData PInteger)
|
||||||
ptryFrom' d k =
|
ptryFrom' d k =
|
||||||
ptryFrom' @_ @(PAsData PInteger) d $
|
ptryFrom' @_ @(PAsData PInteger) d $
|
||||||
|
-- JUSTIFICATION:
|
||||||
|
-- We are coercing from @PAsData PInteger@ to @PAsData PResultTag@.
|
||||||
|
-- Since 'PResultTag' is a simple newtype, their shape is the same.
|
||||||
|
|
||||||
-- JUSTIFICATION:
|
-- JUSTIFICATION:
|
||||||
-- We are coercing from @PAsData PInteger@ to @PAsData PResultTag@.
|
-- We are coercing from @PAsData PInteger@ to @PAsData PResultTag@.
|
||||||
-- Since 'PResultTag' is a simple newtype, their shape is the same.
|
-- Since 'PResultTag' is a simple newtype, their shape is the same.
|
||||||
|
|
@ -291,6 +307,10 @@ instance PTryFrom PData (PAsData PProposalId) where
|
||||||
type PTryFromExcess PData (PAsData PProposalId) = PTryFromExcess PData (PAsData PInteger)
|
type PTryFromExcess PData (PAsData PProposalId) = PTryFromExcess PData (PAsData PInteger)
|
||||||
ptryFrom' d k =
|
ptryFrom' d k =
|
||||||
ptryFrom' @_ @(PAsData PInteger) d $
|
ptryFrom' @_ @(PAsData PInteger) d $
|
||||||
|
-- JUSTIFICATION:
|
||||||
|
-- We are coercing from @PAsData PInteger@ to @PAsData PProposalId@.
|
||||||
|
-- Since 'PProposalId' is a simple newtype, their shape is the same.
|
||||||
|
|
||||||
-- JUSTIFICATION:
|
-- JUSTIFICATION:
|
||||||
-- We are coercing from @PAsData PInteger@ to @PAsData PProposalId@.
|
-- We are coercing from @PAsData PInteger@ to @PAsData PProposalId@.
|
||||||
-- Since 'PProposalId' is a simple newtype, their shape is the same.
|
-- Since 'PProposalId' is a simple newtype, their shape is the same.
|
||||||
|
|
@ -360,7 +380,7 @@ pemptyVotesFor =
|
||||||
plam
|
plam
|
||||||
( \m ->
|
( \m ->
|
||||||
pcon $
|
pcon $
|
||||||
PProposalVotes $ pmapMap # plam (const $ pconstant 0) # m
|
PProposalVotes $ PM.pmap # plam (const $ pconstant 0) # m
|
||||||
)
|
)
|
||||||
|
|
||||||
-- | Plutarch-level version of 'ProposalDatum'.
|
-- | Plutarch-level version of 'ProposalDatum'.
|
||||||
|
|
@ -448,8 +468,8 @@ proposalDatumValid proposal =
|
||||||
(#&&)
|
(#&&)
|
||||||
[ ptraceIfFalse "Proposal has at least one ResultTag has no effects" atLeastOneNegativeResult
|
[ ptraceIfFalse "Proposal has at least one ResultTag has no effects" atLeastOneNegativeResult
|
||||||
, ptraceIfFalse "Proposal has at least one cosigner" $ pnotNull # pfromData datum.cosigners
|
, ptraceIfFalse "Proposal has at least one cosigner" $ pnotNull # pfromData datum.cosigners
|
||||||
, ptraceIfFalse "Proposal has fewer cosigners than the limit" $ plength # (pfromData datum.cosigners) #<= pconstant proposal.maximumCosigners
|
, ptraceIfFalse "Proposal has fewer cosigners than the limit" $ plength # pfromData datum.cosigners #<= pconstant proposal.maximumCosigners
|
||||||
, ptraceIfFalse "Proposal votes and effects are compatible with each other" $ pkeysEqual # datum.effects # pto (pfromData datum.votes)
|
, ptraceIfFalse "Proposal votes and effects are compatible with each other" $ PUM.pkeysEqual # datum.effects # pto (pfromData datum.votes)
|
||||||
]
|
]
|
||||||
|
|
||||||
{- | Find the winner result tag, given the votes, the quorum the "neutral" result tag.
|
{- | Find the winner result tag, given the votes, the quorum the "neutral" result tag.
|
||||||
|
|
@ -467,9 +487,9 @@ pwinner ::
|
||||||
)
|
)
|
||||||
pwinner = phoistAcyclic $
|
pwinner = phoistAcyclic $
|
||||||
plam $ \votes quorum neutral -> unTermCont $ do
|
plam $ \votes quorum neutral -> unTermCont $ do
|
||||||
winner <- tclet $ phighestVotes # votes
|
winner <- pletC $ phighestVotes # votes
|
||||||
winnerResultTag <- tclet $ pfromData $ pfstBuiltin # winner
|
winnerResultTag <- pletC $ pfromData $ pfstBuiltin # winner
|
||||||
highestVotes <- tclet $ pfromData $ psndBuiltin # winner
|
highestVotes <- pletC $ pfromData $ psndBuiltin # winner
|
||||||
|
|
||||||
let l :: Term _ (PBuiltinList _)
|
let l :: Term _ (PBuiltinList _)
|
||||||
l = pto $ pto votes
|
l = pto $ pto votes
|
||||||
|
|
|
||||||
|
|
@ -31,17 +31,6 @@ import Agora.Utils (
|
||||||
getMintingPolicySymbol,
|
getMintingPolicySymbol,
|
||||||
mustBePJust,
|
mustBePJust,
|
||||||
mustFindDatum',
|
mustFindDatum',
|
||||||
pisJust,
|
|
||||||
pisUniqBy,
|
|
||||||
psymbolValueOf,
|
|
||||||
ptokenSpent,
|
|
||||||
ptxSignedBy,
|
|
||||||
pupdate,
|
|
||||||
pvalueSpent,
|
|
||||||
tcassert,
|
|
||||||
tclet,
|
|
||||||
tcmatch,
|
|
||||||
tctryFrom,
|
|
||||||
)
|
)
|
||||||
import Plutarch.Api.V1 (
|
import Plutarch.Api.V1 (
|
||||||
PMintingPolicy,
|
PMintingPolicy,
|
||||||
|
|
@ -51,10 +40,23 @@ import Plutarch.Api.V1 (
|
||||||
PValidator,
|
PValidator,
|
||||||
)
|
)
|
||||||
import Plutarch.Api.V1.AssetClass (passetClass, passetClassValueOf)
|
import Plutarch.Api.V1.AssetClass (passetClass, passetClassValueOf)
|
||||||
|
import Plutarch.Api.V1.ScriptContext (
|
||||||
|
pisTokenSpent,
|
||||||
|
ptxSignedBy,
|
||||||
|
pvalueSpent,
|
||||||
|
)
|
||||||
|
import "liqwid-plutarch-extra" Plutarch.Api.V1.Value (psymbolValueOf)
|
||||||
import Plutarch.Extra.Comonad (pextract)
|
import Plutarch.Extra.Comonad (pextract)
|
||||||
import Plutarch.Extra.Map (plookup)
|
import Plutarch.Extra.List (pisUniqBy)
|
||||||
|
import Plutarch.Extra.Map (plookup, pupdate)
|
||||||
|
import Plutarch.Extra.Maybe (pisJust)
|
||||||
import Plutarch.Extra.Record (mkRecordConstr, (.&), (.=))
|
import Plutarch.Extra.Record (mkRecordConstr, (.&), (.=))
|
||||||
import Plutarch.Extra.TermCont (pmatchC)
|
import Plutarch.Extra.TermCont (
|
||||||
|
pguardC,
|
||||||
|
pletC,
|
||||||
|
pmatchC,
|
||||||
|
ptryFromC,
|
||||||
|
)
|
||||||
import Plutarch.SafeMoney (PDiscrete (..))
|
import Plutarch.SafeMoney (PDiscrete (..))
|
||||||
import PlutusLedgerApi.V1.Value (AssetClass (AssetClass))
|
import PlutusLedgerApi.V1.Value (AssetClass (AssetClass))
|
||||||
|
|
||||||
|
|
@ -82,27 +84,27 @@ proposalPolicy ::
|
||||||
ClosedTerm PMintingPolicy
|
ClosedTerm PMintingPolicy
|
||||||
proposalPolicy (AssetClass (govCs, govTn)) =
|
proposalPolicy (AssetClass (govCs, govTn)) =
|
||||||
plam $ \_redeemer ctx' -> unTermCont $ do
|
plam $ \_redeemer ctx' -> unTermCont $ do
|
||||||
PScriptContext ctx' <- tcmatch ctx'
|
PScriptContext ctx' <- pmatchC ctx'
|
||||||
ctx <- tcont $ pletFields @'["txInfo", "purpose"] ctx'
|
ctx <- tcont $ pletFields @'["txInfo", "purpose"] ctx'
|
||||||
PTxInfo txInfo' <- tcmatch $ pfromData ctx.txInfo
|
PTxInfo txInfo' <- pmatchC $ pfromData ctx.txInfo
|
||||||
txInfo <- tcont $ pletFields @'["inputs", "mint"] txInfo'
|
txInfo <- tcont $ pletFields @'["inputs", "mint"] txInfo'
|
||||||
PMinting _ownSymbol <- tcmatch $ pfromData ctx.purpose
|
PMinting _ownSymbol <- pmatchC $ pfromData ctx.purpose
|
||||||
|
|
||||||
let inputs = txInfo.inputs
|
let inputs = txInfo.inputs
|
||||||
mintedValue = pfromData txInfo.mint
|
mintedValue = pfromData txInfo.mint
|
||||||
|
|
||||||
PMinting ownSymbol' <- tcmatch $ pfromData ctx.purpose
|
PMinting ownSymbol' <- pmatchC $ pfromData ctx.purpose
|
||||||
let mintedProposalST =
|
let mintedProposalST =
|
||||||
passetClassValueOf
|
passetClassValueOf
|
||||||
# mintedValue
|
# mintedValue
|
||||||
# (passetClass # (pfield @"_0" # ownSymbol') # pconstant "")
|
# (passetClass # (pfield @"_0" # ownSymbol') # pconstant "")
|
||||||
|
|
||||||
tcassert "Governance state-thread token must move" $
|
pguardC "Governance state-thread token must move" $
|
||||||
ptokenSpent
|
pisTokenSpent
|
||||||
# (passetClass # pconstant govCs # pconstant govTn)
|
# (passetClass # pconstant govCs # pconstant govTn)
|
||||||
# inputs
|
# inputs
|
||||||
|
|
||||||
tcassert "Minted exactly one proposal ST" $
|
pguardC "Minted exactly one proposal ST" $
|
||||||
mintedProposalST #== 1
|
mintedProposalST #== 1
|
||||||
|
|
||||||
pure $ popaque (pconstant ())
|
pure $ popaque (pconstant ())
|
||||||
|
|
@ -136,10 +138,10 @@ A list of all time-sensitive redeemers and their requirements:
|
||||||
proposalValidator :: Proposal -> ClosedTerm PValidator
|
proposalValidator :: Proposal -> ClosedTerm PValidator
|
||||||
proposalValidator proposal =
|
proposalValidator proposal =
|
||||||
plam $ \datum redeemer ctx' -> unTermCont $ do
|
plam $ \datum redeemer ctx' -> unTermCont $ do
|
||||||
PScriptContext ctx' <- tcmatch ctx'
|
PScriptContext ctx' <- pmatchC ctx'
|
||||||
ctx <- tcont $ pletFields @'["txInfo", "purpose"] ctx'
|
ctx <- tcont $ pletFields @'["txInfo", "purpose"] ctx'
|
||||||
txInfo <- tclet $ pfromData ctx.txInfo
|
txInfo <- pletC $ pfromData ctx.txInfo
|
||||||
PTxInfo txInfo' <- tcmatch txInfo
|
PTxInfo txInfo' <- pmatchC txInfo
|
||||||
txInfoF <-
|
txInfoF <-
|
||||||
tcont $
|
tcont $
|
||||||
pletFields
|
pletFields
|
||||||
|
|
@ -151,15 +153,15 @@ proposalValidator proposal =
|
||||||
, "validRange"
|
, "validRange"
|
||||||
]
|
]
|
||||||
txInfo'
|
txInfo'
|
||||||
PSpending ((pfield @"_0" #) -> txOutRef) <- tcmatch $ pfromData ctx.purpose
|
PSpending ((pfield @"_0" #) -> txOutRef) <- pmatchC $ pfromData ctx.purpose
|
||||||
|
|
||||||
PJust txOut <- tcmatch $ findTxOutByTxOutRef # txOutRef # txInfoF.inputs
|
PJust txOut <- pmatchC $ findTxOutByTxOutRef # txOutRef # txInfoF.inputs
|
||||||
txOutF <- tcont $ pletFields @'["address", "value"] $ txOut
|
txOutF <- tcont $ pletFields @'["address", "value"] $ txOut
|
||||||
|
|
||||||
(pfromData -> proposalDatum, _) <-
|
(pfromData -> proposalDatum, _) <-
|
||||||
tctryFrom @(PAsData PProposalDatum) datum
|
ptryFromC @(PAsData PProposalDatum) datum
|
||||||
(pfromData -> proposalRedeemer, _) <-
|
(pfromData -> proposalRedeemer, _) <-
|
||||||
tctryFrom @(PAsData PProposalRedeemer) redeemer
|
ptryFromC @(PAsData PProposalRedeemer) redeemer
|
||||||
|
|
||||||
proposalF <-
|
proposalF <-
|
||||||
tcont $
|
tcont $
|
||||||
|
|
@ -175,29 +177,29 @@ proposalValidator proposal =
|
||||||
]
|
]
|
||||||
proposalDatum
|
proposalDatum
|
||||||
|
|
||||||
ownAddress <- tclet $ txOutF.address
|
ownAddress <- pletC $ txOutF.address
|
||||||
|
|
||||||
let stCurrencySymbol =
|
let stCurrencySymbol =
|
||||||
pconstant $ getMintingPolicySymbol (proposalPolicy proposal.governorSTAssetClass)
|
pconstant $ getMintingPolicySymbol (proposalPolicy proposal.governorSTAssetClass)
|
||||||
valueSpent <- tclet $ pvalueSpent # txInfoF.inputs
|
valueSpent <- pletC $ pvalueSpent # txInfoF.inputs
|
||||||
spentST <- tclet $ psymbolValueOf # stCurrencySymbol #$ valueSpent
|
spentST <- pletC $ psymbolValueOf # stCurrencySymbol #$ valueSpent
|
||||||
|
|
||||||
let AssetClass (stakeSym, stakeTn) = proposal.stakeSTAssetClass
|
let AssetClass (stakeSym, stakeTn) = proposal.stakeSTAssetClass
|
||||||
stakeSTAssetClass <-
|
stakeSTAssetClass <-
|
||||||
tclet $ passetClass # pconstant stakeSym # pconstant stakeTn
|
pletC $ passetClass # pconstant stakeSym # pconstant stakeTn
|
||||||
spentStakeST <-
|
spentStakeST <-
|
||||||
tclet $ passetClassValueOf # valueSpent # stakeSTAssetClass
|
pletC $ passetClassValueOf # valueSpent # stakeSTAssetClass
|
||||||
|
|
||||||
signedBy <- tclet $ ptxSignedBy # txInfoF.signatories
|
signedBy <- pletC $ ptxSignedBy # txInfoF.signatories
|
||||||
|
|
||||||
tcassert "ST at inputs must be 1" (spentST #== 1)
|
pguardC "ST at inputs must be 1" (spentST #== 1)
|
||||||
|
|
||||||
currentTime <- tclet $ currentProposalTime # txInfoF.validRange
|
currentTime <- pletC $ currentProposalTime # txInfoF.validRange
|
||||||
|
|
||||||
-- Filter out own output with own address and PST.
|
-- Filter out own output with own address and PST.
|
||||||
-- Delay the evaluation cause in some cases there won't be any continuing output.
|
-- Delay the evaluation cause in some cases there won't be any continuing output.
|
||||||
ownOutput <-
|
ownOutput <-
|
||||||
tclet $
|
pletC $
|
||||||
mustBePJust # "Own output should be present" #$ pfind
|
mustBePJust # "Own output should be present" #$ pfind
|
||||||
# plam
|
# plam
|
||||||
( \input -> unTermCont $ do
|
( \input -> unTermCont $ do
|
||||||
|
|
@ -209,7 +211,7 @@ proposalValidator proposal =
|
||||||
# pfromData txInfoF.outputs
|
# pfromData txInfoF.outputs
|
||||||
|
|
||||||
proposalOut <-
|
proposalOut <-
|
||||||
tclet $
|
pletC $
|
||||||
mustFindDatum' @PProposalDatum
|
mustFindDatum' @PProposalDatum
|
||||||
# (pfield @"datumHash" # ownOutput)
|
# (pfield @"datumHash" # ownOutput)
|
||||||
# txInfoF.datums
|
# txInfoF.datums
|
||||||
|
|
@ -217,17 +219,17 @@ proposalValidator proposal =
|
||||||
pure $
|
pure $
|
||||||
pmatch proposalRedeemer $ \case
|
pmatch proposalRedeemer $ \case
|
||||||
PVote r -> unTermCont $ do
|
PVote r -> unTermCont $ do
|
||||||
tcassert "Input proposal must be in VotingReady state" $
|
pguardC "Input proposal must be in VotingReady state" $
|
||||||
proposalF.status #== pconstant VotingReady
|
proposalF.status #== pconstant VotingReady
|
||||||
|
|
||||||
tcassert "Proposal time should be wthin the voting period" $
|
pguardC "Proposal time should be wthin the voting period" $
|
||||||
isVotingPeriod # proposalF.timingConfig # proposalF.startingTime # currentTime
|
isVotingPeriod # proposalF.timingConfig # proposalF.startingTime # currentTime
|
||||||
|
|
||||||
-- Ensure the transaction is voting to a valid 'ResultTag'(outcome).
|
-- Ensure the transaction is voting to a valid 'ResultTag'(outcome).
|
||||||
PProposalVotes voteMap <- tcmatch proposalF.votes
|
PProposalVotes voteMap <- pmatchC proposalF.votes
|
||||||
voteFor <- tclet $ pfromData $ pfield @"resultTag" # r
|
voteFor <- pletC $ pfromData $ pfield @"resultTag" # r
|
||||||
|
|
||||||
tcassert "Vote option should be valid" $
|
pguardC "Vote option should be valid" $
|
||||||
pisJust #$ plookup # voteFor # voteMap
|
pisJust #$ plookup # voteFor # voteMap
|
||||||
|
|
||||||
-- Find the input stake, the amount of new votes should be the 'stakedAmount'.
|
-- Find the input stake, the amount of new votes should be the 'stakedAmount'.
|
||||||
|
|
@ -248,7 +250,7 @@ proposalValidator proposal =
|
||||||
stakeInF <- tcont $ pletFields @'["stakedAmount", "lockedBy", "owner"] stakeIn
|
stakeInF <- tcont $ pletFields @'["stakedAmount", "lockedBy", "owner"] stakeIn
|
||||||
|
|
||||||
-- Ensure that no lock with the current proposal id has been put on the stake.
|
-- Ensure that no lock with the current proposal id has been put on the stake.
|
||||||
tcassert "Same stake shouldn't vote on the same propsoal twice" $
|
pguardC "Same stake shouldn't vote on the same propsoal twice" $
|
||||||
pnot #$ pany
|
pnot #$ pany
|
||||||
# plam
|
# plam
|
||||||
( \((pfield @"proposalTag" #) . pfromData -> pid) ->
|
( \((pfield @"proposalTag" #) . pfromData -> pid) ->
|
||||||
|
|
@ -281,7 +283,7 @@ proposalValidator proposal =
|
||||||
.& #startingTime .= proposalF.startingTime
|
.& #startingTime .= proposalF.startingTime
|
||||||
)
|
)
|
||||||
|
|
||||||
tcassert "Output proposal should be valid" $ proposalOut #== expectedProposalOut
|
pguardC "Output proposal should be valid" $ proposalOut #== expectedProposalOut
|
||||||
|
|
||||||
-- We validate the output stake datum here as well: We need the vote option
|
-- We validate the output stake datum here as well: We need the vote option
|
||||||
-- to create a valid 'ProposalLock', however the vote option is encoded
|
-- to create a valid 'ProposalLock', however the vote option is encoded
|
||||||
|
|
@ -318,26 +320,26 @@ proposalValidator proposal =
|
||||||
.& #lockedBy .= pdata expectedProposalLocks
|
.& #lockedBy .= pdata expectedProposalLocks
|
||||||
)
|
)
|
||||||
|
|
||||||
tcassert "Output stake should be locked by the proposal" $ expectedStakeOut #== stakeOut
|
pguardC "Output stake should be locked by the proposal" $ expectedStakeOut #== stakeOut
|
||||||
|
|
||||||
pure $ popaque (pconstant ())
|
pure $ popaque (pconstant ())
|
||||||
--------------------------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
PCosign r -> unTermCont $ do
|
PCosign r -> unTermCont $ do
|
||||||
newSigs <- tclet $ pfield @"newCosigners" # r
|
newSigs <- pletC $ pfield @"newCosigners" # r
|
||||||
|
|
||||||
tcassert "Cosigners are unique" $
|
pguardC "Cosigners are unique" $
|
||||||
pisUniqBy
|
pisUniqBy
|
||||||
# phoistAcyclic (plam (#==))
|
# phoistAcyclic (plam (#==))
|
||||||
# phoistAcyclic (plam $ \(pfromData -> x) (pfromData -> y) -> x #< y)
|
# phoistAcyclic (plam $ \(pfromData -> x) (pfromData -> y) -> x #< y)
|
||||||
# newSigs
|
# newSigs
|
||||||
|
|
||||||
tcassert "Signed by all new cosigners" $
|
pguardC "Signed by all new cosigners" $
|
||||||
pall # signedBy # newSigs
|
pall # signedBy # newSigs
|
||||||
|
|
||||||
tcassert "As many new cosigners as Stake datums" $
|
pguardC "As many new cosigners as Stake datums" $
|
||||||
spentStakeST #== plength # newSigs
|
spentStakeST #== plength # newSigs
|
||||||
|
|
||||||
tcassert "All new cosigners are witnessed by their Stake datums" $
|
pguardC "All new cosigners are witnessed by their Stake datums" $
|
||||||
pall
|
pall
|
||||||
# plam
|
# plam
|
||||||
( \sig ->
|
( \sig ->
|
||||||
|
|
@ -367,7 +369,7 @@ proposalValidator proposal =
|
||||||
.& #startingTime .= proposalF.startingTime
|
.& #startingTime .= proposalF.startingTime
|
||||||
)
|
)
|
||||||
|
|
||||||
tcassert "Signatures are correctly added to cosignature list" $
|
pguardC "Signatures are correctly added to cosignature list" $
|
||||||
proposalOut #== expectedDatum
|
proposalOut #== expectedDatum
|
||||||
|
|
||||||
pure $ popaque (pconstant ())
|
pure $ popaque (pconstant ())
|
||||||
|
|
@ -376,10 +378,10 @@ proposalValidator proposal =
|
||||||
popaque (pconstant ())
|
popaque (pconstant ())
|
||||||
--------------------------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
PAdvanceProposal _r -> unTermCont $ do
|
PAdvanceProposal _r -> unTermCont $ do
|
||||||
tcassert "No stake input is allowed" $ spentStakeST #== 0
|
pguardC "No stake input is allowed" $ spentStakeST #== 0
|
||||||
|
|
||||||
currentTime <- tclet $ currentProposalTime # txInfoF.validRange
|
currentTime <- pletC $ currentProposalTime # txInfoF.validRange
|
||||||
proposalOutStatus <- tclet $ pfield @"status" # proposalOut
|
proposalOutStatus <- pletC $ pfield @"status" # proposalOut
|
||||||
|
|
||||||
let -- Only the status of proposals should be updated in this case.
|
let -- Only the status of proposals should be updated in this case.
|
||||||
templateProposalOut =
|
templateProposalOut =
|
||||||
|
|
@ -395,13 +397,13 @@ proposalValidator proposal =
|
||||||
.& #startingTime .= proposalF.startingTime
|
.& #startingTime .= proposalF.startingTime
|
||||||
)
|
)
|
||||||
|
|
||||||
tcassert "Only status changes in the output proposal" $
|
pguardC "Only status changes in the output proposal" $
|
||||||
templateProposalOut #== proposalOut
|
templateProposalOut #== proposalOut
|
||||||
|
|
||||||
inDraftPeriod <- tclet $ isDraftPeriod # proposalF.timingConfig # proposalF.startingTime # currentTime
|
inDraftPeriod <- pletC $ isDraftPeriod # proposalF.timingConfig # proposalF.startingTime # currentTime
|
||||||
inVotingPeriod <- tclet $ isVotingPeriod # proposalF.timingConfig # proposalF.startingTime # currentTime
|
inVotingPeriod <- pletC $ isVotingPeriod # proposalF.timingConfig # proposalF.startingTime # currentTime
|
||||||
inLockedPeriod <- tclet $ isLockingPeriod # proposalF.timingConfig # proposalF.startingTime # currentTime
|
inLockedPeriod <- pletC $ isLockingPeriod # proposalF.timingConfig # proposalF.startingTime # currentTime
|
||||||
inExecutionPeriod <- tclet $ isExecutionPeriod # proposalF.timingConfig # proposalF.startingTime # currentTime
|
inExecutionPeriod <- pletC $ isExecutionPeriod # proposalF.timingConfig # proposalF.startingTime # currentTime
|
||||||
|
|
||||||
-- Check the timings.
|
-- Check the timings.
|
||||||
let isFinished = proposalF.status #== pconstantData Finished
|
let isFinished = proposalF.status #== pconstantData Finished
|
||||||
|
|
@ -418,8 +420,8 @@ proposalValidator proposal =
|
||||||
PLocked _ -> pnot # inLockedPeriod
|
PLocked _ -> pnot # inLockedPeriod
|
||||||
_ -> pconstant True
|
_ -> pconstant True
|
||||||
|
|
||||||
tcassert "Cannot advance ahead of time" notTooEarly
|
pguardC "Cannot advance ahead of time" notTooEarly
|
||||||
tcassert "Finished proposals cannot be advanced" $ pnot # isFinished
|
pguardC "Finished proposals cannot be advanced" $ pnot # isFinished
|
||||||
|
|
||||||
pure $
|
pure $
|
||||||
pif
|
pif
|
||||||
|
|
@ -430,19 +432,19 @@ proposalValidator proposal =
|
||||||
-- TODO: Perform other necessary checks.
|
-- TODO: Perform other necessary checks.
|
||||||
|
|
||||||
-- 'Draft' -> 'VotingReady'
|
-- 'Draft' -> 'VotingReady'
|
||||||
tcassert "Proposal status set to VotingReady" $
|
pguardC "Proposal status set to VotingReady" $
|
||||||
proposalOutStatus #== pconstantData VotingReady
|
proposalOutStatus #== pconstantData VotingReady
|
||||||
|
|
||||||
pure $ popaque (pconstant ())
|
pure $ popaque (pconstant ())
|
||||||
PVotingReady _ -> unTermCont $ do
|
PVotingReady _ -> unTermCont $ do
|
||||||
-- 'VotingReady' -> 'Locked'
|
-- 'VotingReady' -> 'Locked'
|
||||||
tcassert "Proposal status set to Locked" $
|
pguardC "Proposal status set to Locked" $
|
||||||
proposalOutStatus #== pconstantData Locked
|
proposalOutStatus #== pconstantData Locked
|
||||||
|
|
||||||
pure $ popaque (pconstant ())
|
pure $ popaque (pconstant ())
|
||||||
PLocked _ -> unTermCont $ do
|
PLocked _ -> unTermCont $ do
|
||||||
-- 'Locked' -> 'Finished'
|
-- 'Locked' -> 'Finished'
|
||||||
tcassert "Proposal status set to Finished" $
|
pguardC "Proposal status set to Finished" $
|
||||||
proposalOutStatus #== pconstantData Finished
|
proposalOutStatus #== pconstantData Finished
|
||||||
|
|
||||||
-- TODO: Perform other necessary checks.
|
-- TODO: Perform other necessary checks.
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ module Agora.Proposal.Time (
|
||||||
isExecutionPeriod,
|
isExecutionPeriod,
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Agora.Utils (tcassert, tcmatch)
|
|
||||||
import GHC.Generics qualified as GHC
|
import GHC.Generics qualified as GHC
|
||||||
import Generics.SOP (Generic, HasDatatypeInfo, I (I))
|
import Generics.SOP (Generic, HasDatatypeInfo, I (I))
|
||||||
import Plutarch.Api.V1 (
|
import Plutarch.Api.V1 (
|
||||||
|
|
@ -45,6 +44,7 @@ import Plutarch.DataRepr (
|
||||||
PDataFields,
|
PDataFields,
|
||||||
PIsDataReprInstances (..),
|
PIsDataReprInstances (..),
|
||||||
)
|
)
|
||||||
|
import Plutarch.Extra.TermCont (pguardC, pmatchC)
|
||||||
import Plutarch.Lift (
|
import Plutarch.Lift (
|
||||||
DerivePConstantViaNewtype (..),
|
DerivePConstantViaNewtype (..),
|
||||||
PConstantDecl,
|
PConstantDecl,
|
||||||
|
|
@ -184,7 +184,7 @@ instance AdditiveSemigroup (Term s PPOSIXTime) where
|
||||||
createProposalStartingTime :: forall (s :: S). Term s (PMaxTimeRangeWidth :--> PPOSIXTimeRange :--> PProposalStartingTime)
|
createProposalStartingTime :: forall (s :: S). Term s (PMaxTimeRangeWidth :--> PPOSIXTimeRange :--> PProposalStartingTime)
|
||||||
createProposalStartingTime = phoistAcyclic $
|
createProposalStartingTime = phoistAcyclic $
|
||||||
plam $ \(pto -> maxDuration) iv -> unTermCont $ do
|
plam $ \(pto -> maxDuration) iv -> unTermCont $ do
|
||||||
currentTimeF <- tcmatch $ currentProposalTime # iv
|
currentTimeF <- pmatchC $ currentProposalTime # iv
|
||||||
|
|
||||||
-- Use the middle of the current time range as the starting time.
|
-- Use the middle of the current time range as the starting time.
|
||||||
let duration = currentTimeF.upperBound - currentTimeF.lowerBound
|
let duration = currentTimeF.upperBound - currentTimeF.lowerBound
|
||||||
|
|
@ -194,7 +194,7 @@ createProposalStartingTime = phoistAcyclic $
|
||||||
# (currentTimeF.lowerBound + currentTimeF.upperBound)
|
# (currentTimeF.lowerBound + currentTimeF.upperBound)
|
||||||
# 2
|
# 2
|
||||||
|
|
||||||
tcassert "createProposalStartingTime: given time range should be tight enough" $
|
pguardC "createProposalStartingTime: given time range should be tight enough" $
|
||||||
duration #<= maxDuration
|
duration #<= maxDuration
|
||||||
|
|
||||||
pure $ pcon $ PProposalStartingTime startingTime
|
pure $ pcon $ PProposalStartingTime startingTime
|
||||||
|
|
@ -207,10 +207,10 @@ createProposalStartingTime = phoistAcyclic $
|
||||||
currentProposalTime :: forall (s :: S). Term s (PPOSIXTimeRange :--> PProposalTime)
|
currentProposalTime :: forall (s :: S). Term s (PPOSIXTimeRange :--> PProposalTime)
|
||||||
currentProposalTime = phoistAcyclic $
|
currentProposalTime = phoistAcyclic $
|
||||||
plam $ \iv -> unTermCont $ do
|
plam $ \iv -> unTermCont $ do
|
||||||
PInterval iv' <- tcmatch iv
|
PInterval iv' <- pmatchC iv
|
||||||
ivf <- tcont $ pletFields @'["from", "to"] iv'
|
ivf <- tcont $ pletFields @'["from", "to"] iv'
|
||||||
PLowerBound lb <- tcmatch ivf.from
|
PLowerBound lb <- pmatchC ivf.from
|
||||||
PUpperBound ub <- tcmatch ivf.to
|
PUpperBound ub <- pmatchC ivf.to
|
||||||
lbf <- tcont $ pletFields @'["_0", "_1"] lb
|
lbf <- tcont $ pletFields @'["_0", "_1"] lb
|
||||||
ubf <- tcont $ pletFields @'["_0", "_1"] ub
|
ubf <- tcont $ pletFields @'["_0", "_1"] ub
|
||||||
pure $
|
pure $
|
||||||
|
|
@ -243,7 +243,7 @@ proposalTimeWithin ::
|
||||||
)
|
)
|
||||||
proposalTimeWithin = phoistAcyclic $
|
proposalTimeWithin = phoistAcyclic $
|
||||||
plam $ \l h proposalTime' -> unTermCont $ do
|
plam $ \l h proposalTime' -> unTermCont $ do
|
||||||
PProposalTime ut lt <- tcmatch proposalTime'
|
PProposalTime ut lt <- pmatchC proposalTime'
|
||||||
pure $
|
pure $
|
||||||
foldr1
|
foldr1
|
||||||
(#&&)
|
(#&&)
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,8 @@ module Agora.Stake (
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
import Control.Applicative (Const)
|
||||||
|
import Data.Tagged (Tagged (..))
|
||||||
import GHC.Generics qualified as GHC
|
import GHC.Generics qualified as GHC
|
||||||
import Generics.SOP (Generic, I (I))
|
import Generics.SOP (Generic, I (I))
|
||||||
import Prelude hiding (Num (..))
|
import Prelude hiding (Num (..))
|
||||||
|
|
@ -33,6 +35,7 @@ import Prelude hiding (Num (..))
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
import PlutusLedgerApi.V1 (PubKeyHash)
|
import PlutusLedgerApi.V1 (PubKeyHash)
|
||||||
|
import PlutusLedgerApi.V1.Value (AssetClass)
|
||||||
import PlutusTx qualified
|
import PlutusTx qualified
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
@ -46,30 +49,24 @@ import Plutarch.Api.V1 (
|
||||||
PTxInInfo (PTxInInfo),
|
PTxInInfo (PTxInInfo),
|
||||||
PTxOut (PTxOut),
|
PTxOut (PTxOut),
|
||||||
)
|
)
|
||||||
|
import Plutarch.Api.V1.AssetClass (PAssetClass, passetClassValueOf)
|
||||||
|
import Plutarch.Api.V1.ScriptContext (ptryFindDatum)
|
||||||
import Plutarch.DataRepr (
|
import Plutarch.DataRepr (
|
||||||
DerivePConstantViaData (..),
|
DerivePConstantViaData (..),
|
||||||
PDataFields,
|
PDataFields,
|
||||||
PIsDataReprInstances (PIsDataReprInstances),
|
PIsDataReprInstances (PIsDataReprInstances),
|
||||||
)
|
)
|
||||||
|
import Plutarch.Extra.List (pnotNull)
|
||||||
|
import Plutarch.Extra.TermCont (pletC, pmatchC)
|
||||||
import Plutarch.Internal (punsafeCoerce)
|
import Plutarch.Internal (punsafeCoerce)
|
||||||
import Plutarch.Lift (PConstantDecl, PUnsafeLiftDecl (..))
|
import Plutarch.Lift (PConstantDecl, PUnsafeLiftDecl (..))
|
||||||
import PlutusLedgerApi.V1.Value (AssetClass)
|
import Plutarch.SafeMoney (PDiscrete)
|
||||||
|
import Plutarch.TryFrom (PTryFrom (PTryFromExcess, ptryFrom'))
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
import Agora.Proposal (PProposalId, PResultTag, ProposalId (..), ResultTag (..))
|
import Agora.Proposal (PProposalId, PResultTag, ProposalId (..), ResultTag (..))
|
||||||
import Agora.SafeMoney (GTTag)
|
import Agora.SafeMoney (GTTag)
|
||||||
import Agora.Utils (
|
|
||||||
pnotNull,
|
|
||||||
ptryFindDatum,
|
|
||||||
tclet,
|
|
||||||
tcmatch,
|
|
||||||
)
|
|
||||||
import Control.Applicative (Const)
|
|
||||||
import Data.Tagged (Tagged (..))
|
|
||||||
import Plutarch.Api.V1.AssetClass (PAssetClass, passetClassValueOf)
|
|
||||||
import Plutarch.SafeMoney (PDiscrete)
|
|
||||||
import Plutarch.TryFrom (PTryFrom (PTryFromExcess, ptryFrom'))
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
@ -300,10 +297,10 @@ isInputStakeOwnedBy ::
|
||||||
)
|
)
|
||||||
isInputStakeOwnedBy =
|
isInputStakeOwnedBy =
|
||||||
plam $ \ac ss datums txInInfo' -> unTermCont $ do
|
plam $ \ac ss datums txInInfo' -> unTermCont $ do
|
||||||
PTxInInfo ((pfield @"resolved" #) -> txOut) <- tcmatch $ pfromData txInInfo'
|
PTxInInfo ((pfield @"resolved" #) -> txOut) <- pmatchC $ pfromData txInInfo'
|
||||||
PTxOut txOut' <- tcmatch txOut
|
PTxOut txOut' <- pmatchC txOut
|
||||||
txOutF <- tcont $ pletFields @'["value", "datumHash"] txOut'
|
txOutF <- tcont $ pletFields @'["value", "datumHash"] txOut'
|
||||||
outStakeST <- tclet $ passetClassValueOf # txOutF.value # ac
|
outStakeST <- pletC $ passetClassValueOf # txOutF.value # ac
|
||||||
pure $
|
pure $
|
||||||
pmatch txOutF.datumHash $ \case
|
pmatch txOutF.datumHash $ \case
|
||||||
PDNothing _ -> pcon PFalse
|
PDNothing _ -> pcon PFalse
|
||||||
|
|
|
||||||
|
|
@ -12,19 +12,7 @@ import Agora.Stake
|
||||||
import Agora.Utils (
|
import Agora.Utils (
|
||||||
mustBePJust,
|
mustBePJust,
|
||||||
mustFindDatum',
|
mustFindDatum',
|
||||||
paddValue,
|
|
||||||
pfindTxInByTxOutRef,
|
|
||||||
pgeqByClass',
|
|
||||||
pgeqBySymbol,
|
|
||||||
psymbolValueOf,
|
|
||||||
ptokenSpent,
|
|
||||||
ptxSignedBy,
|
|
||||||
pvalidatorHashToTokenName,
|
pvalidatorHashToTokenName,
|
||||||
pvalueSpent,
|
|
||||||
tcassert,
|
|
||||||
tclet,
|
|
||||||
tcmatch,
|
|
||||||
tctryFrom,
|
|
||||||
)
|
)
|
||||||
import Data.Tagged (Tagged (..), untag)
|
import Data.Tagged (Tagged (..), untag)
|
||||||
import Plutarch.Api.V1 (
|
import Plutarch.Api.V1 (
|
||||||
|
|
@ -40,7 +28,10 @@ import Plutarch.Api.V1 (
|
||||||
mkMintingPolicy,
|
mkMintingPolicy,
|
||||||
)
|
)
|
||||||
import Plutarch.Api.V1.AssetClass (passetClass, passetClassValueOf, pvalueOf)
|
import Plutarch.Api.V1.AssetClass (passetClass, passetClassValueOf, pvalueOf)
|
||||||
|
import Plutarch.Api.V1.ScriptContext (pfindTxInByTxOutRef, pisTokenSpent, ptxSignedBy, pvalueSpent)
|
||||||
|
import "liqwid-plutarch-extra" Plutarch.Api.V1.Value (pgeqByClass', pgeqBySymbol, psymbolValueOf)
|
||||||
import Plutarch.Extra.Record (mkRecordConstr, (.&), (.=))
|
import Plutarch.Extra.Record (mkRecordConstr, (.&), (.=))
|
||||||
|
import Plutarch.Extra.TermCont (pguardC, pletC, pmatchC, ptryFromC)
|
||||||
import Plutarch.Internal (punsafeCoerce)
|
import Plutarch.Internal (punsafeCoerce)
|
||||||
import Plutarch.Numeric.Additive (AdditiveMonoid (zero), AdditiveSemigroup ((+)))
|
import Plutarch.Numeric.Additive (AdditiveMonoid (zero), AdditiveSemigroup ((+)))
|
||||||
import Plutarch.SafeMoney (
|
import Plutarch.SafeMoney (
|
||||||
|
|
@ -74,24 +65,24 @@ stakePolicy ::
|
||||||
stakePolicy gtClassRef =
|
stakePolicy gtClassRef =
|
||||||
plam $ \_redeemer ctx' -> unTermCont $ do
|
plam $ \_redeemer ctx' -> unTermCont $ do
|
||||||
ctx <- tcont $ pletFields @'["txInfo", "purpose"] ctx'
|
ctx <- tcont $ pletFields @'["txInfo", "purpose"] ctx'
|
||||||
txInfo <- tclet $ ctx.txInfo
|
txInfo <- pletC $ ctx.txInfo
|
||||||
let _a :: Term _ PTxInfo
|
let _a :: Term _ PTxInfo
|
||||||
_a = txInfo
|
_a = txInfo
|
||||||
txInfoF <- tcont $ pletFields @'["mint", "inputs", "outputs", "signatories", "datums"] txInfo
|
txInfoF <- tcont $ pletFields @'["mint", "inputs", "outputs", "signatories", "datums"] txInfo
|
||||||
|
|
||||||
PMinting ownSymbol' <- tcmatch $ pfromData ctx.purpose
|
PMinting ownSymbol' <- pmatchC $ pfromData ctx.purpose
|
||||||
ownSymbol <- tclet $ pfield @"_0" # ownSymbol'
|
ownSymbol <- pletC $ pfield @"_0" # ownSymbol'
|
||||||
spentST <- tclet $ psymbolValueOf # ownSymbol #$ pvalueSpent # txInfoF.inputs
|
spentST <- pletC $ psymbolValueOf # ownSymbol #$ pvalueSpent # txInfoF.inputs
|
||||||
mintedST <- tclet $ psymbolValueOf # ownSymbol # txInfoF.mint
|
mintedST <- pletC $ psymbolValueOf # ownSymbol # txInfoF.mint
|
||||||
|
|
||||||
let burning = unTermCont $ do
|
let burning = unTermCont $ do
|
||||||
tcassert "ST at inputs must be 1" $
|
pguardC "ST at inputs must be 1" $
|
||||||
spentST #== 1
|
spentST #== 1
|
||||||
|
|
||||||
tcassert "ST burned" $
|
pguardC "ST burned" $
|
||||||
mintedST #== -1
|
mintedST #== -1
|
||||||
|
|
||||||
tcassert "An unlocked input existed containing an ST" $
|
pguardC "An unlocked input existed containing an ST" $
|
||||||
pany
|
pany
|
||||||
# plam
|
# plam
|
||||||
( \((pfield @"resolved" #) -> txOut) -> unTermCont $ do
|
( \((pfield @"resolved" #) -> txOut) -> unTermCont $ do
|
||||||
|
|
@ -109,13 +100,13 @@ stakePolicy gtClassRef =
|
||||||
pure $ popaque (pconstant ())
|
pure $ popaque (pconstant ())
|
||||||
|
|
||||||
let minting = unTermCont $ do
|
let minting = unTermCont $ do
|
||||||
tcassert "ST at inputs must be 0" $
|
pguardC "ST at inputs must be 0" $
|
||||||
spentST #== 0
|
spentST #== 0
|
||||||
|
|
||||||
tcassert "Minted ST must be exactly 1" $
|
pguardC "Minted ST must be exactly 1" $
|
||||||
mintedST #== 1
|
mintedST #== 1
|
||||||
|
|
||||||
tcassert "A UTXO must exist with the correct output" $
|
pguardC "A UTXO must exist with the correct output" $
|
||||||
unTermCont $ do
|
unTermCont $ do
|
||||||
let scriptOutputWithStakeST =
|
let scriptOutputWithStakeST =
|
||||||
mustBePJust
|
mustBePJust
|
||||||
|
|
@ -214,50 +205,50 @@ stakeValidator :: Stake -> ClosedTerm PValidator
|
||||||
stakeValidator stake =
|
stakeValidator stake =
|
||||||
plam $ \datum redeemer ctx' -> unTermCont $ do
|
plam $ \datum redeemer ctx' -> unTermCont $ do
|
||||||
ctx <- tcont $ pletFields @'["txInfo", "purpose"] ctx'
|
ctx <- tcont $ pletFields @'["txInfo", "purpose"] ctx'
|
||||||
txInfo <- tclet $ pfromData ctx.txInfo
|
txInfo <- pletC $ pfromData ctx.txInfo
|
||||||
txInfoF <- tcont $ pletFields @'["mint", "inputs", "outputs", "signatories", "datums"] txInfo
|
txInfoF <- tcont $ pletFields @'["mint", "inputs", "outputs", "signatories", "datums"] txInfo
|
||||||
|
|
||||||
(pfromData -> stakeRedeemer, _) <- tctryFrom redeemer
|
(pfromData -> stakeRedeemer, _) <- ptryFromC redeemer
|
||||||
|
|
||||||
-- TODO: Use PTryFrom
|
-- TODO: Use PTryFrom
|
||||||
let stakeDatum' :: Term _ PStakeDatum
|
let stakeDatum' :: Term _ PStakeDatum
|
||||||
stakeDatum' = pfromData $ punsafeCoerce datum
|
stakeDatum' = pfromData $ punsafeCoerce datum
|
||||||
stakeDatum <- tcont $ pletFields @'["owner", "stakedAmount", "lockedBy"] stakeDatum'
|
stakeDatum <- tcont $ pletFields @'["owner", "stakedAmount", "lockedBy"] stakeDatum'
|
||||||
|
|
||||||
PSpending txOutRef <- tcmatch $ pfromData ctx.purpose
|
PSpending txOutRef <- pmatchC $ pfromData ctx.purpose
|
||||||
|
|
||||||
PJust txInInfo <- tcmatch $ pfindTxInByTxOutRef # (pfield @"_0" # txOutRef) # txInfoF.inputs
|
PJust txInInfo <- pmatchC $ pfindTxInByTxOutRef # (pfield @"_0" # txOutRef) # txInfoF.inputs
|
||||||
ownAddress <- tclet $ pfield @"address" #$ pfield @"resolved" # txInInfo
|
ownAddress <- pletC $ pfield @"address" #$ pfield @"resolved" # txInInfo
|
||||||
let continuingValue :: Term _ (PValue _ _)
|
let continuingValue :: Term _ (PValue _ _)
|
||||||
continuingValue = pfield @"value" #$ pfield @"resolved" # txInInfo
|
continuingValue = pfield @"value" #$ pfield @"resolved" # txInInfo
|
||||||
|
|
||||||
-- Whether the owner signs this transaction or not.
|
-- Whether the owner signs this transaction or not.
|
||||||
ownerSignsTransaction <- tclet $ ptxSignedBy # txInfoF.signatories # stakeDatum.owner
|
ownerSignsTransaction <- pletC $ ptxSignedBy # txInfoF.signatories # stakeDatum.owner
|
||||||
|
|
||||||
stCurrencySymbol <- tclet $ pconstant $ mintingPolicySymbol $ mkMintingPolicy (stakePolicy stake.gtClassRef)
|
stCurrencySymbol <- pletC $ pconstant $ mintingPolicySymbol $ mkMintingPolicy (stakePolicy stake.gtClassRef)
|
||||||
mintedST <- tclet $ psymbolValueOf # stCurrencySymbol # txInfoF.mint
|
mintedST <- pletC $ psymbolValueOf # stCurrencySymbol # txInfoF.mint
|
||||||
valueSpent <- tclet $ pvalueSpent # txInfoF.inputs
|
valueSpent <- pletC $ pvalueSpent # txInfoF.inputs
|
||||||
spentST <- tclet $ psymbolValueOf # stCurrencySymbol #$ valueSpent
|
spentST <- pletC $ psymbolValueOf # stCurrencySymbol #$ valueSpent
|
||||||
|
|
||||||
let AssetClass (propCs, propTn) = stake.proposalSTClass
|
let AssetClass (propCs, propTn) = stake.proposalSTClass
|
||||||
proposalSTClass = passetClass # pconstant propCs # pconstant propTn
|
proposalSTClass = passetClass # pconstant propCs # pconstant propTn
|
||||||
spentProposalST <- tclet $ passetClassValueOf # valueSpent # proposalSTClass
|
spentProposalST <- pletC $ passetClassValueOf # valueSpent # proposalSTClass
|
||||||
|
|
||||||
-- Is the stake currently locked?
|
-- Is the stake currently locked?
|
||||||
stakeIsLocked <- tclet $ stakeLocked # stakeDatum'
|
stakeIsLocked <- pletC $ stakeLocked # stakeDatum'
|
||||||
|
|
||||||
pure $
|
pure $
|
||||||
pmatch stakeRedeemer $ \case
|
pmatch stakeRedeemer $ \case
|
||||||
PDestroy _ -> unTermCont $ do
|
PDestroy _ -> unTermCont $ do
|
||||||
tcassert "ST at inputs must be 1" $
|
pguardC "ST at inputs must be 1" $
|
||||||
spentST #== 1
|
spentST #== 1
|
||||||
|
|
||||||
tcassert "Should burn ST" $
|
pguardC "Should burn ST" $
|
||||||
mintedST #== -1
|
mintedST #== -1
|
||||||
|
|
||||||
tcassert "Stake unlocked" $ pnot # stakeIsLocked
|
pguardC "Stake unlocked" $ pnot # stakeIsLocked
|
||||||
|
|
||||||
tcassert "Owner signs this transaction" ownerSignsTransaction
|
pguardC "Owner signs this transaction" ownerSignsTransaction
|
||||||
|
|
||||||
pure $ popaque (pconstant ())
|
pure $ popaque (pconstant ())
|
||||||
--------------------------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
|
|
@ -265,7 +256,7 @@ stakeValidator stake =
|
||||||
_ -> unTermCont $ do
|
_ -> unTermCont $ do
|
||||||
-- Filter out own output with own address and PST.
|
-- Filter out own output with own address and PST.
|
||||||
ownOutput <-
|
ownOutput <-
|
||||||
tclet $
|
pletC $
|
||||||
mustBePJust # "Own output should be present" #$ pfind
|
mustBePJust # "Own output should be present" #$ pfind
|
||||||
# plam
|
# plam
|
||||||
( \input -> unTermCont $ do
|
( \input -> unTermCont $ do
|
||||||
|
|
@ -277,39 +268,39 @@ stakeValidator stake =
|
||||||
# pfromData txInfoF.outputs
|
# pfromData txInfoF.outputs
|
||||||
|
|
||||||
stakeOut <-
|
stakeOut <-
|
||||||
tclet $
|
pletC $
|
||||||
mustFindDatum' @PStakeDatum
|
mustFindDatum' @PStakeDatum
|
||||||
# (pfield @"datumHash" # ownOutput)
|
# (pfield @"datumHash" # ownOutput)
|
||||||
# txInfoF.datums
|
# txInfoF.datums
|
||||||
|
|
||||||
ownOutputValue <-
|
ownOutputValue <-
|
||||||
tclet $
|
pletC $
|
||||||
pfield @"value" # ownOutput
|
pfield @"value" # ownOutput
|
||||||
|
|
||||||
ownOutputValueUnchanged <-
|
ownOutputValueUnchanged <-
|
||||||
tclet $
|
pletC $
|
||||||
pdata continuingValue #== pdata ownOutputValue
|
pdata continuingValue #== pdata ownOutputValue
|
||||||
|
|
||||||
stakeOutUnchanged <-
|
stakeOutUnchanged <-
|
||||||
tclet $
|
pletC $
|
||||||
pdata stakeOut #== pdata stakeDatum'
|
pdata stakeOut #== pdata stakeDatum'
|
||||||
|
|
||||||
pure $
|
pure $
|
||||||
pmatch stakeRedeemer $ \case
|
pmatch stakeRedeemer $ \case
|
||||||
PRetractVotes _ -> unTermCont $ do
|
PRetractVotes _ -> unTermCont $ do
|
||||||
tcassert
|
pguardC
|
||||||
"Owner signs this transaction"
|
"Owner signs this transaction"
|
||||||
ownerSignsTransaction
|
ownerSignsTransaction
|
||||||
|
|
||||||
tcassert "ST at inputs must be 1" $
|
pguardC "ST at inputs must be 1" $
|
||||||
spentST #== 1
|
spentST #== 1
|
||||||
|
|
||||||
-- This puts trust into the Proposal. The Proposal must necessarily check
|
-- This puts trust into the Proposal. The Proposal must necessarily check
|
||||||
-- that this is not abused.
|
-- that this is not abused.
|
||||||
tcassert "Proposal ST spent" $
|
pguardC "Proposal ST spent" $
|
||||||
spentProposalST #== 1
|
spentProposalST #== 1
|
||||||
|
|
||||||
tcassert "A UTXO must exist with the correct output" $
|
pguardC "A UTXO must exist with the correct output" $
|
||||||
unTermCont $ do
|
unTermCont $ do
|
||||||
let valueCorrect = ownOutputValueUnchanged
|
let valueCorrect = ownOutputValueUnchanged
|
||||||
|
|
||||||
|
|
@ -324,13 +315,13 @@ stakeValidator stake =
|
||||||
pure $ popaque (pconstant ())
|
pure $ popaque (pconstant ())
|
||||||
--------------------------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
PPermitVote l -> unTermCont $ do
|
PPermitVote l -> unTermCont $ do
|
||||||
tcassert
|
pguardC
|
||||||
"Owner signs this transaction"
|
"Owner signs this transaction"
|
||||||
ownerSignsTransaction
|
ownerSignsTransaction
|
||||||
|
|
||||||
-- This puts trust into the Proposal. The Proposal must necessarily check
|
-- This puts trust into the Proposal. The Proposal must necessarily check
|
||||||
-- that this is not abused.
|
-- that this is not abused.
|
||||||
tcassert "Proposal ST spent" $
|
pguardC "Proposal ST spent" $
|
||||||
spentProposalST #== 1
|
spentProposalST #== 1
|
||||||
|
|
||||||
-- Update the stake datum, but only the 'lockedBy' field.
|
-- Update the stake datum, but only the 'lockedBy' field.
|
||||||
|
|
@ -342,7 +333,7 @@ stakeValidator stake =
|
||||||
expectedLocks = pcons # newLock # stakeDatum.lockedBy
|
expectedLocks = pcons # newLock # stakeDatum.lockedBy
|
||||||
|
|
||||||
expectedDatum <-
|
expectedDatum <-
|
||||||
tclet $
|
pletC $
|
||||||
mkRecordConstr
|
mkRecordConstr
|
||||||
PStakeDatum
|
PStakeDatum
|
||||||
( #stakedAmount .= stakeDatum.stakedAmount
|
( #stakedAmount .= stakeDatum.stakedAmount
|
||||||
|
|
@ -350,7 +341,7 @@ stakeValidator stake =
|
||||||
.& #lockedBy .= pdata expectedLocks
|
.& #lockedBy .= pdata expectedLocks
|
||||||
)
|
)
|
||||||
|
|
||||||
tcassert "A UTXO must exist with the correct output" $
|
pguardC "A UTXO must exist with the correct output" $
|
||||||
let correctOutputDatum = stakeOut #== expectedDatum
|
let correctOutputDatum = stakeOut #== expectedDatum
|
||||||
valueCorrect = ownOutputValueUnchanged
|
valueCorrect = ownOutputValueUnchanged
|
||||||
in foldl1
|
in foldl1
|
||||||
|
|
@ -362,24 +353,24 @@ stakeValidator stake =
|
||||||
pure $ popaque (pconstant ())
|
pure $ popaque (pconstant ())
|
||||||
--------------------------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
PWitnessStake _ -> unTermCont $ do
|
PWitnessStake _ -> unTermCont $ do
|
||||||
tcassert "ST at inputs must be 1" $
|
pguardC "ST at inputs must be 1" $
|
||||||
spentST #== 1
|
spentST #== 1
|
||||||
|
|
||||||
let AssetClass (propCs, propTn) = stake.proposalSTClass
|
let AssetClass (propCs, propTn) = stake.proposalSTClass
|
||||||
propAssetClass = passetClass # pconstant propCs # pconstant propTn
|
propAssetClass = passetClass # pconstant propCs # pconstant propTn
|
||||||
proposalTokenMoved =
|
proposalTokenMoved =
|
||||||
ptokenSpent
|
pisTokenSpent
|
||||||
# propAssetClass
|
# propAssetClass
|
||||||
# txInfoF.inputs
|
# txInfoF.inputs
|
||||||
|
|
||||||
-- In order for cosignature to be witnessed, it must be possible for a
|
-- In order for cosignature to be witnessed, it must be possible for a
|
||||||
-- proposal to allow this transaction to happen. This puts trust into the Proposal.
|
-- proposal to allow this transaction to happen. This puts trust into the Proposal.
|
||||||
-- The Proposal must necessarily check that this is not abused.
|
-- The Proposal must necessarily check that this is not abused.
|
||||||
tcassert
|
pguardC
|
||||||
"Owner signs this transaction OR proposal token is spent"
|
"Owner signs this transaction OR proposal token is spent"
|
||||||
(ownerSignsTransaction #|| proposalTokenMoved)
|
(ownerSignsTransaction #|| proposalTokenMoved)
|
||||||
|
|
||||||
tcassert "A UTXO must exist with the correct output" $
|
pguardC "A UTXO must exist with the correct output" $
|
||||||
let correctOutputDatum = stakeOutUnchanged
|
let correctOutputDatum = stakeOutUnchanged
|
||||||
valueCorrect = ownOutputValueUnchanged
|
valueCorrect = ownOutputValueUnchanged
|
||||||
in foldl1
|
in foldl1
|
||||||
|
|
@ -390,21 +381,21 @@ stakeValidator stake =
|
||||||
pure $ popaque (pconstant ())
|
pure $ popaque (pconstant ())
|
||||||
--------------------------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
PDepositWithdraw r -> unTermCont $ do
|
PDepositWithdraw r -> unTermCont $ do
|
||||||
tcassert "ST at inputs must be 1" $
|
pguardC "ST at inputs must be 1" $
|
||||||
spentST #== 1
|
spentST #== 1
|
||||||
tcassert "Stake unlocked" $
|
pguardC "Stake unlocked" $
|
||||||
pnot #$ stakeIsLocked
|
pnot #$ stakeIsLocked
|
||||||
tcassert
|
pguardC
|
||||||
"Owner signs this transaction"
|
"Owner signs this transaction"
|
||||||
ownerSignsTransaction
|
ownerSignsTransaction
|
||||||
tcassert "A UTXO must exist with the correct output" $
|
pguardC "A UTXO must exist with the correct output" $
|
||||||
unTermCont $ do
|
unTermCont $ do
|
||||||
let oldStakedAmount = pfromData $ stakeDatum.stakedAmount
|
let oldStakedAmount = pfromData $ stakeDatum.stakedAmount
|
||||||
delta = pfromData $ pfield @"delta" # r
|
delta = pfromData $ pfield @"delta" # r
|
||||||
|
|
||||||
newStakedAmount <- tclet $ oldStakedAmount + delta
|
newStakedAmount <- pletC $ oldStakedAmount + delta
|
||||||
|
|
||||||
tcassert "New staked amount shoudl be greater than or equal to 0" $
|
pguardC "New staked amount shoudl be greater than or equal to 0" $
|
||||||
zero #<= newStakedAmount
|
zero #<= newStakedAmount
|
||||||
|
|
||||||
let expectedDatum =
|
let expectedDatum =
|
||||||
|
|
@ -420,7 +411,7 @@ stakeValidator stake =
|
||||||
valueDelta = pdiscreteValue' stake.gtClassRef # delta
|
valueDelta = pdiscreteValue' stake.gtClassRef # delta
|
||||||
|
|
||||||
expectedValue =
|
expectedValue =
|
||||||
paddValue # continuingValue # valueDelta
|
continuingValue <> valueDelta
|
||||||
|
|
||||||
valueCorrect =
|
valueCorrect =
|
||||||
foldr1
|
foldr1
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ treasury.
|
||||||
module Agora.Treasury (module Agora.Treasury) where
|
module Agora.Treasury (module Agora.Treasury) where
|
||||||
|
|
||||||
import Agora.AuthorityToken (singleAuthorityTokenBurned)
|
import Agora.AuthorityToken (singleAuthorityTokenBurned)
|
||||||
import Agora.Utils (tcassert, tclet, tcmatch, tctryFrom)
|
|
||||||
import GHC.Generics qualified as GHC
|
import GHC.Generics qualified as GHC
|
||||||
import Generics.SOP
|
import Generics.SOP
|
||||||
import Plutarch.Api.V1 (PValidator)
|
import Plutarch.Api.V1 (PValidator)
|
||||||
|
|
@ -21,6 +20,7 @@ import Plutarch.DataRepr (
|
||||||
DerivePConstantViaData (..),
|
DerivePConstantViaData (..),
|
||||||
PIsDataReprInstances (PIsDataReprInstances),
|
PIsDataReprInstances (PIsDataReprInstances),
|
||||||
)
|
)
|
||||||
|
import Plutarch.Extra.TermCont (pguardC, pletC, pmatchC, ptryFromC)
|
||||||
import Plutarch.Lift (PConstantDecl (..), PLifted (..), PUnsafeLiftDecl)
|
import Plutarch.Lift (PConstantDecl (..), PLifted (..), PUnsafeLiftDecl)
|
||||||
import Plutarch.TryFrom ()
|
import Plutarch.TryFrom ()
|
||||||
import PlutusLedgerApi.V1.Value (CurrencySymbol)
|
import PlutusLedgerApi.V1.Value (CurrencySymbol)
|
||||||
|
|
@ -76,26 +76,26 @@ treasuryValidator ::
|
||||||
CurrencySymbol ->
|
CurrencySymbol ->
|
||||||
ClosedTerm PValidator
|
ClosedTerm PValidator
|
||||||
treasuryValidator gatCs' = plam $ \_datum redeemer ctx' -> unTermCont $ do
|
treasuryValidator gatCs' = plam $ \_datum redeemer ctx' -> unTermCont $ do
|
||||||
(treasuryRedeemer, _) <- tctryFrom redeemer
|
(treasuryRedeemer, _) <- ptryFromC redeemer
|
||||||
|
|
||||||
-- plet required fields from script context.
|
-- plet required fields from script context.
|
||||||
ctx <- tcont $ pletFields @["txInfo", "purpose"] ctx'
|
ctx <- tcont $ pletFields @["txInfo", "purpose"] ctx'
|
||||||
|
|
||||||
-- Ensure that script is for burning i.e. minting a negative amount.
|
-- Ensure that script is for burning i.e. minting a negative amount.
|
||||||
PMinting _ <- tcmatch ctx.purpose
|
PMinting _ <- pmatchC ctx.purpose
|
||||||
|
|
||||||
-- Ensure redeemer type is valid.
|
-- Ensure redeemer type is valid.
|
||||||
PSpendTreasuryGAT _ <- tcmatch $ pfromData treasuryRedeemer
|
PSpendTreasuryGAT _ <- pmatchC $ pfromData treasuryRedeemer
|
||||||
|
|
||||||
-- Get the minted value from txInfo.
|
-- Get the minted value from txInfo.
|
||||||
txInfo' <- tclet ctx.txInfo
|
txInfo' <- pletC ctx.txInfo
|
||||||
txInfo <- tcont $ pletFields @'["mint"] txInfo'
|
txInfo <- tcont $ pletFields @'["mint"] txInfo'
|
||||||
let mint :: Term _ (PValue _ _)
|
let mint :: Term _ (PValue _ _)
|
||||||
mint = txInfo.mint
|
mint = txInfo.mint
|
||||||
|
|
||||||
gatCs <- tclet $ pconstant gatCs'
|
gatCs <- pletC $ pconstant gatCs'
|
||||||
|
|
||||||
tcassert "A single authority token has been burned" $
|
pguardC "A single authority token has been burned" $
|
||||||
singleAuthorityTokenBurned gatCs txInfo' mint
|
singleAuthorityTokenBurned gatCs txInfo' mint
|
||||||
|
|
||||||
pure . popaque $ pconstant ()
|
pure . popaque $ pconstant ()
|
||||||
|
|
|
||||||
|
|
@ -6,45 +6,6 @@ Description: Plutarch utility functions that should be upstreamed or don't belon
|
||||||
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 (
|
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,
|
findTxOutByTxOutRef,
|
||||||
scriptHashFromAddress,
|
scriptHashFromAddress,
|
||||||
findOutputsToAddress,
|
findOutputsToAddress,
|
||||||
|
|
@ -57,10 +18,9 @@ module Agora.Utils (
|
||||||
mustBePJust,
|
mustBePJust,
|
||||||
mustBePDJust,
|
mustBePDJust,
|
||||||
validatorHashToAddress,
|
validatorHashToAddress,
|
||||||
pmergeBy,
|
|
||||||
phalve,
|
|
||||||
isScriptAddress,
|
isScriptAddress,
|
||||||
isPubKey,
|
isPubKey,
|
||||||
|
psingletonValue,
|
||||||
) where
|
) where
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
@ -72,541 +32,37 @@ import PlutusLedgerApi.V1 (
|
||||||
TokenName (..),
|
TokenName (..),
|
||||||
ValidatorHash (..),
|
ValidatorHash (..),
|
||||||
)
|
)
|
||||||
import PlutusLedgerApi.V1.Value (AssetClass (..))
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
import Plutarch.Api.V1 (
|
import Plutarch.Api.V1 (
|
||||||
AmountGuarantees (NoGuarantees, NonZero, Positive),
|
AmountGuarantees,
|
||||||
KeyGuarantees (Sorted, Unsorted),
|
KeyGuarantees,
|
||||||
PAddress,
|
PAddress,
|
||||||
PCredential (PScriptCredential),
|
PCredential (PScriptCredential),
|
||||||
PCurrencySymbol,
|
PCurrencySymbol,
|
||||||
PDatum,
|
PDatum,
|
||||||
PDatumHash,
|
PDatumHash,
|
||||||
PMap,
|
|
||||||
PMaybeData (PDJust),
|
PMaybeData (PDJust),
|
||||||
PMintingPolicy,
|
PMintingPolicy,
|
||||||
PPubKeyHash,
|
|
||||||
PTokenName (PTokenName),
|
PTokenName (PTokenName),
|
||||||
PTuple,
|
PTuple,
|
||||||
PTxInInfo (PTxInInfo),
|
PTxInInfo,
|
||||||
PTxOut (PTxOut),
|
PTxOut,
|
||||||
PTxOutRef,
|
PTxOutRef,
|
||||||
PValidatorHash,
|
PValidatorHash,
|
||||||
PValue,
|
PValue,
|
||||||
mintingPolicySymbol,
|
mintingPolicySymbol,
|
||||||
mkMintingPolicy,
|
mkMintingPolicy,
|
||||||
)
|
)
|
||||||
import Plutarch.Api.V1.AssetClass (PAssetClass, passetClassValueOf, pvalueOf)
|
|
||||||
import Plutarch.Api.V1.AssocMap (PMap (PMap))
|
import Plutarch.Api.V1.AssocMap (PMap (PMap))
|
||||||
|
import Plutarch.Api.V1.ScriptContext (pfindDatum, pfindTxInByTxOutRef)
|
||||||
|
import "liqwid-plutarch-extra" Plutarch.Api.V1.Value (psymbolValueOf)
|
||||||
import "plutarch" Plutarch.Api.V1.Value (PValue (PValue))
|
import "plutarch" Plutarch.Api.V1.Value (PValue (PValue))
|
||||||
import Plutarch.Builtin (pforgetData, ppairDataBuiltin)
|
import Plutarch.Builtin (pforgetData, ppairDataBuiltin)
|
||||||
import Plutarch.Extra.Map (pkeys)
|
import Plutarch.Extra.List (plookupTuple)
|
||||||
import Plutarch.Reducible (Reducible (Reduce))
|
import Plutarch.Extra.TermCont (pletC, pmatchC)
|
||||||
import Plutarch.TryFrom (PTryFrom (PTryFromExcess))
|
|
||||||
import Plutarch.Unsafe (punsafeCoerce)
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
-- 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 ::
|
|
||||||
forall (keys :: KeyGuarantees) (amounts :: AmountGuarantees) (s :: S).
|
|
||||||
Term s (PCurrencySymbol :--> PValue keys amounts :--> 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' ::
|
|
||||||
forall (keys :: KeyGuarantees) (amounts :: AmountGuarantees) (s :: S).
|
|
||||||
AssetClass ->
|
|
||||||
Term s (PValue keys amounts :--> 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 ::
|
|
||||||
forall (keys :: KeyGuarantees) (amounts :: AmountGuarantees) (s :: S).
|
|
||||||
Term s (PCurrencySymbol :--> PTokenName :--> PValue keys amounts :--> PValue keys amounts :--> 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 ::
|
|
||||||
forall (keys :: KeyGuarantees) (amounts :: AmountGuarantees) (s :: S).
|
|
||||||
Term s (PCurrencySymbol :--> PValue keys amounts :--> PValue keys amounts :--> 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' ::
|
|
||||||
forall (keys :: KeyGuarantees) (amounts :: AmountGuarantees) (s :: S).
|
|
||||||
AssetClass ->
|
|
||||||
Term s (PValue keys amounts :--> PValue keys amounts :--> 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 :: PType) (v :: PType) (keys :: KeyGuarantees) (s :: S).
|
|
||||||
PIsData v =>
|
|
||||||
Term s ((v :--> v :--> v) :--> PMap keys k v :--> PMap keys k v :--> PMap keys 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 (k :: PType) (v :: PType) (keys :: KeyGuarantees) (s :: S).
|
|
||||||
(PIsData k, PIsData v) =>
|
|
||||||
Term s ((v :--> PMaybe v) :--> k :--> PMap keys k v :--> PMap keys 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 (k :: PType) (a :: PType) (b :: PType) (keys :: KeyGuarantees) (s :: S).
|
|
||||||
(PIsData k, PIsData a, PIsData b) =>
|
|
||||||
Term s ((a :--> b) :--> PMap keys k a :--> PMap keys 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
|
|
||||||
|
|
||||||
-- | Compute the guarantees known after adding two values.
|
|
||||||
type family AddGuarantees (a :: AmountGuarantees) (b :: AmountGuarantees) where
|
|
||||||
AddGuarantees 'Positive 'Positive = 'Positive
|
|
||||||
AddGuarantees _ _ = 'NoGuarantees
|
|
||||||
|
|
||||||
-- | Add two 'PValue's together.
|
|
||||||
paddValue ::
|
|
||||||
forall (keys :: KeyGuarantees) (as :: AmountGuarantees) (bs :: AmountGuarantees) (s :: S).
|
|
||||||
Term s (PValue keys as :--> PValue keys bs :--> PValue keys (AddGuarantees as bs))
|
|
||||||
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 ::
|
|
||||||
forall (s :: S).
|
|
||||||
Term s (PBuiltinList (PAsData PTxInInfo) :--> PValue 'Sorted 'Positive)
|
|
||||||
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
|
|
||||||
)
|
|
||||||
-- TODO: This should be possible without coercions, but I can't figure out the types atm.
|
|
||||||
# punsafeCoerce (pconstant mempty :: Term _ (PValue 'Unsorted 'NonZero))
|
|
||||||
# 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 ::
|
|
||||||
forall (k :: PType) (a :: PType) (b :: PType) (keys :: KeyGuarantees) (s :: S).
|
|
||||||
(POrd k, PIsData k) =>
|
|
||||||
Term s (PMap keys k a :--> PMap keys 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
|
{- Functions which should (probably) not be upstreamed
|
||||||
All of these functions are quite inefficient.
|
All of these functions are quite inefficient.
|
||||||
-}
|
-}
|
||||||
|
|
@ -655,7 +111,7 @@ isPubKey = phoistAcyclic $
|
||||||
findOutputsToAddress :: Term s (PBuiltinList (PAsData PTxOut) :--> PAddress :--> PBuiltinList (PAsData PTxOut))
|
findOutputsToAddress :: Term s (PBuiltinList (PAsData PTxOut) :--> PAddress :--> PBuiltinList (PAsData PTxOut))
|
||||||
findOutputsToAddress = phoistAcyclic $
|
findOutputsToAddress = phoistAcyclic $
|
||||||
plam $ \outputs address' -> unTermCont $ do
|
plam $ \outputs address' -> unTermCont $ do
|
||||||
address <- tclet $ pdata address'
|
address <- pletC $ pdata address'
|
||||||
pure $
|
pure $
|
||||||
pfilter # plam (\(pfromData -> txOut) -> pfield @"address" # txOut #== address)
|
pfilter # plam (\(pfromData -> txOut) -> pfield @"address" # txOut #== address)
|
||||||
# outputs
|
# outputs
|
||||||
|
|
@ -664,7 +120,7 @@ findOutputsToAddress = phoistAcyclic $
|
||||||
findTxOutDatum :: Term s (PBuiltinList (PAsData (PTuple PDatumHash PDatum)) :--> PTxOut :--> PMaybe PDatum)
|
findTxOutDatum :: Term s (PBuiltinList (PAsData (PTuple PDatumHash PDatum)) :--> PTxOut :--> PMaybe PDatum)
|
||||||
findTxOutDatum = phoistAcyclic $
|
findTxOutDatum = phoistAcyclic $
|
||||||
plam $ \datums out -> unTermCont $ do
|
plam $ \datums out -> unTermCont $ do
|
||||||
datumHash' <- tcmatch $ pfromData $ pfield @"datumHash" # out
|
datumHash' <- pmatchC $ pfromData $ pfield @"datumHash" # out
|
||||||
pure $ case datumHash' of
|
pure $ case datumHash' of
|
||||||
PDJust ((pfield @"_0" #) -> datumHash) -> pfindDatum # datumHash # datums
|
PDJust ((pfield @"_0" #) -> datumHash) -> pfindDatum # datumHash # datums
|
||||||
_ -> pcon PNothing
|
_ -> pcon PNothing
|
||||||
|
|
|
||||||
42
bench.csv
42
bench.csv
|
|
@ -1,29 +1,29 @@
|
||||||
name,cpu,mem,size
|
name,cpu,mem,size
|
||||||
Agora/Effects/Treasury Withdrawal Effect/effect/Simple,317467035,778238,3172
|
Agora/Effects/Treasury Withdrawal Effect/effect/Simple,289461528,703055,3191
|
||||||
Agora/Effects/Treasury Withdrawal Effect/effect/Simple with multiple treasuries ,555940189,1350738,3499
|
Agora/Effects/Treasury Withdrawal Effect/effect/Simple with multiple treasuries ,448521458,1070167,3518
|
||||||
Agora/Effects/Treasury Withdrawal Effect/effect/Mixed Assets,488765974,1174701,3364
|
Agora/Effects/Treasury Withdrawal Effect/effect/Mixed Assets,408085321,966048,3383
|
||||||
Agora/Effects/Governor Mutation Effect/validator/valid new governor datum/governor validator should pass,83689582,228928,7629
|
Agora/Effects/Governor Mutation Effect/validator/valid new governor datum/governor validator should pass,83758582,229228,7665
|
||||||
Agora/Effects/Governor Mutation Effect/validator/valid new governor datum/effect validator should pass,97345575,266935,3358
|
Agora/Effects/Governor Mutation Effect/validator/valid new governor datum/effect validator should pass,97345575,266935,3358
|
||||||
Agora/Stake/policy/stakeCreation,43459609,126049,2116
|
Agora/Stake/policy/stakeCreation,43114609,124549,2094
|
||||||
Agora/Stake/validator/stakeDepositWithdraw deposit,226083166,599197,4024
|
Agora/Stake/validator/stakeDepositWithdraw deposit,171823342,464745,4069
|
||||||
Agora/Stake/validator/stakeDepositWithdraw withdraw,226083166,599197,4016
|
Agora/Stake/validator/stakeDepositWithdraw withdraw,171823342,464745,4061
|
||||||
Agora/Proposal/policy/proposalCreation,23071177,68894,1523
|
Agora/Proposal/policy/proposalCreation,23140177,69194,1525
|
||||||
Agora/Proposal/validator/cosignature/proposal,190181087,511819,5644
|
Agora/Proposal/validator/cosignature/proposal,147258436,403167,5646
|
||||||
Agora/Proposal/validator/cosignature/stake,162540553,402961,4561
|
Agora/Proposal/validator/cosignature/stake,117270039,287783,4606
|
||||||
Agora/Proposal/validator/voting/proposal,181998338,491168,5652
|
Agora/Proposal/validator/voting/proposal,154824944,415642,5654
|
||||||
Agora/Proposal/validator/voting/stake,127693475,328703,4614
|
Agora/Proposal/validator/voting/stake,99545453,256941,4659
|
||||||
Agora/Proposal/validator/advancing/successfully advance to next state/Draft -> VotringReady,98071575,260351,5030
|
Agora/Proposal/validator/advancing/successfully advance to next state/Draft -> VotringReady,94701799,249495,5031
|
||||||
Agora/Proposal/validator/advancing/successfully advance to next state/VotingReady -> Locked,97228153,258848,5033
|
Agora/Proposal/validator/advancing/successfully advance to next state/VotingReady -> Locked,93858377,247992,5034
|
||||||
Agora/Proposal/validator/advancing/successfully advance to next state/Locked -> Finished,98924620,262454,5033
|
Agora/Proposal/validator/advancing/successfully advance to next state/Locked -> Finished,95554844,251598,5034
|
||||||
Agora/Proposal/validator/advancing/successfully advance to failed state: timeout/Draft -> Finished,96941774,257621,5032
|
Agora/Proposal/validator/advancing/successfully advance to failed state: timeout/Draft -> Finished,93571998,246765,5033
|
||||||
Agora/Proposal/validator/advancing/successfully advance to failed state: timeout/VotingReady -> Finished,95532863,254916,5033
|
Agora/Proposal/validator/advancing/successfully advance to failed state: timeout/VotingReady -> Finished,92163087,244060,5034
|
||||||
Agora/Proposal/validator/advancing/successfully advance to failed state: timeout/Locked -> Finished,96663841,257320,5033
|
Agora/Proposal/validator/advancing/successfully advance to failed state: timeout/Locked -> Finished,93294065,246464,5034
|
||||||
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct simple,21017788,55883,806
|
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct simple,21017788,55883,806
|
||||||
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct many inputs,33204186,88241,900
|
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct many inputs,33204186,88241,900
|
||||||
Agora/Treasury/Validator/Positive/Allows for effect changes,29938856,79744,1841
|
Agora/Treasury/Validator/Positive/Allows for effect changes,29938856,79744,1841
|
||||||
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct simple,21017788,55883,806
|
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct simple,21017788,55883,806
|
||||||
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct many inputs,33204186,88241,900
|
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct many inputs,33204186,88241,900
|
||||||
Agora/Governor/policy/GST minting,43087287,120125,1833
|
Agora/Governor/policy/GST minting,43087287,120125,1833
|
||||||
Agora/Governor/validator/proposal creation,258936253,681815,8145
|
Agora/Governor/validator/proposal creation,261928725,689487,8181
|
||||||
Agora/Governor/validator/GATs minting,358292569,955552,8266
|
Agora/Governor/validator/GATs minting,351749811,938560,8302
|
||||||
Agora/Governor/validator/mutate governor state,81661538,223202,7682
|
Agora/Governor/validator/mutate governor state,81730538,223502,7718
|
||||||
|
|
|
||||||
|
1640
flake.lock
generated
1640
flake.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -21,7 +21,7 @@
|
||||||
inputs.plutarch-numeric.url =
|
inputs.plutarch-numeric.url =
|
||||||
"github:Liqwid-Labs/plutarch-numeric?ref=main";
|
"github:Liqwid-Labs/plutarch-numeric?ref=main";
|
||||||
inputs.plutarch-safe-money.url =
|
inputs.plutarch-safe-money.url =
|
||||||
"github:Liqwid-Labs/plutarch-safe-money?ref=main";
|
"github:Liqwid-Labs/plutarch-safe-money?rev=9f968b80189c7e4b335527cd5b103dc26952f667";
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
inputs.plutarch-quickcheck.url =
|
inputs.plutarch-quickcheck.url =
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue