Merge pull request #93 from Liqwid-Labs/connor/proposal-timing-conf
Proposal Timings
This commit is contained in:
commit
dbfff95bfa
13 changed files with 344 additions and 175 deletions
|
|
@ -11,14 +11,22 @@ module Sample.Effect.GovernorMutation (
|
||||||
mkEffectDatum,
|
mkEffectDatum,
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
import Agora.Effect.GovernorMutation (
|
import Agora.Effect.GovernorMutation (
|
||||||
MutateGovernorDatum (..),
|
MutateGovernorDatum (..),
|
||||||
mutateGovernorValidator,
|
mutateGovernorValidator,
|
||||||
)
|
)
|
||||||
import Agora.Governor (GovernorDatum (..))
|
import Agora.Governor (GovernorDatum (..))
|
||||||
import Agora.Proposal (ProposalId (..), ProposalThresholds (..))
|
import Agora.Proposal (ProposalId (..), ProposalThresholds (..))
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
import Plutarch.Api.V1 (mkValidator, validatorHash)
|
import Plutarch.Api.V1 (mkValidator, validatorHash)
|
||||||
import Plutarch.SafeMoney (Tagged (Tagged))
|
import Plutarch.SafeMoney (Tagged (Tagged))
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
import Plutus.V1.Ledger.Address (scriptHashAddress)
|
import Plutus.V1.Ledger.Address (scriptHashAddress)
|
||||||
import Plutus.V1.Ledger.Api (
|
import Plutus.V1.Ledger.Api (
|
||||||
Address,
|
Address,
|
||||||
|
|
@ -35,6 +43,9 @@ import Plutus.V1.Ledger.Api (
|
||||||
import Plutus.V1.Ledger.Api qualified as Interval
|
import Plutus.V1.Ledger.Api qualified as Interval
|
||||||
import Plutus.V1.Ledger.Value (AssetClass, assetClass)
|
import Plutus.V1.Ledger.Value (AssetClass, assetClass)
|
||||||
import Plutus.V1.Ledger.Value qualified as Value
|
import Plutus.V1.Ledger.Value qualified as Value
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
import Sample.Shared (
|
import Sample.Shared (
|
||||||
authorityTokenSymbol,
|
authorityTokenSymbol,
|
||||||
defaultProposalThresholds,
|
defaultProposalThresholds,
|
||||||
|
|
@ -46,6 +57,12 @@ import Sample.Shared (
|
||||||
)
|
)
|
||||||
import Test.Util (datumPair, toDatumHash)
|
import Test.Util (datumPair, toDatumHash)
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
import Data.Default.Class (Default (def))
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
-- | The effect validator instance.
|
-- | The effect validator instance.
|
||||||
effectValidator :: Validator
|
effectValidator :: Validator
|
||||||
effectValidator = mkValidator $ mutateGovernorValidator governor
|
effectValidator = mkValidator $ mutateGovernorValidator governor
|
||||||
|
|
@ -101,6 +118,8 @@ mkEffectTxInfo newGovDatum =
|
||||||
GovernorDatum
|
GovernorDatum
|
||||||
{ proposalThresholds = defaultProposalThresholds
|
{ proposalThresholds = defaultProposalThresholds
|
||||||
, nextProposalId = ProposalId 0
|
, nextProposalId = ProposalId 0
|
||||||
|
, proposalTimings = def
|
||||||
|
, createProposalTimeRangeMaxWidth = def
|
||||||
}
|
}
|
||||||
governorInputDatum :: Datum
|
governorInputDatum :: Datum
|
||||||
governorInputDatum = Datum $ toBuiltinData governorInputDatum'
|
governorInputDatum = Datum $ toBuiltinData governorInputDatum'
|
||||||
|
|
@ -161,6 +180,8 @@ validNewGovernorDatum =
|
||||||
GovernorDatum
|
GovernorDatum
|
||||||
{ proposalThresholds = defaultProposalThresholds
|
{ proposalThresholds = defaultProposalThresholds
|
||||||
, nextProposalId = ProposalId 42
|
, nextProposalId = ProposalId 42
|
||||||
|
, proposalTimings = def
|
||||||
|
, createProposalTimeRangeMaxWidth = def
|
||||||
}
|
}
|
||||||
|
|
||||||
invalidNewGovernorDatum :: GovernorDatum
|
invalidNewGovernorDatum :: GovernorDatum
|
||||||
|
|
@ -171,4 +192,6 @@ invalidNewGovernorDatum =
|
||||||
{ countVoting = Tagged (-1)
|
{ countVoting = Tagged (-1)
|
||||||
}
|
}
|
||||||
, nextProposalId = ProposalId 42
|
, nextProposalId = ProposalId 42
|
||||||
|
, proposalTimings = def
|
||||||
|
, createProposalTimeRangeMaxWidth = def
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,10 @@ import Agora.Proposal (
|
||||||
emptyVotesFor,
|
emptyVotesFor,
|
||||||
)
|
)
|
||||||
import Agora.Proposal qualified as P
|
import Agora.Proposal qualified as P
|
||||||
|
import Agora.Proposal.Time (
|
||||||
|
ProposalStartingTime (ProposalStartingTime),
|
||||||
|
ProposalTimingConfig (..),
|
||||||
|
)
|
||||||
import Agora.Stake (
|
import Agora.Stake (
|
||||||
ProposalLock (..),
|
ProposalLock (..),
|
||||||
Stake (..),
|
Stake (..),
|
||||||
|
|
@ -73,16 +77,19 @@ import Sample.Shared (
|
||||||
gstUTXORef,
|
gstUTXORef,
|
||||||
minAda,
|
minAda,
|
||||||
proposalPolicySymbol,
|
proposalPolicySymbol,
|
||||||
proposalTimingConfig,
|
proposalStartingTimeFromTimeRange,
|
||||||
proposalValidatorAddress,
|
proposalValidatorAddress,
|
||||||
signer,
|
signer,
|
||||||
signer2,
|
signer2,
|
||||||
stake,
|
stake,
|
||||||
stakeAddress,
|
stakeAddress,
|
||||||
stakeAssetClass,
|
stakeAssetClass,
|
||||||
tmpProposalStartingTime,
|
|
||||||
)
|
)
|
||||||
import Test.Util (datumPair, toDatumHash)
|
import Test.Util (closedBoundedInterval, datumPair, toDatumHash)
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
import Data.Default.Class (Default (def))
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
@ -114,6 +121,8 @@ mintGST =
|
||||||
GovernorDatum
|
GovernorDatum
|
||||||
{ proposalThresholds = defaultProposalThresholds
|
{ proposalThresholds = defaultProposalThresholds
|
||||||
, nextProposalId = ProposalId 0
|
, nextProposalId = ProposalId 0
|
||||||
|
, proposalTimings = def
|
||||||
|
, createProposalTimeRangeMaxWidth = def
|
||||||
}
|
}
|
||||||
governorOutputDatum :: Datum
|
governorOutputDatum :: Datum
|
||||||
governorOutputDatum = Datum $ toBuiltinData governorOutputDatum'
|
governorOutputDatum = Datum $ toBuiltinData governorOutputDatum'
|
||||||
|
|
@ -207,6 +216,8 @@ createProposal =
|
||||||
GovernorDatum
|
GovernorDatum
|
||||||
{ proposalThresholds = defaultProposalThresholds
|
{ proposalThresholds = defaultProposalThresholds
|
||||||
, nextProposalId = thisProposalId
|
, nextProposalId = thisProposalId
|
||||||
|
, proposalTimings = def
|
||||||
|
, createProposalTimeRangeMaxWidth = def
|
||||||
}
|
}
|
||||||
governorInputDatum :: Datum
|
governorInputDatum :: Datum
|
||||||
governorInputDatum = Datum $ toBuiltinData governorInputDatum'
|
governorInputDatum = Datum $ toBuiltinData governorInputDatum'
|
||||||
|
|
@ -236,8 +247,8 @@ createProposal =
|
||||||
, cosigners = [signer]
|
, cosigners = [signer]
|
||||||
, thresholds = defaultProposalThresholds
|
, thresholds = defaultProposalThresholds
|
||||||
, votes = emptyVotesFor effects
|
, votes = emptyVotesFor effects
|
||||||
, timingConfig = proposalTimingConfig
|
, timingConfig = def
|
||||||
, startingTime = tmpProposalStartingTime
|
, startingTime = proposalStartingTimeFromTimeRange validTimeRange
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
proposalOutput :: TxOut
|
proposalOutput :: TxOut
|
||||||
|
|
@ -298,8 +309,13 @@ createProposal =
|
||||||
}
|
}
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
ownInputRef :: TxOutRef
|
ownInputRef :: TxOutRef
|
||||||
ownInputRef = TxOutRef "4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865" 1
|
ownInputRef = TxOutRef "4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865" 1
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
validTimeRange = closedBoundedInterval 10 15
|
||||||
in ScriptContext
|
in ScriptContext
|
||||||
{ scriptContextTxInfo =
|
{ scriptContextTxInfo =
|
||||||
TxInfo
|
TxInfo
|
||||||
|
|
@ -316,7 +332,7 @@ createProposal =
|
||||||
, txInfoMint = pst
|
, txInfoMint = pst
|
||||||
, txInfoDCert = []
|
, txInfoDCert = []
|
||||||
, txInfoWdrl = []
|
, txInfoWdrl = []
|
||||||
, txInfoValidRange = Interval.always
|
, txInfoValidRange = validTimeRange
|
||||||
, txInfoSignatories = [signer]
|
, txInfoSignatories = [signer]
|
||||||
, txInfoData =
|
, txInfoData =
|
||||||
datumPair
|
datumPair
|
||||||
|
|
@ -378,6 +394,8 @@ mintGATs =
|
||||||
GovernorDatum
|
GovernorDatum
|
||||||
{ proposalThresholds = defaultProposalThresholds
|
{ proposalThresholds = defaultProposalThresholds
|
||||||
, nextProposalId = ProposalId 5
|
, nextProposalId = ProposalId 5
|
||||||
|
, proposalTimings = def
|
||||||
|
, createProposalTimeRangeMaxWidth = def
|
||||||
}
|
}
|
||||||
governorInputDatum :: Datum
|
governorInputDatum :: Datum
|
||||||
governorInputDatum = Datum $ toBuiltinData governorInputDatum'
|
governorInputDatum = Datum $ toBuiltinData governorInputDatum'
|
||||||
|
|
@ -412,8 +430,8 @@ mintGATs =
|
||||||
, cosigners = [signer, signer2]
|
, cosigners = [signer, signer2]
|
||||||
, thresholds = defaultProposalThresholds
|
, thresholds = defaultProposalThresholds
|
||||||
, votes = proposalVotes
|
, votes = proposalVotes
|
||||||
, timingConfig = proposalTimingConfig
|
, timingConfig = def
|
||||||
, startingTime = tmpProposalStartingTime
|
, startingTime = ProposalStartingTime 10
|
||||||
}
|
}
|
||||||
proposalInputDatum :: Datum
|
proposalInputDatum :: Datum
|
||||||
proposalInputDatum = Datum $ toBuiltinData proposalInputDatum'
|
proposalInputDatum = Datum $ toBuiltinData proposalInputDatum'
|
||||||
|
|
@ -465,6 +483,12 @@ mintGATs =
|
||||||
|
|
||||||
ownInputRef :: TxOutRef
|
ownInputRef :: TxOutRef
|
||||||
ownInputRef = TxOutRef "4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865" 1
|
ownInputRef = TxOutRef "4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865" 1
|
||||||
|
|
||||||
|
--
|
||||||
|
validTimeRange =
|
||||||
|
closedBoundedInterval
|
||||||
|
((def :: ProposalTimingConfig).lockingTime + 11)
|
||||||
|
((def :: ProposalTimingConfig).executingTime - 11)
|
||||||
in ScriptContext
|
in ScriptContext
|
||||||
{ scriptContextTxInfo =
|
{ scriptContextTxInfo =
|
||||||
TxInfo
|
TxInfo
|
||||||
|
|
@ -483,7 +507,7 @@ mintGATs =
|
||||||
, txInfoMint = gat
|
, txInfoMint = gat
|
||||||
, txInfoDCert = []
|
, txInfoDCert = []
|
||||||
, txInfoWdrl = []
|
, txInfoWdrl = []
|
||||||
, txInfoValidRange = Interval.always
|
, txInfoValidRange = validTimeRange
|
||||||
, txInfoSignatories = [signer, signer2]
|
, txInfoSignatories = [signer, signer2]
|
||||||
, txInfoData =
|
, txInfoData =
|
||||||
datumPair
|
datumPair
|
||||||
|
|
@ -565,6 +589,8 @@ mutateState =
|
||||||
GovernorDatum
|
GovernorDatum
|
||||||
{ proposalThresholds = defaultProposalThresholds
|
{ proposalThresholds = defaultProposalThresholds
|
||||||
, nextProposalId = ProposalId 5
|
, nextProposalId = ProposalId 5
|
||||||
|
, proposalTimings = def
|
||||||
|
, createProposalTimeRangeMaxWidth = def
|
||||||
}
|
}
|
||||||
governorInputDatum :: Datum
|
governorInputDatum :: Datum
|
||||||
governorInputDatum = Datum $ toBuiltinData governorInputDatum'
|
governorInputDatum = Datum $ toBuiltinData governorInputDatum'
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,14 @@ module Sample.Proposal (
|
||||||
) where
|
) where
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
import Plutarch.Api.V1 (
|
import Plutarch.Api.V1 (
|
||||||
validatorHash,
|
validatorHash,
|
||||||
)
|
)
|
||||||
|
import Plutarch.SafeMoney (Tagged (Tagged), untag)
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
import Plutus.V1.Ledger.Api (
|
import Plutus.V1.Ledger.Api (
|
||||||
Address (Address),
|
Address (Address),
|
||||||
Credential (ScriptCredential),
|
Credential (ScriptCredential),
|
||||||
|
|
@ -33,13 +38,13 @@ import Plutus.V1.Ledger.Api (
|
||||||
TxOut (TxOut, txOutAddress, txOutDatumHash, txOutValue),
|
TxOut (TxOut, txOutAddress, txOutDatumHash, txOutValue),
|
||||||
TxOutRef (TxOutRef),
|
TxOutRef (TxOutRef),
|
||||||
)
|
)
|
||||||
import Plutus.V1.Ledger.Interval qualified as Interval
|
|
||||||
import Plutus.V1.Ledger.Value qualified as Value
|
import Plutus.V1.Ledger.Value qualified as Value
|
||||||
|
import PlutusTx.AssocMap qualified as AssocMap
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
import Agora.Governor (
|
import Agora.Governor (
|
||||||
GovernorDatum (GovernorDatum, nextProposalId, proposalThresholds),
|
GovernorDatum (..),
|
||||||
)
|
)
|
||||||
import Agora.Proposal (
|
import Agora.Proposal (
|
||||||
Proposal (..),
|
Proposal (..),
|
||||||
|
|
@ -50,15 +55,20 @@ import Agora.Proposal (
|
||||||
ResultTag (..),
|
ResultTag (..),
|
||||||
emptyVotesFor,
|
emptyVotesFor,
|
||||||
)
|
)
|
||||||
import Agora.Proposal.Time (ProposalTimingConfig (..))
|
import Agora.Proposal.Time (ProposalStartingTime (ProposalStartingTime), ProposalTimingConfig (..))
|
||||||
import Agora.Stake (ProposalLock (ProposalLock), Stake (..), StakeDatum (..))
|
import Agora.Stake (ProposalLock (ProposalLock), Stake (..), StakeDatum (..))
|
||||||
import Plutarch.SafeMoney (Tagged (Tagged), untag)
|
|
||||||
import PlutusTx.AssocMap qualified as AssocMap
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
import Sample.Shared
|
import Sample.Shared
|
||||||
import Test.Util (closedBoundedInterval, datumPair, toDatumHash, updateMap)
|
import Test.Util (closedBoundedInterval, datumPair, toDatumHash, updateMap)
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
import Data.Default.Class (Default (def))
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
-- | This script context should be a valid transaction.
|
-- | This script context should be a valid transaction.
|
||||||
proposalCreation :: ScriptContext
|
proposalCreation :: ScriptContext
|
||||||
proposalCreation =
|
proposalCreation =
|
||||||
|
|
@ -79,8 +89,8 @@ proposalCreation =
|
||||||
, cosigners = [signer]
|
, cosigners = [signer]
|
||||||
, thresholds = defaultProposalThresholds
|
, thresholds = defaultProposalThresholds
|
||||||
, votes = emptyVotesFor effects
|
, votes = emptyVotesFor effects
|
||||||
, timingConfig = proposalTimingConfig
|
, timingConfig = def
|
||||||
, startingTime = tmpProposalStartingTime
|
, startingTime = proposalStartingTimeFromTimeRange validTimeRange
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -91,6 +101,8 @@ proposalCreation =
|
||||||
GovernorDatum
|
GovernorDatum
|
||||||
{ proposalThresholds = defaultProposalThresholds
|
{ proposalThresholds = defaultProposalThresholds
|
||||||
, nextProposalId = ProposalId 0
|
, nextProposalId = ProposalId 0
|
||||||
|
, proposalTimings = def
|
||||||
|
, createProposalTimeRangeMaxWidth = def
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
govAfter :: Datum
|
govAfter :: Datum
|
||||||
|
|
@ -100,8 +112,12 @@ proposalCreation =
|
||||||
GovernorDatum
|
GovernorDatum
|
||||||
{ proposalThresholds = defaultProposalThresholds
|
{ proposalThresholds = defaultProposalThresholds
|
||||||
, nextProposalId = ProposalId 1
|
, nextProposalId = ProposalId 1
|
||||||
|
, proposalTimings = def
|
||||||
|
, createProposalTimeRangeMaxWidth = def
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
validTimeRange = closedBoundedInterval 10 15
|
||||||
in ScriptContext
|
in ScriptContext
|
||||||
{ scriptContextTxInfo =
|
{ scriptContextTxInfo =
|
||||||
TxInfo
|
TxInfo
|
||||||
|
|
@ -138,7 +154,7 @@ proposalCreation =
|
||||||
, txInfoMint = st
|
, txInfoMint = st
|
||||||
, txInfoDCert = []
|
, txInfoDCert = []
|
||||||
, txInfoWdrl = []
|
, txInfoWdrl = []
|
||||||
, txInfoValidRange = Interval.always
|
, txInfoValidRange = validTimeRange
|
||||||
, txInfoSignatories = [signer]
|
, txInfoSignatories = [signer]
|
||||||
, txInfoData =
|
, txInfoData =
|
||||||
[ datumPair proposalDatum
|
[ datumPair proposalDatum
|
||||||
|
|
@ -174,8 +190,8 @@ cosignProposal newSigners =
|
||||||
, cosigners = [signer]
|
, cosigners = [signer]
|
||||||
, thresholds = defaultProposalThresholds
|
, thresholds = defaultProposalThresholds
|
||||||
, votes = emptyVotesFor effects
|
, votes = emptyVotesFor effects
|
||||||
, timingConfig = proposalTimingConfig
|
, timingConfig = def
|
||||||
, startingTime = tmpProposalStartingTime
|
, startingTime = ProposalStartingTime 0
|
||||||
}
|
}
|
||||||
stakeDatum :: StakeDatum
|
stakeDatum :: StakeDatum
|
||||||
stakeDatum = StakeDatum (Tagged 50_000_000) signer2 []
|
stakeDatum = StakeDatum (Tagged 50_000_000) signer2 []
|
||||||
|
|
@ -185,7 +201,7 @@ cosignProposal newSigners =
|
||||||
validTimeRange =
|
validTimeRange =
|
||||||
closedBoundedInterval
|
closedBoundedInterval
|
||||||
10
|
10
|
||||||
(proposalTimingConfig.draftTime - 10)
|
((def :: ProposalTimingConfig).draftTime - 10)
|
||||||
in TxInfo
|
in TxInfo
|
||||||
{ txInfoInputs =
|
{ txInfoInputs =
|
||||||
[ TxInInfo
|
[ TxInInfo
|
||||||
|
|
@ -295,8 +311,8 @@ voteOnProposal params =
|
||||||
, cosigners = [stakeOwner]
|
, cosigners = [stakeOwner]
|
||||||
, thresholds = defaultProposalThresholds
|
, thresholds = defaultProposalThresholds
|
||||||
, votes = ProposalVotes initialVotes
|
, votes = ProposalVotes initialVotes
|
||||||
, timingConfig = proposalTimingConfig
|
, timingConfig = def
|
||||||
, startingTime = tmpProposalStartingTime
|
, startingTime = ProposalStartingTime 0
|
||||||
}
|
}
|
||||||
proposalInputDatum :: Datum
|
proposalInputDatum :: Datum
|
||||||
proposalInputDatum = Datum $ toBuiltinData proposalInputDatum'
|
proposalInputDatum = Datum $ toBuiltinData proposalInputDatum'
|
||||||
|
|
@ -384,7 +400,9 @@ voteOnProposal params =
|
||||||
---
|
---
|
||||||
|
|
||||||
validTimeRange =
|
validTimeRange =
|
||||||
closedBoundedInterval (proposalTimingConfig.draftTime + 1) (proposalTimingConfig.votingTime - 1)
|
closedBoundedInterval
|
||||||
|
((def :: ProposalTimingConfig).draftTime + 1)
|
||||||
|
((def :: ProposalTimingConfig).votingTime - 1)
|
||||||
in TxInfo
|
in TxInfo
|
||||||
{ txInfoInputs =
|
{ txInfoInputs =
|
||||||
[ TxInInfo proposalRef proposalInput
|
[ TxInInfo proposalRef proposalInput
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
{-# OPTIONS_GHC -Wno-orphans #-}
|
||||||
|
|
||||||
{- |
|
{- |
|
||||||
Module : Sample.Shared
|
Module : Sample.Shared
|
||||||
Maintainer : emi@haskell.fyi
|
Maintainer : emi@haskell.fyi
|
||||||
|
|
@ -36,8 +38,7 @@ module Sample.Shared (
|
||||||
proposalPolicySymbol,
|
proposalPolicySymbol,
|
||||||
proposalValidatorHash,
|
proposalValidatorHash,
|
||||||
proposalValidatorAddress,
|
proposalValidatorAddress,
|
||||||
proposalTimingConfig,
|
proposalStartingTimeFromTimeRange,
|
||||||
tmpProposalStartingTime,
|
|
||||||
|
|
||||||
-- ** Authority
|
-- ** Authority
|
||||||
authorityToken,
|
authorityToken,
|
||||||
|
|
@ -77,12 +78,14 @@ import Agora.Proposal (
|
||||||
ProposalThresholds (..),
|
ProposalThresholds (..),
|
||||||
)
|
)
|
||||||
import Agora.Proposal.Time (
|
import Agora.Proposal.Time (
|
||||||
ProposalStartingTime (..),
|
MaxTimeRangeWidth (..),
|
||||||
|
ProposalStartingTime (ProposalStartingTime),
|
||||||
ProposalTimingConfig (..),
|
ProposalTimingConfig (..),
|
||||||
)
|
)
|
||||||
import Agora.Stake (Stake (..))
|
import Agora.Stake (Stake (..))
|
||||||
import Agora.Treasury (treasuryValidator)
|
import Agora.Treasury (treasuryValidator)
|
||||||
import Agora.Utils (validatorHashToTokenName)
|
import Agora.Utils (validatorHashToTokenName)
|
||||||
|
import Data.Default.Class (Default (..))
|
||||||
import Plutarch.Api.V1 (
|
import Plutarch.Api.V1 (
|
||||||
mintingPolicySymbol,
|
mintingPolicySymbol,
|
||||||
mkMintingPolicy,
|
mkMintingPolicy,
|
||||||
|
|
@ -95,9 +98,14 @@ import Plutus.V1.Ledger.Api (
|
||||||
Address (Address),
|
Address (Address),
|
||||||
Credential (ScriptCredential),
|
Credential (ScriptCredential),
|
||||||
CurrencySymbol,
|
CurrencySymbol,
|
||||||
|
Extended (..),
|
||||||
|
Interval (..),
|
||||||
|
LowerBound (..),
|
||||||
MintingPolicy (..),
|
MintingPolicy (..),
|
||||||
|
POSIXTimeRange,
|
||||||
PubKeyHash,
|
PubKeyHash,
|
||||||
TxOutRef (TxOutRef),
|
TxOutRef (TxOutRef),
|
||||||
|
UpperBound (..),
|
||||||
Value,
|
Value,
|
||||||
)
|
)
|
||||||
import Plutus.V1.Ledger.Contexts (
|
import Plutus.V1.Ledger.Contexts (
|
||||||
|
|
@ -190,21 +198,32 @@ authorityToken = authorityTokenFromGovernor governor
|
||||||
authorityTokenSymbol :: CurrencySymbol
|
authorityTokenSymbol :: CurrencySymbol
|
||||||
authorityTokenSymbol = authorityTokenSymbolFromGovernor governor
|
authorityTokenSymbol = authorityTokenSymbolFromGovernor governor
|
||||||
|
|
||||||
proposalTimingConfig :: ProposalTimingConfig
|
{- | Default value of 'Agora.Governor.GovernorDatum.proposalTimings'.
|
||||||
proposalTimingConfig =
|
For testing purpose only.
|
||||||
ProposalTimingConfig
|
|
||||||
{ draftTime = 50
|
|
||||||
, votingTime = 1000
|
|
||||||
, lockingTime = 2000
|
|
||||||
, executingTime = 3000
|
|
||||||
}
|
|
||||||
|
|
||||||
{- | Hard coded starting time of every propoal.
|
|
||||||
This will be calculated by the governor in the future.
|
|
||||||
FIXME: Remove this.
|
|
||||||
-}
|
-}
|
||||||
tmpProposalStartingTime :: ProposalStartingTime
|
instance Default ProposalTimingConfig where
|
||||||
tmpProposalStartingTime = ProposalStartingTime 0
|
def =
|
||||||
|
ProposalTimingConfig
|
||||||
|
{ draftTime = 50
|
||||||
|
, votingTime = 1000
|
||||||
|
, lockingTime = 2000
|
||||||
|
, executingTime = 3000
|
||||||
|
}
|
||||||
|
|
||||||
|
{- | Default value of 'Agora.Governor.GovernorDatum.createProposalTimeRangeMaxWidth'.
|
||||||
|
For testing purpose only.
|
||||||
|
-}
|
||||||
|
instance Default MaxTimeRangeWidth where
|
||||||
|
def = MaxTimeRangeWidth 10
|
||||||
|
|
||||||
|
{- | Get the starting time of a proposal, given a closed finite time range.
|
||||||
|
Tightness of the time range is not checked. See 'Agora.Proposal.Time.createProposalStartingTime'.
|
||||||
|
-}
|
||||||
|
proposalStartingTimeFromTimeRange :: POSIXTimeRange -> ProposalStartingTime
|
||||||
|
proposalStartingTimeFromTimeRange
|
||||||
|
(Interval (LowerBound (Finite l) True) (UpperBound (Finite u) True)) =
|
||||||
|
ProposalStartingTime $ (l + u) `div` 2
|
||||||
|
proposalStartingTimeFromTimeRange _ = error "Given time range should be finite and closed"
|
||||||
|
|
||||||
------------------------------------------------------------------
|
------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import Agora.Effect.GovernorMutation (mutateGovernorValidator)
|
||||||
import Agora.Governor (GovernorDatum (..), GovernorRedeemer (MutateGovernor))
|
import Agora.Governor (GovernorDatum (..), GovernorRedeemer (MutateGovernor))
|
||||||
import Agora.Governor.Scripts (governorValidator)
|
import Agora.Governor.Scripts (governorValidator)
|
||||||
import Agora.Proposal (ProposalId (..))
|
import Agora.Proposal (ProposalId (..))
|
||||||
|
import Data.Default.Class (Default (def))
|
||||||
import Plutus.V1.Ledger.Api (ScriptContext (ScriptContext), ScriptPurpose (Spending))
|
import Plutus.V1.Ledger.Api (ScriptContext (ScriptContext), ScriptPurpose (Spending))
|
||||||
import Sample.Effect.GovernorMutation (
|
import Sample.Effect.GovernorMutation (
|
||||||
effectRef,
|
effectRef,
|
||||||
|
|
@ -27,9 +28,10 @@ tests =
|
||||||
"governor validator should pass"
|
"governor validator should pass"
|
||||||
(governorValidator Shared.governor)
|
(governorValidator Shared.governor)
|
||||||
( GovernorDatum
|
( GovernorDatum
|
||||||
{ proposalThresholds = Shared.defaultProposalThresholds
|
Shared.defaultProposalThresholds
|
||||||
, nextProposalId = ProposalId 0
|
(ProposalId 0)
|
||||||
}
|
def
|
||||||
|
def
|
||||||
)
|
)
|
||||||
MutateGovernor
|
MutateGovernor
|
||||||
( ScriptContext
|
( ScriptContext
|
||||||
|
|
@ -48,9 +50,10 @@ tests =
|
||||||
"governor validator should fail"
|
"governor validator should fail"
|
||||||
(governorValidator Shared.governor)
|
(governorValidator Shared.governor)
|
||||||
( GovernorDatum
|
( GovernorDatum
|
||||||
{ proposalThresholds = Shared.defaultProposalThresholds
|
Shared.defaultProposalThresholds
|
||||||
, nextProposalId = ProposalId 0
|
(ProposalId 0)
|
||||||
}
|
def
|
||||||
|
def
|
||||||
)
|
)
|
||||||
MutateGovernor
|
MutateGovernor
|
||||||
( ScriptContext
|
( ScriptContext
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ module Spec.Governor (tests) where
|
||||||
import Agora.Governor (GovernorDatum (..), GovernorRedeemer (..))
|
import Agora.Governor (GovernorDatum (..), GovernorRedeemer (..))
|
||||||
import Agora.Governor.Scripts (governorPolicy, governorValidator)
|
import Agora.Governor.Scripts (governorPolicy, governorValidator)
|
||||||
import Agora.Proposal (ProposalId (..))
|
import Agora.Proposal (ProposalId (..))
|
||||||
|
import Data.Default.Class (Default (def))
|
||||||
import Sample.Governor (createProposal, mintGATs, mintGST, mutateState)
|
import Sample.Governor (createProposal, mintGATs, mintGST, mutateState)
|
||||||
import Sample.Shared qualified as Shared
|
import Sample.Shared qualified as Shared
|
||||||
import Test.Tasty (TestTree, testGroup)
|
import Test.Tasty (TestTree, testGroup)
|
||||||
|
|
@ -38,19 +39,34 @@ tests =
|
||||||
[ validatorSucceedsWith
|
[ validatorSucceedsWith
|
||||||
"proposal creation"
|
"proposal creation"
|
||||||
(governorValidator Shared.governor)
|
(governorValidator Shared.governor)
|
||||||
(GovernorDatum Shared.defaultProposalThresholds (ProposalId 0))
|
( GovernorDatum
|
||||||
|
Shared.defaultProposalThresholds
|
||||||
|
(ProposalId 0)
|
||||||
|
def
|
||||||
|
def
|
||||||
|
)
|
||||||
CreateProposal
|
CreateProposal
|
||||||
createProposal
|
createProposal
|
||||||
, validatorSucceedsWith
|
, validatorSucceedsWith
|
||||||
"GATs minting"
|
"GATs minting"
|
||||||
(governorValidator Shared.governor)
|
(governorValidator Shared.governor)
|
||||||
(GovernorDatum Shared.defaultProposalThresholds (ProposalId 5))
|
( GovernorDatum
|
||||||
|
Shared.defaultProposalThresholds
|
||||||
|
(ProposalId 5)
|
||||||
|
def
|
||||||
|
def
|
||||||
|
)
|
||||||
MintGATs
|
MintGATs
|
||||||
mintGATs
|
mintGATs
|
||||||
, validatorSucceedsWith
|
, validatorSucceedsWith
|
||||||
"mutate governor state"
|
"mutate governor state"
|
||||||
(governorValidator Shared.governor)
|
(governorValidator Shared.governor)
|
||||||
(GovernorDatum Shared.defaultProposalThresholds (ProposalId 5))
|
( GovernorDatum
|
||||||
|
Shared.defaultProposalThresholds
|
||||||
|
(ProposalId 5)
|
||||||
|
def
|
||||||
|
def
|
||||||
|
)
|
||||||
MutateGovernor
|
MutateGovernor
|
||||||
mutateState
|
mutateState
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -31,12 +31,14 @@ import Agora.Proposal.Scripts (
|
||||||
proposalPolicy,
|
proposalPolicy,
|
||||||
proposalValidator,
|
proposalValidator,
|
||||||
)
|
)
|
||||||
|
import Agora.Proposal.Time (ProposalStartingTime (ProposalStartingTime))
|
||||||
import Agora.Stake (
|
import Agora.Stake (
|
||||||
ProposalLock (ProposalLock),
|
ProposalLock (ProposalLock),
|
||||||
StakeDatum (StakeDatum),
|
StakeDatum (StakeDatum),
|
||||||
StakeRedeemer (PermitVote, WitnessStake),
|
StakeRedeemer (PermitVote, WitnessStake),
|
||||||
)
|
)
|
||||||
import Agora.Stake.Scripts (stakeValidator)
|
import Agora.Stake.Scripts (stakeValidator)
|
||||||
|
import Data.Default.Class (Default (def))
|
||||||
import Plutarch.SafeMoney (Tagged (Tagged))
|
import Plutarch.SafeMoney (Tagged (Tagged))
|
||||||
import Plutus.V1.Ledger.Api (ScriptContext (..), ScriptPurpose (..))
|
import Plutus.V1.Ledger.Api (ScriptContext (..), ScriptPurpose (..))
|
||||||
import PlutusTx.AssocMap qualified as AssocMap
|
import PlutusTx.AssocMap qualified as AssocMap
|
||||||
|
|
@ -82,8 +84,8 @@ tests =
|
||||||
[ (ResultTag 0, AssocMap.empty)
|
[ (ResultTag 0, AssocMap.empty)
|
||||||
, (ResultTag 1, AssocMap.empty)
|
, (ResultTag 1, AssocMap.empty)
|
||||||
]
|
]
|
||||||
, timingConfig = Shared.proposalTimingConfig
|
, timingConfig = def
|
||||||
, startingTime = Shared.tmpProposalStartingTime
|
, startingTime = ProposalStartingTime 0
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
(Cosign [signer2])
|
(Cosign [signer2])
|
||||||
|
|
@ -117,8 +119,8 @@ tests =
|
||||||
, (ResultTag 1, 4242)
|
, (ResultTag 1, 4242)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
, timingConfig = Shared.proposalTimingConfig
|
, timingConfig = def
|
||||||
, startingTime = Shared.tmpProposalStartingTime
|
, startingTime = ProposalStartingTime 0
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
(Vote (ResultTag 0))
|
(Vote (ResultTag 0))
|
||||||
|
|
|
||||||
12
agora.cabal
12
agora.cabal
|
|
@ -114,6 +114,7 @@ common test-deps
|
||||||
, agora
|
, agora
|
||||||
, apropos
|
, apropos
|
||||||
, apropos-tx
|
, apropos-tx
|
||||||
|
, data-default-class
|
||||||
, mtl
|
, mtl
|
||||||
, QuickCheck
|
, QuickCheck
|
||||||
, quickcheck-instances
|
, quickcheck-instances
|
||||||
|
|
@ -141,22 +142,20 @@ library
|
||||||
Agora.Proposal.Time
|
Agora.Proposal.Time
|
||||||
Agora.Record
|
Agora.Record
|
||||||
Agora.SafeMoney
|
Agora.SafeMoney
|
||||||
|
Agora.ScriptInfo
|
||||||
Agora.Stake
|
Agora.Stake
|
||||||
Agora.Stake.Scripts
|
Agora.Stake.Scripts
|
||||||
Agora.Treasury
|
Agora.Treasury
|
||||||
Agora.Utils
|
Agora.Utils
|
||||||
Agora.Utils.Value
|
Agora.Utils.Value
|
||||||
Agora.ScriptInfo
|
|
||||||
|
|
||||||
other-modules:
|
other-modules: Agora.Aeson.Orphans
|
||||||
Agora.Aeson.Orphans
|
|
||||||
hs-source-dirs: agora
|
hs-source-dirs: agora
|
||||||
|
|
||||||
library pprelude
|
library pprelude
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
exposed-modules: PPrelude
|
exposed-modules: PPrelude
|
||||||
hs-source-dirs: agora
|
hs-source-dirs: agora
|
||||||
|
|
||||||
build-depends:
|
build-depends:
|
||||||
, base
|
, base
|
||||||
, plutarch
|
, plutarch
|
||||||
|
|
@ -176,8 +175,8 @@ library agora-sample
|
||||||
Sample.Shared
|
Sample.Shared
|
||||||
Sample.Stake
|
Sample.Stake
|
||||||
Sample.Treasury
|
Sample.Treasury
|
||||||
hs-source-dirs: agora-sample
|
|
||||||
|
|
||||||
|
hs-source-dirs: agora-sample
|
||||||
build-depends: agora-testlib
|
build-depends: agora-testlib
|
||||||
|
|
||||||
test-suite agora-test
|
test-suite agora-test
|
||||||
|
|
@ -214,8 +213,7 @@ executable agora-scripts
|
||||||
import: lang, deps, exe-opts
|
import: lang, deps, exe-opts
|
||||||
main-is: Scripts.hs
|
main-is: Scripts.hs
|
||||||
hs-source-dirs: agora-scripts
|
hs-source-dirs: agora-scripts
|
||||||
other-modules:
|
other-modules: Options
|
||||||
Options
|
|
||||||
build-depends:
|
build-depends:
|
||||||
, agora
|
, agora
|
||||||
, optparse-applicative
|
, optparse-applicative
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,12 @@ import Agora.Proposal (
|
||||||
ProposalId (ProposalId),
|
ProposalId (ProposalId),
|
||||||
ProposalThresholds,
|
ProposalThresholds,
|
||||||
)
|
)
|
||||||
|
import Agora.Proposal.Time (
|
||||||
|
MaxTimeRangeWidth,
|
||||||
|
PMaxTimeRangeWidth,
|
||||||
|
PProposalTimingConfig,
|
||||||
|
ProposalTimingConfig,
|
||||||
|
)
|
||||||
import Agora.SafeMoney (GTTag)
|
import Agora.SafeMoney (GTTag)
|
||||||
import Agora.Utils (tclet)
|
import Agora.Utils (tclet)
|
||||||
|
|
||||||
|
|
@ -66,6 +72,11 @@ data GovernorDatum = GovernorDatum
|
||||||
-- ^ Gets copied over upon creation of a 'Agora.Proposal.ProposalDatum'.
|
-- ^ Gets copied over upon creation of a 'Agora.Proposal.ProposalDatum'.
|
||||||
, nextProposalId :: ProposalId
|
, nextProposalId :: ProposalId
|
||||||
-- ^ What tag the next proposal will get upon creating.
|
-- ^ What tag the next proposal will get upon creating.
|
||||||
|
, proposalTimings :: ProposalTimingConfig
|
||||||
|
-- ^ The timing configuration for proposals.
|
||||||
|
-- Will get copied over upon the creation of proposals.
|
||||||
|
, createProposalTimeRangeMaxWidth :: MaxTimeRangeWidth
|
||||||
|
-- ^ The maximum valid duration of a transaction that creats a proposal.
|
||||||
}
|
}
|
||||||
deriving stock (Show, GHC.Generic)
|
deriving stock (Show, GHC.Generic)
|
||||||
|
|
||||||
|
|
@ -118,6 +129,8 @@ newtype PGovernorDatum (s :: S) = PGovernorDatum
|
||||||
( PDataRecord
|
( PDataRecord
|
||||||
'[ "proposalThresholds" ':= PProposalThresholds
|
'[ "proposalThresholds" ':= PProposalThresholds
|
||||||
, "nextProposalId" ':= PProposalId
|
, "nextProposalId" ':= PProposalId
|
||||||
|
, "proposalTimings" ':= PProposalTimingConfig
|
||||||
|
, "createProposalTimeRangeMaxWidth" ':= PMaxTimeRangeWidth
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,12 +51,14 @@ import Agora.Proposal (
|
||||||
PResultTag,
|
PResultTag,
|
||||||
Proposal (..),
|
Proposal (..),
|
||||||
ProposalStatus (Draft, Locked),
|
ProposalStatus (Draft, Locked),
|
||||||
|
pemptyVotesFor,
|
||||||
proposalDatumValid,
|
proposalDatumValid,
|
||||||
)
|
)
|
||||||
import Agora.Proposal.Scripts (
|
import Agora.Proposal.Scripts (
|
||||||
proposalPolicy,
|
proposalPolicy,
|
||||||
proposalValidator,
|
proposalValidator,
|
||||||
)
|
)
|
||||||
|
import Agora.Proposal.Time (createProposalStartingTime)
|
||||||
import Agora.Record
|
import Agora.Record
|
||||||
import Agora.SafeMoney (GTTag)
|
import Agora.SafeMoney (GTTag)
|
||||||
import Agora.Stake (
|
import Agora.Stake (
|
||||||
|
|
@ -126,7 +128,6 @@ import Plutarch.TryFrom (ptryFrom)
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
import Agora.Proposal.Time (ProposalStartingTime (..), ProposalTimingConfig (..))
|
|
||||||
import Plutus.V1.Ledger.Api (
|
import Plutus.V1.Ledger.Api (
|
||||||
CurrencySymbol (..),
|
CurrencySymbol (..),
|
||||||
MintingPolicy,
|
MintingPolicy,
|
||||||
|
|
@ -172,7 +173,7 @@ governorPolicy gov =
|
||||||
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"] txInfo
|
txInfoF <- tcont $ pletFields @'["mint", "inputs", "outputs", "datums", "validRange"] txInfo
|
||||||
|
|
||||||
tcassert "Referenced utxo should be spent" $
|
tcassert "Referenced utxo should be spent" $
|
||||||
pisUTXOSpent # oref # txInfoF.inputs
|
pisUTXOSpent # oref # txInfoF.inputs
|
||||||
|
|
@ -288,7 +289,7 @@ governorValidator gov =
|
||||||
ctxF <- tcont $ pletFields @'["txInfo", "purpose"] ctx'
|
ctxF <- tcont $ pletFields @'["txInfo", "purpose"] ctx'
|
||||||
|
|
||||||
txInfo' <- tclet $ pfromData $ ctxF.txInfo
|
txInfo' <- tclet $ pfromData $ ctxF.txInfo
|
||||||
txInfoF <- tcont $ pletFields @'["mint", "inputs", "outputs", "datums", "signatories"] 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) <- tcmatch $ pfromData ctxF.purpose
|
||||||
|
|
||||||
|
|
@ -300,7 +301,15 @@ governorValidator gov =
|
||||||
let ownAddress = pfromData $ ownInputF.address
|
let ownAddress = pfromData $ ownInputF.address
|
||||||
|
|
||||||
(pfromData -> (oldGovernorDatum :: Term _ PGovernorDatum), _) <- tcont $ ptryFrom datum'
|
(pfromData -> (oldGovernorDatum :: Term _ PGovernorDatum), _) <- tcont $ ptryFrom datum'
|
||||||
oldGovernorDatumF <- tcont $ pletFields @'["proposalThresholds", "nextProposalId"] oldGovernorDatum
|
oldGovernorDatumF <-
|
||||||
|
tcont $
|
||||||
|
pletFields
|
||||||
|
@'[ "proposalThresholds"
|
||||||
|
, "nextProposalId"
|
||||||
|
, "proposalTimings"
|
||||||
|
, "createProposalTimeRangeMaxWidth"
|
||||||
|
]
|
||||||
|
oldGovernorDatum
|
||||||
|
|
||||||
-- 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
|
||||||
|
|
@ -337,6 +346,9 @@ governorValidator gov =
|
||||||
PGovernorDatum
|
PGovernorDatum
|
||||||
( #proposalThresholds .= oldGovernorDatumF.proposalThresholds
|
( #proposalThresholds .= oldGovernorDatumF.proposalThresholds
|
||||||
.& #nextProposalId .= pdata expectedNextProposalId
|
.& #nextProposalId .= pdata expectedNextProposalId
|
||||||
|
.& #proposalTimings .= oldGovernorDatumF.proposalTimings
|
||||||
|
.& #createProposalTimeRangeMaxWidth
|
||||||
|
.= oldGovernorDatumF.createProposalTimeRangeMaxWidth
|
||||||
)
|
)
|
||||||
tcassert "Unexpected governor state datum" $
|
tcassert "Unexpected governor state datum" $
|
||||||
newGovernorDatum #== expectedNewDatum
|
newGovernorDatum #== expectedNewDatum
|
||||||
|
|
@ -402,9 +414,6 @@ governorValidator gov =
|
||||||
|
|
||||||
outputDatumHash <- tclet $ pfield @"datumHash" #$ phead # outputsToProposalValidatorWithStateToken
|
outputDatumHash <- tclet $ pfield @"datumHash" #$ phead # outputsToProposalValidatorWithStateToken
|
||||||
|
|
||||||
tcassert "The utxo paid to the proposal validator must have datum" $
|
|
||||||
pisDJust # outputDatumHash
|
|
||||||
|
|
||||||
proposalOutputDatum' <-
|
proposalOutputDatum' <-
|
||||||
tclet $
|
tclet $
|
||||||
mustFindDatum' @PProposalDatum
|
mustFindDatum' @PProposalDatum
|
||||||
|
|
@ -417,23 +426,34 @@ governorValidator gov =
|
||||||
proposalOutputDatum <-
|
proposalOutputDatum <-
|
||||||
tcont $
|
tcont $
|
||||||
pletFields
|
pletFields
|
||||||
@'["proposalId", "status", "cosigners", "thresholds", "votes"]
|
@'["effects", "cosigners", "proposalId", "votes"]
|
||||||
proposalOutputDatum'
|
proposalOutputDatum'
|
||||||
|
|
||||||
-- Id and thresholds should be copied from the old governor state datum.
|
|
||||||
tcassert "Invalid proposal id in proposal datum" $
|
|
||||||
proposalOutputDatum.proposalId #== oldGovernorDatumF.nextProposalId
|
|
||||||
|
|
||||||
tcassert "Invalid thresholds in proposal datum" $
|
|
||||||
proposalOutputDatum.thresholds #== oldGovernorDatumF.proposalThresholds
|
|
||||||
|
|
||||||
-- The proposal at this point should be in draft state.
|
|
||||||
tcassert "Proposal state should be draft" $
|
|
||||||
proposalOutputDatum.status #== pconstantData Draft
|
|
||||||
|
|
||||||
tcassert "Proposal should have only one cosigner" $
|
tcassert "Proposal should have only one cosigner" $
|
||||||
plength # pfromData proposalOutputDatum.cosigners #== 1
|
plength # pfromData proposalOutputDatum.cosigners #== 1
|
||||||
|
|
||||||
|
let -- Votes should be empty at this point
|
||||||
|
expectedVotes = pemptyVotesFor # pfromData proposalOutputDatum.effects
|
||||||
|
expectedStartingTime =
|
||||||
|
createProposalStartingTime
|
||||||
|
# oldGovernorDatumF.createProposalTimeRangeMaxWidth
|
||||||
|
# txInfoF.validRange
|
||||||
|
-- Id, thresholds and timings should be copied from the old governor state datum.
|
||||||
|
expectedProposalOut =
|
||||||
|
mkRecordConstr
|
||||||
|
PProposalDatum
|
||||||
|
( #proposalId .= oldGovernorDatumF.nextProposalId
|
||||||
|
.& #effects .= proposalOutputDatum.effects
|
||||||
|
.& #status .= pconstantData Draft
|
||||||
|
.& #cosigners .= proposalOutputDatum.cosigners
|
||||||
|
.& #thresholds .= oldGovernorDatumF.proposalThresholds
|
||||||
|
.& #votes .= pdata expectedVotes
|
||||||
|
.& #timingConfig .= oldGovernorDatumF.proposalTimings
|
||||||
|
.& #startingTime .= pdata expectedStartingTime
|
||||||
|
)
|
||||||
|
|
||||||
|
tcassert "Datum correct" $ expectedProposalOut #== proposalOutputDatum'
|
||||||
|
|
||||||
let cosigner = phead # pfromData proposalOutputDatum.cosigners
|
let cosigner = phead # pfromData proposalOutputDatum.cosigners
|
||||||
|
|
||||||
tcassert "Cosigner should be the stake owner" $
|
tcassert "Cosigner should be the stake owner" $
|
||||||
|
|
@ -560,7 +580,7 @@ governorValidator gov =
|
||||||
|
|
||||||
proposalInputDatumF <-
|
proposalInputDatumF <-
|
||||||
tcont $
|
tcont $
|
||||||
pletFields @'["proposalId", "effects", "status", "cosigners", "thresholds", "votes"]
|
pletFields @'["proposalId", "effects", "status", "cosigners", "thresholds", "votes", "timingConfig", "startingTime"]
|
||||||
proposalInputDatum
|
proposalInputDatum
|
||||||
|
|
||||||
-- 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.
|
||||||
|
|
@ -577,10 +597,8 @@ governorValidator gov =
|
||||||
.& #cosigners .= proposalInputDatumF.cosigners
|
.& #cosigners .= proposalInputDatumF.cosigners
|
||||||
.& #thresholds .= proposalInputDatumF.thresholds
|
.& #thresholds .= proposalInputDatumF.thresholds
|
||||||
.& #votes .= proposalInputDatumF.votes
|
.& #votes .= proposalInputDatumF.votes
|
||||||
-- FIXME: copy from the governor datum
|
.& #timingConfig .= proposalInputDatumF.timingConfig
|
||||||
.& #timingConfig .= pdata (pconstant tmpTimingConfig)
|
.& #startingTime .= proposalInputDatumF.startingTime
|
||||||
-- FIXME: calculate from 'txInfoValidRange'
|
|
||||||
.& #startingTime .= pdata (pconstant tmpProposalStartingTime)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
tcassert "Unexpected output proposal datum" $
|
tcassert "Unexpected output proposal datum" $
|
||||||
|
|
@ -732,20 +750,6 @@ governorValidator gov =
|
||||||
let sym = governorSTSymbolFromGovernor gov
|
let sym = governorSTSymbolFromGovernor gov
|
||||||
in phoistAcyclic $ pconstant sym
|
in phoistAcyclic $ pconstant sym
|
||||||
|
|
||||||
-- TODO: remove this. This is temperary.
|
|
||||||
tmpTimingConfig :: ProposalTimingConfig
|
|
||||||
tmpTimingConfig =
|
|
||||||
ProposalTimingConfig
|
|
||||||
{ draftTime = 50
|
|
||||||
, votingTime = 1000
|
|
||||||
, lockingTime = 2000
|
|
||||||
, executingTime = 3000
|
|
||||||
}
|
|
||||||
|
|
||||||
-- TODO: remove this.
|
|
||||||
tmpProposalStartingTime :: ProposalStartingTime
|
|
||||||
tmpProposalStartingTime = ProposalStartingTime 0
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
-- | Get the 'CurrencySymbol' of GST.
|
-- | Get the 'CurrencySymbol' of GST.
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ module Agora.Proposal (
|
||||||
|
|
||||||
-- * Plutarch helpers
|
-- * Plutarch helpers
|
||||||
proposalDatumValid,
|
proposalDatumValid,
|
||||||
|
pemptyVotesFor,
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import GHC.Generics qualified as GHC
|
import GHC.Generics qualified as GHC
|
||||||
|
|
@ -47,7 +48,7 @@ import PlutusTx.AssocMap qualified as AssocMap
|
||||||
|
|
||||||
import Agora.Proposal.Time (PProposalStartingTime, PProposalTimingConfig, ProposalStartingTime, ProposalTimingConfig)
|
import Agora.Proposal.Time (PProposalStartingTime, PProposalTimingConfig, ProposalStartingTime, ProposalTimingConfig)
|
||||||
import Agora.SafeMoney (GTTag)
|
import Agora.SafeMoney (GTTag)
|
||||||
import Agora.Utils (pkeysEqual, pnotNull)
|
import Agora.Utils (pkeysEqual, pmapMap, pnotNull)
|
||||||
import Control.Applicative (Const)
|
import Control.Applicative (Const)
|
||||||
import Control.Arrow (first)
|
import Control.Arrow (first)
|
||||||
import Plutarch.DataRepr (DerivePConstantViaData (..), PDataFields, PIsDataReprInstances (..))
|
import Plutarch.DataRepr (DerivePConstantViaData (..), PDataFields, PIsDataReprInstances (..))
|
||||||
|
|
@ -348,6 +349,16 @@ deriving via
|
||||||
instance
|
instance
|
||||||
(PConstantDecl ProposalVotes)
|
(PConstantDecl ProposalVotes)
|
||||||
|
|
||||||
|
-- Plutarch-level version of 'emptyVotesFor'.
|
||||||
|
pemptyVotesFor :: forall s a. (PIsData a) => Term s (PMap PResultTag a :--> PProposalVotes)
|
||||||
|
pemptyVotesFor =
|
||||||
|
phoistAcyclic $
|
||||||
|
plam
|
||||||
|
( \m ->
|
||||||
|
pcon $
|
||||||
|
PProposalVotes $ pmapMap # plam (const $ pconstant 0) # m
|
||||||
|
)
|
||||||
|
|
||||||
-- | Plutarch-level version of 'ProposalDatum'.
|
-- | Plutarch-level version of 'ProposalDatum'.
|
||||||
newtype PProposalDatum (s :: S) = PProposalDatum
|
newtype PProposalDatum (s :: S) = PProposalDatum
|
||||||
{ getProposalDatum ::
|
{ getProposalDatum ::
|
||||||
|
|
|
||||||
|
|
@ -10,16 +10,18 @@ Time functions for proposal phases.
|
||||||
-}
|
-}
|
||||||
module Agora.Proposal.Time (
|
module Agora.Proposal.Time (
|
||||||
-- * Haskell-land
|
-- * Haskell-land
|
||||||
ProposalTime (..),
|
|
||||||
ProposalTimingConfig (..),
|
ProposalTimingConfig (..),
|
||||||
ProposalStartingTime (..),
|
ProposalStartingTime (..),
|
||||||
|
MaxTimeRangeWidth (..),
|
||||||
|
|
||||||
-- * Plutarch-land
|
-- * Plutarch-land
|
||||||
PProposalTime (..),
|
PProposalTime (..),
|
||||||
PProposalTimingConfig (..),
|
PProposalTimingConfig (..),
|
||||||
PProposalStartingTime (..),
|
PProposalStartingTime (..),
|
||||||
|
PMaxTimeRangeWidth (..),
|
||||||
|
|
||||||
-- * Compute periods given config and starting time.
|
-- * Compute periods given config and starting time.
|
||||||
|
createProposalStartingTime,
|
||||||
currentProposalTime,
|
currentProposalTime,
|
||||||
isDraftPeriod,
|
isDraftPeriod,
|
||||||
isVotingPeriod,
|
isVotingPeriod,
|
||||||
|
|
@ -27,10 +29,9 @@ module Agora.Proposal.Time (
|
||||||
isExecutionPeriod,
|
isExecutionPeriod,
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Agora.Record (mkRecordConstr, (.&), (.=))
|
import Agora.Utils (tcassert, tcmatch)
|
||||||
import Agora.Utils (tcmatch)
|
|
||||||
import GHC.Generics qualified as GHC
|
import GHC.Generics qualified as GHC
|
||||||
import Generics.SOP (Generic, I (I))
|
import Generics.SOP (Generic, HasDatatypeInfo, I (I))
|
||||||
import Plutarch.Api.V1 (
|
import Plutarch.Api.V1 (
|
||||||
PExtended (PFinite),
|
PExtended (PFinite),
|
||||||
PInterval (PInterval),
|
PInterval (PInterval),
|
||||||
|
|
@ -39,7 +40,11 @@ import Plutarch.Api.V1 (
|
||||||
PPOSIXTimeRange,
|
PPOSIXTimeRange,
|
||||||
PUpperBound (PUpperBound),
|
PUpperBound (PUpperBound),
|
||||||
)
|
)
|
||||||
import Plutarch.DataRepr (DerivePConstantViaData (..), PDataFields, PIsDataReprInstances (..))
|
import Plutarch.DataRepr (
|
||||||
|
DerivePConstantViaData (..),
|
||||||
|
PDataFields,
|
||||||
|
PIsDataReprInstances (..),
|
||||||
|
)
|
||||||
import Plutarch.Lift (
|
import Plutarch.Lift (
|
||||||
DerivePConstantViaNewtype (..),
|
DerivePConstantViaNewtype (..),
|
||||||
PConstantDecl,
|
PConstantDecl,
|
||||||
|
|
@ -53,35 +58,6 @@ import Prelude hiding ((+))
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
{- | == Establishing timing in Proposal interactions.
|
|
||||||
|
|
||||||
In Plutus, it's impossible to determine time exactly. It's also impossible
|
|
||||||
to get a single point in time, yet often we need to check
|
|
||||||
various constraints on time.
|
|
||||||
|
|
||||||
For the purposes of proposals, there's a single most important feature:
|
|
||||||
The ability to determine if we can perform an action. In order to correctly
|
|
||||||
determine if we are able to perform certain actions, we need to know what
|
|
||||||
time it roughly is, compared to when the proposal was created.
|
|
||||||
|
|
||||||
'ProposalTime' represents "the time according to the proposal".
|
|
||||||
Its representation is opaque, and doesn't matter.
|
|
||||||
|
|
||||||
Various functions work simply on 'ProposalTime' and 'ProposalTimingConfig'.
|
|
||||||
In particular, 'currentProposalTime' is useful for extracting the time
|
|
||||||
from the 'Plutus.V1.Ledger.Api.txInfoValidPeriod' field
|
|
||||||
of 'Plutus.V1.Ledger.Api.TxInfo'.
|
|
||||||
|
|
||||||
We avoid 'PPOSIXTimeRange' where we can in order to save on operations.
|
|
||||||
-}
|
|
||||||
data ProposalTime = ProposalTime
|
|
||||||
{ lowerBound :: POSIXTime
|
|
||||||
, upperBound :: POSIXTime
|
|
||||||
}
|
|
||||||
deriving stock (Eq, Show, GHC.Generic)
|
|
||||||
|
|
||||||
PlutusTx.makeIsDataIndexed ''ProposalTime [('ProposalTime, 0)]
|
|
||||||
|
|
||||||
-- | Represents the starting time of the proposal.
|
-- | Represents the starting time of the proposal.
|
||||||
newtype ProposalStartingTime = ProposalStartingTime
|
newtype ProposalStartingTime = ProposalStartingTime
|
||||||
{ getProposalStartingTime :: POSIXTime
|
{ getProposalStartingTime :: POSIXTime
|
||||||
|
|
@ -107,32 +83,40 @@ data ProposalTimingConfig = ProposalTimingConfig
|
||||||
|
|
||||||
PlutusTx.makeIsDataIndexed ''ProposalTimingConfig [('ProposalTimingConfig, 0)]
|
PlutusTx.makeIsDataIndexed ''ProposalTimingConfig [('ProposalTimingConfig, 0)]
|
||||||
|
|
||||||
|
-- | Represents the maximum width of a 'POSIXTimeRange'.
|
||||||
|
newtype MaxTimeRangeWidth = MaxTimeRangeWidth {getMaxWidth :: POSIXTime}
|
||||||
|
deriving stock (Eq, Show, Ord, GHC.Generic)
|
||||||
|
deriving newtype (PlutusTx.ToData, PlutusTx.FromData, PlutusTx.UnsafeFromData)
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
-- | Plutarch-level version of 'ProposalTime'.
|
{- | == Establishing timing in Proposal interactions.
|
||||||
newtype PProposalTime (s :: S)
|
|
||||||
= PProposalTime
|
|
||||||
( Term
|
|
||||||
s
|
|
||||||
( PDataRecord
|
|
||||||
'[ "lowerBound" ':= PPOSIXTime
|
|
||||||
, "upperBound" ':= PPOSIXTime
|
|
||||||
]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
deriving stock (GHC.Generic)
|
|
||||||
deriving anyclass (Generic)
|
|
||||||
deriving anyclass (PIsDataRepr)
|
|
||||||
deriving
|
|
||||||
(PlutusType, PIsData, PDataFields)
|
|
||||||
via (PIsDataReprInstances PProposalTime)
|
|
||||||
|
|
||||||
instance PUnsafeLiftDecl PProposalTime where
|
In Plutus, it's impossible to determine time exactly. It's also impossible
|
||||||
type PLifted PProposalTime = ProposalTime
|
to get a single point in time, yet often we need to check
|
||||||
deriving via
|
various constraints on time.
|
||||||
(DerivePConstantViaData ProposalTime PProposalTime)
|
|
||||||
instance
|
For the purposes of proposals, there's a single most important feature:
|
||||||
(PConstantDecl ProposalTime)
|
The ability to determine if we can perform an action. In order to correctly
|
||||||
|
determine if we are able to perform certain actions, we need to know what
|
||||||
|
time it roughly is, compared to when the proposal was created.
|
||||||
|
|
||||||
|
'ProposalTime' represents "the time according to the proposal".
|
||||||
|
Its representation is opaque, and doesn't matter.
|
||||||
|
|
||||||
|
Various functions work simply on 'ProposalTime' and 'ProposalTimingConfig'.
|
||||||
|
In particular, 'currentProposalTime' is useful for extracting the time
|
||||||
|
from the 'Plutus.V1.Ledger.Api.txInfoValidPeriod' field
|
||||||
|
of 'Plutus.V1.Ledger.Api.TxInfo'.
|
||||||
|
|
||||||
|
We avoid 'PPOSIXTimeRange' where we can in order to save on operations.
|
||||||
|
-}
|
||||||
|
data PProposalTime (s :: S) = PProposalTime
|
||||||
|
{ lowerBound :: Term s PPOSIXTime
|
||||||
|
, upperBound :: Term s PPOSIXTime
|
||||||
|
}
|
||||||
|
deriving stock (GHC.Generic)
|
||||||
|
deriving anyclass (Generic, PlutusType, HasDatatypeInfo, PEq)
|
||||||
|
|
||||||
-- | Plutarch-level version of 'ProposalStartingTime'.
|
-- | Plutarch-level version of 'ProposalStartingTime'.
|
||||||
newtype PProposalStartingTime (s :: S) = PProposalStartingTime (Term s PPOSIXTime)
|
newtype PProposalStartingTime (s :: S) = PProposalStartingTime (Term s PPOSIXTime)
|
||||||
|
|
@ -172,12 +156,45 @@ deriving via
|
||||||
instance
|
instance
|
||||||
(PConstantDecl ProposalTimingConfig)
|
(PConstantDecl ProposalTimingConfig)
|
||||||
|
|
||||||
|
-- | Plutarch-level version of 'MaxTimeRangeWidth'.
|
||||||
|
newtype PMaxTimeRangeWidth (s :: S)
|
||||||
|
= PMaxTimeRangeWidth (Term s PPOSIXTime)
|
||||||
|
deriving (PlutusType, PIsData, PEq, POrd) via (DerivePNewtype PMaxTimeRangeWidth PPOSIXTime)
|
||||||
|
|
||||||
|
instance PUnsafeLiftDecl PMaxTimeRangeWidth where type PLifted PMaxTimeRangeWidth = MaxTimeRangeWidth
|
||||||
|
deriving via
|
||||||
|
(DerivePConstantViaNewtype MaxTimeRangeWidth PMaxTimeRangeWidth PPOSIXTime)
|
||||||
|
instance
|
||||||
|
(PConstantDecl MaxTimeRangeWidth)
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
-- FIXME: Orphan instance, move this to plutarch-extra.
|
-- FIXME: Orphan instance, move this to plutarch-extra.
|
||||||
instance AdditiveSemigroup (Term s PPOSIXTime) where
|
instance AdditiveSemigroup (Term s PPOSIXTime) where
|
||||||
(punsafeCoerce @_ @_ @PInteger -> x) + (punsafeCoerce @_ @_ @PInteger -> y) = punsafeCoerce $ x + y
|
(punsafeCoerce @_ @_ @PInteger -> x) + (punsafeCoerce @_ @_ @PInteger -> y) = punsafeCoerce $ x + y
|
||||||
|
|
||||||
|
{- | Get the starting time of a proposal, from the 'Plutus.V1.Ledger.Api.txInfoValidPeriod' field.
|
||||||
|
For every proposal, this is only meant to run once upon creation. Given time range should be
|
||||||
|
tight enough, meaning that the width of the time range should be less than the maximum value.
|
||||||
|
-}
|
||||||
|
createProposalStartingTime :: forall (s :: S). Term s (PMaxTimeRangeWidth :--> PPOSIXTimeRange :--> PProposalStartingTime)
|
||||||
|
createProposalStartingTime = phoistAcyclic $
|
||||||
|
plam $ \(pto -> maxDuration) iv -> unTermCont $ do
|
||||||
|
currentTimeF <- tcmatch $ currentProposalTime # iv
|
||||||
|
|
||||||
|
-- Use the middle of the current time range as the starting time.
|
||||||
|
let duration = currentTimeF.upperBound - currentTimeF.lowerBound
|
||||||
|
|
||||||
|
startingTime =
|
||||||
|
pdiv
|
||||||
|
# (currentTimeF.lowerBound + currentTimeF.upperBound)
|
||||||
|
# 2
|
||||||
|
|
||||||
|
tcassert "createProposalStartingTime: given time range should be tight enough" $
|
||||||
|
duration #<= maxDuration
|
||||||
|
|
||||||
|
pure $ pcon $ PProposalStartingTime startingTime
|
||||||
|
|
||||||
{- | Get the current proposal time, from the 'Plutus.V1.Ledger.Api.txInfoValidPeriod' field.
|
{- | Get the current proposal time, from the 'Plutus.V1.Ledger.Api.txInfoValidPeriod' field.
|
||||||
|
|
||||||
If it's impossible to get a fully-bounded time, (e.g. either end of the 'PPOSIXTimeRange' is
|
If it's impossible to get a fully-bounded time, (e.g. either end of the 'PPOSIXTimeRange' is
|
||||||
|
|
@ -193,21 +210,23 @@ currentProposalTime = phoistAcyclic $
|
||||||
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 $
|
||||||
mkRecordConstr PProposalTime $
|
pcon $
|
||||||
#lowerBound
|
PProposalTime
|
||||||
.= pmatch
|
{ lowerBound =
|
||||||
lbf._0
|
pmatch
|
||||||
( \case
|
lbf._0
|
||||||
PFinite ((pfield @"_0" #) -> d) -> d
|
( \case
|
||||||
_ -> ptraceError "currentProposalTime: Can't get fully-bounded proposal time."
|
PFinite ((pfield @"_0" #) -> d) -> d
|
||||||
)
|
_ -> ptraceError "currentProposalTime: Can't get fully-bounded proposal time."
|
||||||
.& #upperBound
|
)
|
||||||
.= pmatch
|
, upperBound =
|
||||||
ubf._0
|
pmatch
|
||||||
( \case
|
ubf._0
|
||||||
PFinite ((pfield @"_0" #) -> d) -> d
|
( \case
|
||||||
_ -> ptraceError "currentProposalTime: Can't get fully-bounded proposal time."
|
PFinite ((pfield @"_0" #) -> d) -> d
|
||||||
)
|
_ -> ptraceError "currentProposalTime: Can't get fully-bounded proposal time."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
-- | Check if 'PProposalTime' is within two 'PPOSIXTime'. Inclusive.
|
-- | Check if 'PProposalTime' is within two 'PPOSIXTime'. Inclusive.
|
||||||
proposalTimeWithin ::
|
proposalTimeWithin ::
|
||||||
|
|
@ -220,13 +239,12 @@ proposalTimeWithin ::
|
||||||
)
|
)
|
||||||
proposalTimeWithin = phoistAcyclic $
|
proposalTimeWithin = phoistAcyclic $
|
||||||
plam $ \l h proposalTime' -> unTermCont $ do
|
plam $ \l h proposalTime' -> unTermCont $ do
|
||||||
PProposalTime proposalTime <- tcmatch proposalTime'
|
PProposalTime ut lt <- tcmatch proposalTime'
|
||||||
ptf <- tcont $ pletFields @'["lowerBound", "upperBound"] proposalTime
|
|
||||||
pure $
|
pure $
|
||||||
foldr1
|
foldr1
|
||||||
(#&&)
|
(#&&)
|
||||||
[ l #<= pfromData ptf.lowerBound
|
[ l #<= lt
|
||||||
, pfromData ptf.upperBound #<= h
|
, ut #<= h
|
||||||
]
|
]
|
||||||
|
|
||||||
-- | True if the 'PProposalTime' is in the draft period.
|
-- | True if the 'PProposalTime' is in the draft period.
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ module Agora.Utils (
|
||||||
pmsort,
|
pmsort,
|
||||||
pnubSort,
|
pnubSort,
|
||||||
pupdate,
|
pupdate,
|
||||||
|
pmapMap,
|
||||||
pmapMaybe,
|
pmapMaybe,
|
||||||
|
|
||||||
-- * Functions which should (probably) not be upstreamed
|
-- * Functions which should (probably) not be upstreamed
|
||||||
|
|
@ -322,6 +323,23 @@ pupdate = phoistAcyclic $
|
||||||
)
|
)
|
||||||
# ps
|
# ps
|
||||||
|
|
||||||
|
-- | / O(n) /. Map a function over all values in a 'PMap'.
|
||||||
|
pmapMap :: forall s k a b. (PIsData k, PIsData a, PIsData b) => Term s ((a :--> b) :--> PMap k a :--> PMap k b)
|
||||||
|
pmapMap = phoistAcyclic $
|
||||||
|
plam $ \f (pto -> (ps :: Term _ (PBuiltinList _))) ->
|
||||||
|
pcon $
|
||||||
|
PMap $
|
||||||
|
pmap
|
||||||
|
# plam
|
||||||
|
( \kv ->
|
||||||
|
let k = pfstBuiltin # kv
|
||||||
|
v = psndBuiltin # kv
|
||||||
|
|
||||||
|
nv = pdata $ f # pfromData v
|
||||||
|
in ppairDataBuiltin # k # nv
|
||||||
|
)
|
||||||
|
# ps
|
||||||
|
|
||||||
-- | Add two 'PValue's together.
|
-- | Add two 'PValue's together.
|
||||||
paddValue :: forall s. Term s (PValue :--> PValue :--> PValue)
|
paddValue :: forall s. Term s (PValue :--> PValue :--> PValue)
|
||||||
paddValue = phoistAcyclic $
|
paddValue = phoistAcyclic $
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue