diff --git a/agora-sample/Sample/Effect/GovernorMutation.hs b/agora-sample/Sample/Effect/GovernorMutation.hs new file mode 100644 index 0000000..7d41cb7 --- /dev/null +++ b/agora-sample/Sample/Effect/GovernorMutation.hs @@ -0,0 +1,174 @@ +module Sample.Effect.GovernorMutation ( + mkEffectTxInfo, + effectValidator, + effectValidatorAddress, + effectValidatorHash, + atAssetClass, + govRef, + effectRef, + invalidNewGovernorDatum, + validNewGovernorDatum, + mkEffectDatum, +) where + +import Agora.Effect.GovernorMutation ( + MutateGovernorDatum (..), + mutateGovernorValidator, + ) +import Agora.Governor (GovernorDatum (..)) +import Agora.Proposal (ProposalId (..), ProposalThresholds (..)) +import Plutarch.Api.V1 (mkValidator, validatorHash) +import Plutarch.SafeMoney (Tagged (Tagged)) +import Plutus.V1.Ledger.Address (scriptHashAddress) +import Plutus.V1.Ledger.Api ( + Address, + Datum (..), + ToData (..), + TokenName (..), + TxInInfo (..), + TxInfo (..), + TxOut (..), + TxOutRef (TxOutRef), + Validator, + ValidatorHash (..), + ) +import Plutus.V1.Ledger.Api qualified as Interval +import Plutus.V1.Ledger.Value (AssetClass, assetClass) +import Plutus.V1.Ledger.Value qualified as Value +import Sample.Shared ( + authorityTokenSymbol, + defaultProposalThresholds, + govAssetClass, + govValidatorAddress, + governor, + minAda, + signer, + ) +import Test.Util (datumPair, toDatumHash) + +-- | The effect validator instance. +effectValidator :: Validator +effectValidator = mkValidator $ mutateGovernorValidator governor + +-- | The hash of the validator instance. +effectValidatorHash :: ValidatorHash +effectValidatorHash = validatorHash effectValidator + +-- | The address of the validator. +effectValidatorAddress :: Address +effectValidatorAddress = scriptHashAddress effectValidatorHash + +-- | The assetclass of the authority token. +atAssetClass :: AssetClass +atAssetClass = assetClass authorityTokenSymbol tokenName + where + -- TODO: use 'validatorHashToTokenName' + ValidatorHash bs = effectValidatorHash + tokenName = TokenName bs + +-- | The mock reference of the governor state UTXO. +govRef :: TxOutRef +govRef = TxOutRef "614481d2159bfb72350222d61fce17e548e0fc00e5a1f841ff1837c431346ce7" 1 + +-- | The mock reference of the effect UTXO. +effectRef :: TxOutRef +effectRef = TxOutRef "c31164dc11835de7eb6187f67d0e1a19c1dfc0786a456923eef5043189cdb578" 1 + +-- | The input effect datum in 'mkEffectTransaction'. +mkEffectDatum :: GovernorDatum -> MutateGovernorDatum +mkEffectDatum newGovDatum = + MutateGovernorDatum + { governorRef = govRef + , newDatum = newGovDatum + } + +{- | Given the new governor state, create an effect to update the governor's state. + + Note that the transaction is valid only if the given new datum is valid. +-} +mkEffectTxInfo :: GovernorDatum -> TxInfo +mkEffectTxInfo newGovDatum = + let gst = Value.assetClassValue govAssetClass 1 + at = Value.assetClassValue atAssetClass 1 + + -- One authority token is burnt in the process. + burnt = Value.assetClassValue atAssetClass (-1) + + -- + + governorInputDatum' :: GovernorDatum + governorInputDatum' = + GovernorDatum + { proposalThresholds = defaultProposalThresholds + , nextProposalId = ProposalId 0 + } + governorInputDatum :: Datum + governorInputDatum = Datum $ toBuiltinData governorInputDatum' + governorInput :: TxOut + governorInput = + TxOut + { txOutAddress = govValidatorAddress + , txOutValue = gst + , txOutDatumHash = Just $ toDatumHash governorInputDatum + } + + -- + + -- The effect should update 'nextProposalId' + effectInputDatum' :: MutateGovernorDatum + effectInputDatum' = mkEffectDatum newGovDatum + effectInputDatum :: Datum + effectInputDatum = Datum $ toBuiltinData effectInputDatum' + effectInput :: TxOut + effectInput = + TxOut + { txOutAddress = effectValidatorAddress + , txOutValue = at -- The effect carry an authotity token. + , txOutDatumHash = Just $ toDatumHash effectInputDatum + } + + -- + + governorOutputDatum' :: GovernorDatum + governorOutputDatum' = effectInputDatum'.newDatum + governorOutputDatum :: Datum + governorOutputDatum = Datum $ toBuiltinData governorOutputDatum' + governorOutput :: TxOut + governorOutput = + TxOut + { txOutAddress = govValidatorAddress + , txOutValue = mconcat [gst, minAda] + , txOutDatumHash = Just $ toDatumHash governorOutputDatum + } + in TxInfo + { txInfoInputs = + [ TxInInfo effectRef effectInput + , TxInInfo govRef governorInput + ] + , txInfoOutputs = [governorOutput] + , txInfoFee = Value.singleton "" "" 2 + , txInfoMint = burnt + , txInfoDCert = [] + , txInfoWdrl = [] + , txInfoValidRange = Interval.always + , txInfoSignatories = [signer] + , txInfoData = datumPair <$> [governorInputDatum, governorOutputDatum, effectInputDatum] + , txInfoId = "4dae3806cc69615b721d52ed09b758f43f25a8f39b7934d6b28514caf71f5f7b" + } + +validNewGovernorDatum :: GovernorDatum +validNewGovernorDatum = + GovernorDatum + { proposalThresholds = defaultProposalThresholds + , nextProposalId = ProposalId 42 + } + +invalidNewGovernorDatum :: GovernorDatum +invalidNewGovernorDatum = + GovernorDatum + { proposalThresholds = + defaultProposalThresholds + { countVoting = Tagged (-1) + } + , nextProposalId = ProposalId 42 + } diff --git a/agora-sample/Sample/Governor.hs b/agora-sample/Sample/Governor.hs index 27d867b..8d0cab5 100644 --- a/agora-sample/Sample/Governor.hs +++ b/agora-sample/Sample/Governor.hs @@ -508,7 +508,7 @@ mintGATs = The effect script should carry an valid tagged authority token, and said token will be burnt in the transaction. We use 'noOpValidator' here as a mock effect, so no actual change is done to the governor state. - TODO: use 'mutateGovernorEffect' as the mock effect in the future. + TODO: use 'Agora.Effect.GovernorMutation.mutateGovernorEffect' as the mock effect in the future. The governor will ensure the new governor state is valid. -} diff --git a/agora-scripts/Options.hs b/agora-scripts/Options.hs new file mode 100644 index 0000000..e56d09a --- /dev/null +++ b/agora-scripts/Options.hs @@ -0,0 +1,45 @@ +{- | +Module : Options +Maintainer : emi@haskell.fyi +Description: Command line options for 'agora-scripts'. + +Command line options for 'agora-scripts'. +-} +module Options (Options (..), parseOptions) where + +import Options.Applicative ((<**>)) +import Options.Applicative qualified as Opt + +data Options = Options + { config :: FilePath + , output :: FilePath + } + deriving stock (Show, Eq) + +opt :: Opt.Parser Options +opt = + Options + <$> Opt.strOption + ( Opt.long "config" + <> Opt.short 'c' + <> Opt.metavar "CONFIG_PATH" + <> Opt.value "./agora-scripts/agora-params.json" + <> Opt.help "The path where the script configuration is." + ) + <*> Opt.strOption + ( Opt.long "output" + <> Opt.short 'o' + <> Opt.metavar "OUTPUT_PATH" + <> Opt.value "./agora-scripts/agora-scripts.json" + <> Opt.help "Output where generated scripts will be." + ) + +parseOptions :: IO Options +parseOptions = Opt.execParser p + where + p = + Opt.info + (opt <**> Opt.helper) + ( Opt.fullDesc + <> Opt.progDesc "Generate Agora scripts for off-chain use." + ) diff --git a/agora-scripts/Scripts.hs b/agora-scripts/Scripts.hs new file mode 100644 index 0000000..0544a1c --- /dev/null +++ b/agora-scripts/Scripts.hs @@ -0,0 +1,113 @@ +{- | +Module : Scripts +Maintainer : emi@haskell.fyi +Description: Export scripts given configuration. + +Export scripts given configuration. +-} +module Main (main) where + +import Agora.AuthorityToken (AuthorityToken, authorityTokenPolicy) +import Agora.Governor (Governor (Governor)) +import Agora.Governor qualified as Governor +import Agora.Governor.Scripts ( + authorityTokenFromGovernor, + authorityTokenSymbolFromGovernor, + governorPolicy, + governorValidator, + proposalFromGovernor, + stakeFromGovernor, + ) +import Agora.Proposal (Proposal) +import Agora.Proposal.Scripts (proposalPolicy, proposalValidator) +import Agora.SafeMoney (GTTag) +import Agora.ScriptInfo (PolicyInfo, ValidatorInfo, mkPolicyInfo, mkValidatorInfo) +import Agora.Stake (Stake) +import Agora.Stake.Scripts (stakePolicy, stakeValidator) +import Agora.Treasury (treasuryValidator) +import Control.Monad ((>=>)) +import Data.Aeson qualified as Aeson +import GHC.Generics qualified as GHC +import Options (Options (..), parseOptions) +import Plutarch.Api.V1 (mintingPolicySymbol, mkMintingPolicy) +import Plutarch.SafeMoney (Tagged) +import Plutus.V1.Ledger.Api (TxOutRef) +import Plutus.V1.Ledger.Value (AssetClass, CurrencySymbol) +import Plutus.V1.Ledger.Value qualified as Value +import System.Exit (exitFailure) +import Text.Printf (printf) + +-- | Params required for creating script export. +data ScriptParams = ScriptParams + { governorInitialSpend :: TxOutRef + , gtClassRef :: Tagged GTTag AssetClass + , maximumCosigners :: Integer + } + deriving anyclass (Aeson.ToJSON, Aeson.FromJSON) + deriving stock (Show, Eq, GHC.Generic) + +-- | Scripts that get exported. +data AgoraScripts = AgoraScripts + { governorPolicyInfo :: PolicyInfo + , governorValidatorInfo :: ValidatorInfo + , stakePolicyInfo :: PolicyInfo + , stakeValidatorInfo :: ValidatorInfo + , proposalPolicyInfo :: PolicyInfo + , proposalValidatorInfo :: ValidatorInfo + , treasuryValidatorInfo :: ValidatorInfo + , authorityTokenPolicyInfo :: PolicyInfo + } + deriving anyclass (Aeson.ToJSON, Aeson.FromJSON) + deriving stock (Show, Eq, GHC.Generic) + +main :: IO () +main = do + options <- parseOptions + + params <- + Aeson.eitherDecodeFileStrict @ScriptParams options.config + >>= either (putStrLn >=> const exitFailure) pure + + let scripts = agoraScripts params + + Aeson.encodeFile options.output scripts + + printf "Done! Wrote to %s\n" options.output + +-- | Create scripts from params. +agoraScripts :: ScriptParams -> AgoraScripts +agoraScripts params = + AgoraScripts + { governorPolicyInfo = mkPolicyInfo (governorPolicy governor) + , governorValidatorInfo = mkValidatorInfo (governorValidator governor) + , stakePolicyInfo = mkPolicyInfo (stakePolicy params.gtClassRef) + , stakeValidatorInfo = mkValidatorInfo (stakeValidator stake) + , proposalPolicyInfo = mkPolicyInfo (proposalPolicy governorSTAssetClass) + , proposalValidatorInfo = mkValidatorInfo (proposalValidator proposal) + , treasuryValidatorInfo = mkValidatorInfo (treasuryValidator authorityTokenSymbol) + , authorityTokenPolicyInfo = mkPolicyInfo (authorityTokenPolicy authorityToken) + } + where + governor :: Governor + governor = + Governor + { Governor.gstOutRef = params.governorInitialSpend + , Governor.gtClassRef = params.gtClassRef + , Governor.maximumCosigners = params.maximumCosigners + } + + authorityToken :: AuthorityToken + authorityToken = authorityTokenFromGovernor governor + + authorityTokenSymbol :: CurrencySymbol + authorityTokenSymbol = authorityTokenSymbolFromGovernor governor + + governorSTAssetClass :: AssetClass + governorSTAssetClass = + Value.assetClass (mintingPolicySymbol $ mkMintingPolicy $ governorPolicy governor) "" + + proposal :: Proposal + proposal = proposalFromGovernor governor + + stake :: Stake + stake = stakeFromGovernor governor diff --git a/agora-scripts/agora-params.json b/agora-scripts/agora-params.json new file mode 100644 index 0000000..cb036a3 --- /dev/null +++ b/agora-scripts/agora-params.json @@ -0,0 +1,11 @@ +{ + "governorInitialSpend": { + "txOutRefId": "0b2086cbf8b6900f8cb65e012de4516cb66b5cb08a9aaba12a8b88be", + "txOutRefIdx": 0 + }, + "gtClassRef": [ + "", + "" + ], + "maximumCosigners": 5 +} diff --git a/agora-scripts/agora-scripts.json b/agora-scripts/agora-scripts.json new file mode 100644 index 0000000..d02a671 --- /dev/null +++ b/agora-scripts/agora-scripts.json @@ -0,0 +1 @@ +{"authorityTokenPolicyInfo":{"currencySymbol":"6aa44a4ea34725d9bcf9b522babe6b6b94ae55eddac23b9ccaa94427","policy":"5904ad010000323232323232323232323232323232323232323222323232323253330153370e90000010a99980a99b8848000cc88c8ccc02c00cdd7180e0009bae301c301b001301c00137566032601e6030008664466e9520003300e37520046601c6ea400403cdd7180c8008070a99980a999119b8848000ccc88c054894ccc060004400c4cc010c07c004c008c07800488cdc00009991191998078019bae3020001375c6040603e00260400026eacc074c070c078c8c078c074004c078008011200000133223374a9000198071ba90023300e375200201e91011c3f57878004c69a4ebcad6907b12e8245e44fd85ebaff8c27dd9c2f4b0000e375860320082a66602a6644646602646464646464a66603c66e1d2000002133332233333301c22533301f0011225001153330223371e646eb8c09cc0a0004c098004010488c008c0a000c4c008c094004004888c008c09cc098c0a000c4894004888c00800c4894004dd718110009bac302232302230223022302230190013021007233330090073756604600a604600c002294054cc07d2401455061747465726e206d61746368206661696c75726520696e2027646f2720626c6f636b2061742061676f72612f41676f72612f5574696c732e68733a3536333a31352d323300163023002301e0013754603e603c002603a002603c0026eb0c070004c068c070008c0640148888cc88c8cccc88cccc068894ccc074004489400454ccc080cdd798111812000802091180118130018980118118008009111801181380189128009ba900337566040603e0024646464a66604066e1d2002002132533302100110011330224913e617574686f72697479546f6b656e7356616c6964496e3a2047415420546f6b656e4e616d6520646f65736e2774206d617463682053637269707448617368000013301b23375e60460026ea4dd718128011bab0041325333021001100113302249137617574686f72697479546f6b656e7356616c6964496e3a2047415420696e636f72726563746c79206c69766573206174205075624b6579000014a0604a00460400026ea8c084c088c08400852898100009bae301d005004149854cc059240120416c6c206f757470757473206f6e6c7920656d69742076616c696420474154730016153301649129506172656e7420746f6b656e20646964206e6f74206d6f766520696e206d696e74696e6720474154730016149854cc0592414d5061747465726e206d61746368206661696c75726520696e2027646f2720626c6f636b2061742061676f72612f41676f72612f417574686f72697479546f6b656e2e68733a3134343a372d32350016301a00230150013754602c602a004602c602a002602a00244466660080040064666600c0040024002290000a4000446666008466e3cdd7180880080180091118011bab3017003122500122333300323371e6eb8c04000400c004888c008dd6980b00189128009803111299980500089128008a99980698011808800891180118098018998018011808000aba04bd702441002300a300a0012300222533300500114a22a6600c60066018002260046016002464600446600400400246004466004004002aae7c88ccc01000800400c5282b9a5738aae755d12ba1230023754002aae781"},"governorPolicyInfo":{"currencySymbol":"3f57878004c69a4ebcad6907b12e8245e44fd85ebaff8c27dd9c2f4b","policy":"59054d01000032323232323232323232323232323232323222323232533300e3370e90000010991919299980899911919800925114a0664602c44a666032002244a00226660066038002444600400626004603a0024a66602866ebc00cc068c070004488c00800c489400400530127d8799fd8799f581c0b2086cbf8b6900f8cb65e012de4516cb66b5cb08a9aaba12a8b88beff00ff003758602e0062a666022a6601466e1ccc03cdd7180b8021bab3017001480084cdc39991191991199999809919b8f375c603800200600a44460046eacc08800c48940048cccccc0508cdc79bae301d0010030012223002375a6046006244a0024002290000a40006eb8c068004dd7180d180d800980d8009bab301700133223374a9000198059ba90023300b375200297ae0375c602e00891100480084c8c8c8c8c8c8c94cc044c94ccc06400440044cc07524012a45786563757465207468726573686f6c64206973206c657373207468616e206f7220657175616c20746f000013371290000018a9980899299980c800880089980ea481294472616674207468726573686f6c64206973206c657373207468616e206f7220657175616c20746f20000013371290000010a9980899299980c800880089980ea48128566f7465207468726573686f6c64206973206c657373207468616e206f7220657175616c20746f20000013371290000008a9980899299980c800880089980ea4812b4472616674207468726573686f6c64206973206c657373207468616e20766f7465207468726573686f6c640000133712004002264a666032002200226603a9212d45786563757465207468726573686f6c64206973206c657373207468616e20766f7465207468726573686f6c6400001337100020066eb4c074c07800cdd6980e0011bad301b002301b001301b3019301b00133223301049122446174756d206e6f7420666f756e6420696e20746865207472616e73616374696f6e003322333301822533301b0011225001153330183371e646eb8c07cc084004c078004010488c008c08000c4c008c07c004004888c008c07cc080c08400c4894004cc88c8c8c94ccc064cdc3a400000426eb8c07c00454cc07401458c084008c070004dd5000a4920476976656e2054784f757420646f6e657327742068617665206120646174756d000020013018300d301a001375860306460346034601c00260320046601a920119476f7665726e6f72206f7574707574206e6f7420666f756e64003300e23370e660206eb8c060014dd5980c180c980d000a40046eb0c05c00854cc05524012245786163746c79206f6e6520746f6b656e2073686f756c64206265206d696e746564001615330154911f5265666572656e636564207574786f2073686f756c64206265207370656e740016301730170013016001301630143016004153301249014f5061747465726e206d61746368206661696c75726520696e2027646f2720626c6f636b2061742061676f72612f41676f72612f476f7665726e6f722f536372697074732e68733a3137303a352d34340016301600230110013754602260246026002ae8088ccc02400800400c528118069806800911980090008a998050010b1802911299980480089128008a999803180118060008911801180700189980180118068009119998019ba9002001233300622337006eb4c040008005200037560022900011199980291299980400089128008a99980299baf300a300b0010041223002300d00313002300c0010012223002300f0031225001573444600644a66600c0022006266008601200260046014002464600446600400400246004466004004002aae7d5ce2ab9d5742ae888c008dd5000aab9e01"},"governorValidatorInfo":{"hash":"f975d9a89cf94e58ad4955c9d2a3ce21ce5351909bfc4de4857fc46c","script":"59193901000032323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323222232323232323232323253330563370e90010010991919299982c99b8733049010375660bc60ba0049001099299982d19b873032001480084c94ccc16ccdc3998258091bab3060001480084c94ccc170c8c8c8c8c94cc134c94ccc18800440044cc18d2412a45786563757465207468726573686f6c64206973206c657373207468616e206f7220657175616c20746f000013371290000018a998269929998310008800899831a481294472616674207468726573686f6c64206973206c657373207468616e206f7220657175616c20746f20000013371290000010a998269929998310008800899831a48128566f7465207468726573686f6c64206973206c657373207468616e206f7220657175616c20746f20000013371290000008a998269929998310008800899831a4812b4472616674207468726573686f6c64206973206c657373207468616e20766f7465207468726573686f6c640000133712004002264a6660c400220022660c69212d45786563757465207468726573686f6c64206973206c657373207468616e20766f7465207468726573686f6c6400001337100020066eb4c194c19000cdd698320011bad306300230610013062306130620011323232533305f3370e90010010a99982f99baf0040151533305f3370e6609e08a646660a44466446664446608c6604046466660b4002006466ec0008dd3198031bab30710033756002200460d400200466042460466605e466ebcc1ac004c1ac00800c00488cc114cc07c8c8cccc16400400c8cdd80011ba8337006eb4c1c000cdd680088011834800801198101181119817119baf306a001306a00200300100200137566460d060ce00260d06460d060ce00260d00040020380026eb0c190041200213232533306153304d3025002130250011323232323253330663375e60d600698103d87b8000153330663375e00c66e95200033062306b00533062306b004330623374a90030309983118358011983118358009983118359835000830899299983399b89375a6460da60dc00260d80046eb4c1b80044c8c94ccc1a4cdc39982c8151bab306e018001132533306a3370e608400200426660b844a6660d866e1ccc1700b4dd59838983818390012400020022a6660d866e1ccc1700b4dd5983898381839001240042a660b0002264a660b264a6660dc00220022660de92125474154206d7573742062652074616767656420627920746865206566666563742068617368000013302a02e003132533306e001100113306f49110556e657870656374656420646174756d000013371e660cc921224f757470757420746f206566666563742073686f756c64206861766520646174756d003072307130710013306a4901225265636569766572206973206e6f7420696e2074686520656666656374206c697374003322333305a23371e6eb8c1c800400c004888c008dd7183c8018912800998352491c474154207265636569766572206973206e6f74206120736372697074003232323253330713370e900100109118011bae3078003122500130770023071001375460e660e800260e400200c60e40042941289bac306f01a153306b4901244f75747075742047415473206973206d6f7265207468616e206d696e746564204741547300163306323371090001982d0159bab306f306e3070001375860dc0322a660d4920128526571756972656420616d6f756e74206f6620474154732073686f756c64206265206d696e74656400163040001332232330012001153306b491505061747465726e206d61746368206661696c75726520696e207175616c69666965642027646f2720626c6f636b20617420506c7574617263682f4d617962652f45787472612e68733a31353a352d31310016333305423370e6eb4c1b000400c004888c008dd5983980189128009bad3069001375660d800a2a660d0921314869676765737420766f746520646f65736e2774206d65657420746865206d696e696d756d20726571756972656d656e74001633063491124e6f2077696e6e696e67206f7574636f6d65003330582253330683300124a229404cc00494ccc1a4cdc41bad3070001375a60e0006244600400a20042a660d2921505061747465726e206d61746368206661696c75726520696e2027646f2720626c6f636b2061742061676f72612f41676f72612f476f7665726e6f722f536372697074732e68733a3539343a33312d343000161223002004225001375660d660d40022a660ce920120556e6578706563746564206f75747075742070726f706f73616c20646174756d001615330674914850726f706f73616c206d75737420626520696e206c6f636b65642865786563757461626c652920737461746520696e206f7264657220746f20657865637574652065666665637473001630690013068001306700130660013067002153306204416330483065306330663305d4911950726f706f73616c206f7574707574206e6f7420666f756e64003304b23253304e3370e660a40906eacc19cc198005200213375e60ce00208c60ce0026eb0c194040dd61832983200719823983218311832991832983218330009982e2491850726f706f73616c20696e707574206e6f7420666f756e64003304a23253304d3370e660a208e6eacc198c194005200213375e60cc00208a60cc60ca60c860cc0026eb0c190040dd6183218318068a998302493454686520676f7665726e6f722063616e206f6e6c792070726f63657373206f6e652070726f706f73616c20617420612074696d650016153306049124476f7665726e6f722073746174652073686f756c64206e6f74206265206368616e67656400161533305f3370e90020010a998259929998300008800899830a493373696e676c65417574686f72697479546f6b656e4275726e65643a204d757374206275726e2065786163746c79203120474154000013370e6609e0406eacc190038cdc0a4000900109929998300008800899830a494673696e676c65417574686f72697479546f6b656e4275726e65643a20416c6c2047415420746f6b656e73206d7573742062652076616c69642061742074686520696e70757473000013301e23301d02132306630650013066001375860c860ca60c80242a6660be66ebc010cdd2a4000660b660c800e660b66ea0c8cdc0000a40046eb4c190c18c01c16854ccc17cc94cc130cdc399828023000a4004266e1cc0e00052002375660c801c26464a6660c2604860cc60ca00226464a6660c666e1cdd69834001181e1bab30680031533306333223306322533306600114a02a6660ce66ebcc1b000400c5288980118358008011bac3068011306800113253330643370e607800290010992999832981400089929998331815000899191919299983519baf306f004306f306e0121533306a3375e60de00260de0242a6660d466ebcc1bc00d30103d87980001533306a3370e60846eb0c1bc00920021533306a3375e6ea4dd7183780418379bac306f00213232533306c3370e6eb4c1c402cc114dd598388008a99983619baf3374a9000198341838805998341838805198341ba73304b375860e260e00146609a64466e9520003306a0013306a37500040d26eb4c1c4018c130dd598389838001833998348271983119832a491e5374616b65206f75747075742073686f756c64206861766520646174756d0030713070001375860e260e00342930a99836a4811d556e6578706563746564207374616b65206f757470757420646174756d0016153306d491315374616b6564204754732073686f756c642062652073656e74206261636b20746f207374616b652076616c696461746f720016306f30710013306704c330552325330583375e60e20020b4266e1ccc17017cdd598389838000a400460e20026eb0c1bc06854cc1ad240122436f7369676e65722073686f756c6420626520746865207374616b65206f776e65720016153306b4912650726f706f73616c2073686f756c642068617665206f6e6c79206f6e6520636f7369676e65720016153306b4911e50726f706f73616c2073746174652073686f756c642062652064726166740016153306b49124496e76616c6964207468726573686f6c647320696e2070726f706f73616c20646174756d0016153306b49125496e76616c69642070726f706f73616c20696420696e2070726f706f73616c20646174756d0016306d001306c001306b306b001306c0011533067049163304d001375860d460d20262a660cc920137546865207574786f207061696420746f207468652070726f706f73616c2076616c696461746f72206d757374206861766520646174756d001632306a3068306b001306900115330654915345786163746c79206f6e65205554584f20776974682070726f706f73616c20737461746520746f6b656e2073686f756c642062652073656e7420746f207468652070726f706f73616c2076616c696461746f7200163305d2325330513375e60d4002092266e1ccc15412cdd598351834800a400460d40026eb0c1a004c54cc1912412654782073686f756c64206265207369676e656420627920746865207374616b65206f776e65720016153306449130526571756972656420616d6f756e74206f66207374616b65204754732073686f756c642062652070726573656e7465640016306600130673304930663065001375860cc60ca01e2a660c49211e5374616b6520696e70757420646f65736e2774206861766520646174756d0016306430663065306430660013305c4901155374616b6520696e707574206e6f7420666f756e64003304a23253304d3375e60cc00209e266e1ccc144150dd598331832800a400460cc60ca60c860cc0026eb0c19004054cc1812412945786163746c79206f6e652070726f706f73616c20746f6b656e206d757374206265206d696e746564001615330604911f556e657870656374656420676f7665726e6f7220737461746520646174756d00163065002305f00137546464646402aa6660be66e1d20000021324994ccc1780045261533060054161533305f3370e900100109924ca6660bc0022930a9983002a0b0a99982f99b87480100084c926533305e001149854cc1801505854cc1812413f7265616368656420656e64206f662073756d207768696c65207374696c6c206e6f7420686176696e6720666f756e642074686520636f6e7374727563746f7200163065002305f00137540222a660ba9201164e657720646174756d206973206e6f742076616c6964001633058491244f7570757420676f7665726e6f7220737461746520646174756d206e6f7420666f756e64003305133054490122476f7665726e6f72206f757470757420646f65736e2774206861766520646174756d003060305f001375860c060be0122a660b89212d537461746520746f6b656e2073686f756c64207374617920617420676f7665726e6f72277320616464726573730016305e3060305f001153305b49012f45786163746c79206f6e65207574786f2073686f756c642062652073656e7420746f2074686520676f7665726e6f72001633223305523375e60c260c40020040046eb0c178024c17800854cc1692412d4f776e20696e7075742073686f756c6420686176652065786163746c79206f6e6520737461746520746f6b656e0016305e00e305d305c305b305d00133053491134f776e20696e707574206e6f7420666f756e64003322332305722533305a001122500113330033060001222300200313002305f001253330593375e00660bc60be0022446004006244a00200260b60026eb0c16c01c54cc15d24014f5061747465726e206d61746368206661696c75726520696e2027646f2720626c6f636b2061742061676f72612f41676f72612f476f7665726e6f722f536372697074732e68733a3239323a352d35390016305c0023056001375460b060ae00c6460ae60ac00260ac00260aa60aa00260a800260aa00260a600260a60020029111c3f57878004c69a4ebcad6907b12e8245e44fd85ebaff8c27dd9c2f4b002304622533304900110061330463003304f0013002304e001230452253330480011005132533304a30040011330460013003304e00213003304e002304e00123330460014a09452f5bded8c04464666606e6ea400cdd5982598250009191919299982519b87480080084c94ccc12c00440044cc130024004cc0248cdd798268009ba9375c60a00046eac0104c94ccc12c00440044cc13002c00528182800118250009baa304c304d304c00214a2609600292013e617574686f72697479546f6b656e7356616c6964496e3a2047415420546f6b656e4e616d6520646f65736e2774206d617463682053637269707448617368002304022533304300114a22a660606006609200226004609000292137617574686f72697479546f6b656e7356616c6964496e3a2047415420696e636f72726563746c79206c69766573206174205075624b6579000014891c6aa44a4ea34725d9bcf9b522babe6b6b94ae55eddac23b9ccaa9442700232323253330413370e90010010a5014a2608e00460820026ea80040048c8c94cc0a8c94ccc0fc00440044cc100014004cc014020dd598218010a9981519299981f800880089982000500098051bac3043001153302a32533303f001100113304000c00133712602c6eb0c10c005200a132533303f001100113304000d0013300d375660860046eacc10cc108c108004c104c104004c100c10800524013250726f706f73616c20686173206174206c65617374206f6e6520526573756c7454616720686173206e6f2065666665637473002303822533303b00114a02a660086006608200226004608000246607200200429448c034dd5981f000a4812250726f706f73616c20686173206174206c65617374206f6e6520636f7369676e6572002533303400114a029452412b50726f706f73616c2068617320666577657220636f7369676e657273207468616e20746865206c696d69740049013950726f706f73616c20766f74657320616e6420656666656374732061726520636f6d70617469626c6520776974682065616368206f746865720022323253330353370e601a004601a00226600a6600c0180046600c0180022940c050008c04c008c0b88894ccc0c800854ccc0c80045288a501533303200114a02a66606666ebcc0e0008c0e00044cc00cc0dc008c0dc00452818169112999819180400088168a9998191804181b00088008998020009119980480219802802001198028020009198010008009815911299981818030008919800816001899191929998199804801891980098040018010991998038010009111980099819803001801181b801981b002181b001981a000919814800814181491112999817980280108008a99981798028008801099191919299981999803802001099817802199804003801802899817801199804003803000981b002181b001981a001981a0012ba322337106eb4008dd6800998131112999815000880109980199b8000248008c0bc005200000123232333004003375c605c0026eb8c0b8c0b4004c0b8004cdd2a4000660466ea4014cc08cdd480281111119998020010019199980300100090008a400029000111999808919b8f375c605200200600244460046eacc0c000c489400488cccc0408cdc79bae30280010030012223002375a605e006244a00291010022330212253330240011003133021302a001300230290010022330020230012301e225333021001101d13301e3003302700130023026001491165374616b65206f7574707574206e6f7420666f756e640049011c50726f706f73616c20646174756d206d7573742062652076616c6964000014c129d8799fd87a9f581c44fd51f02679fe98ba45c64eea4b7169cad2f573439e542ef08cff3dffd87a80ff000014891c8fd3f732c764550a66a42f31e5936b6dc9f85eabb5c08c774cebd3cd002233017003330123301300400200149122446174756d206e6f7420666f756e6420696e20746865207472616e73616374696f6e00490120476976656e2054784f757420646f6e657327742068617665206120646174756d00301322253330170011225001153330183002301d0011223002301f003133003002301c0012233301600200100314a000298129d8799fd87a9f581c9aecaa97f19230ddfcd5c67fdab9a032783ed2aba422362b68a63253ffd87a80ff002233330033752004002466600a4466e00dd6980e001000a40006eac0045200022333300f2253330120011225001153330133375e602a60300020082446004603400626004602e00200244460046036006244a00244601c44a6660220022006266008602e0026004602c0020029111c55d252e7c81d25e68ed198a26f07832f838c4d1f078e7d28bd18c209004901186c697374206973206c6f6e676572207468616e207a65726f002233330030020012223002003122500100122333300822533300b00112250011533300c3371e646eb8c048c04c004c044004010488c008c04c00c4c008c040004004888c008c048c044c04c00c489400488c8c8c94ccc030cdc3a400000426eb8c04400454cc03401458c048008c030004dd500091802912999804000880209929998051802000899803000980198070010980198070011807000a5eb815d0111980090008a998030010b119180111980100100091801119801001000aab9f5734ae7155ce918011801000aba25742460046ea800555cf01"},"proposalPolicyInfo":{"currencySymbol":"8fd3f732c764550a66a42f31e5936b6dc9f85eabb5c08c774cebd3cd","policy":"5902d501000032323232323232323232323232323232223232323232533300e3370e90000010991919299980899b874800000854ccc044cc88cdc42400066644602644a66602c0022006266008603c0026004603a0024466e00004cc88c8ccc04800cdd7180f8009bae301f301e001301f001375660386036603a64603a6038002603a0040089000000999119ba548000cc050dd48011980a1ba90010154891c3f57878004c69a4ebcad6907b12e8245e44fd85ebaff8c27dd9c2f4b000143758603000e2a66602266e1ccc88c8ccc03800cdd7180d8009bae301b301a001301b001375660306460306030002602e00e664466e952000330143752004660286ea4004054dd7180c00080a240042930a9980aa4811e4d696e7465642065786163746c79206f6e652070726f706f73616c2053540016153301549127476f7665726e616e63652073746174652d74687265616420746f6b656e206d757374206d6f7665001615330154914e5061747465726e206d61746368206661696c75726520696e2027646f2720626c6f636b2061742061676f72612f41676f72612f50726f706f73616c2f536372697074732e68733a37373a352d32330016301900230140013754602a602800a2a6602492014e5061747465726e206d61746368206661696c75726520696e2027646f2720626c6f636b2061742061676f72612f41676f72612f50726f706f73616c2f536372697074732e68733a37323a352d323300163016002301100137546024602200460246022002602200244466660080040064666600c0040024002290000a4000446666008466e3cdd7180680080180091118011bab3013003122500122333300323371e6eb8c03000400c004888c008dd6980900189128009800911299980280089128008a99980318011806800891180118078018998018011806000919180111980100100091801119801001000aab9f5734ae812f5c0911005738aae755d12ba1230023754002aae781"},"proposalValidatorInfo":{"hash":"44fd51f02679fe98ba45c64eea4b7169cad2f573439e542ef08cff3d","script":"5909bf010000323232323232323232323232323232323232323232323232323232323232323232323232222323232323232323253330253370e9001001099999119999181291299981400089128008999801981900091118010018980118188009299981419baf003303030310011223002003122500100122230023032303130330031225001302d0013758605a00c4646464646464646464646464a66606466e1c0112002132323253330353370e9001001099299981b199119b873021001302153330383020001100113233330362222533303d30250011302400213232533303f3300500200413330060050040011330390043330060050020013045002304500175e6082002608000266606a444a6660766046002205e2a66607660466084002200226666070444a66607c604c00224660020660062646464a66608260520062466002605200600426466600e004002444660026607e00c0060046090006608e008608e006608a0020020024466660744444a666082605200420022a66608260520022004264646464a66608a6600e00800426607e00866601000e00600a26607e00466601000e00c00260960086096006609200660920040086600a0080046600a0080020040024466e40dd70011bae0010011533303633020005001153330363370e00c603e0022a66606c66040466666444666605a46464646464a66608066e1d200200214a02a66608066e1c01120021333302b375c6090002010466ebcc124c120c128004dd48050a5014a0609200460840026ea8c114c110008cc88c8ccc0bc00cdd718238009bae3047304600130470013756608800201a60846088646088608600260880020024646464a66607c66e1d2000002133029375c608c00200c244a002608e00460800026ea8c10cc104c110c10cc108c1100044894004dd70009bac303f303e0163758607e03049445280008a99981b1991191981a91919191919299981f99b874800800854cc1092401455061747465726e206d61746368206661696c75726520696e2027646f2720626c6f636b2061742061676f72612f41676f72612f5574696c732e68733a3533393a31352d323300161333302a375c608e0026eb0c11cc8c11cc11cc11cc114004c11801c8ccc024dd5982400298240030008a5030480023041001375460886086002608400260860026eb0c104004c0fcc104008c0f80648894cc09cc94ccc0e800440044cc0f524115446174756d206d75737420626520636f7272656374000013375e00266e95200033033304101233033304101133033304101033033374e6605e0086eb0c10403ccc0ccc104038cc0ccc104c1000380b454cc09cc94ccc0e800440044cc0f524011756616c75652073686f756c6420626520636f7272656374000013375e6e98dd5982098200099ba6003132533303a001100113303d4901224d7573742062652073656e7420746f2050726f706f73616c27732061646472657373000013375e01a0042930a9981ca481325369676e6174757265732061726520636f72726563746c7920616464656420746f20636f7369676e6174757265206c6973740016153303949135416c6c206e657720636f7369676e65727320617265207769746e6573736564206279207468656972205374616b6520646174756d7300161533039491254173206d616e79206e657720636f7369676e657273206173205374616b6520646174756d73001615330394911b5369676e656420627920616c6c206e657720636f7369676e6572730016153303949114436f7369676e6572732061726520756e6971756500163758607a002293181f001181b8009baa0171533035490116535420617420696e70757473206d757374206265203100163223303022533303300114a02a66606a66ebcc0f400400c52889801181e0008011bac303901032333021004375c60720026eb8c0e4c0e0004c0e4004cc88cdd2a4000660566ea4008cc0acdd4800812a451c55d252e7c81d25e68ed198a26f07832f838c4d1f078e7d28bd18c2090048811c9aecaa97f19230ddfcd5c67fdab9a032783ed2aba422362b68a6325300332233330283752004002466604c4466e00dd6981e801000a40006eac004520004891c8fd3f732c764550a66a42f31e5936b6dc9f85eabb5c08c774cebd3cd00001323330232233223332223302b3302c232333302f0010032337600046e98cc018dd598210019bab0011002303a0010023302e230313303223375e60760026076004006002446605466056464666605c002006466ec0008dd419b80375a60820066eb40044008c0e4004008cc0b48c0c0cc0c48cdd7981d000981d0010018008010009bab32303930380013039323039303800130390020010300013758606a01c606800c606400260620026060002605e002606001a605e0022a660509214f5061747465726e206d61746368206661696c75726520696e2027646f2720626c6f636b2061742061676f72612f41676f72612f50726f706f73616c2f536372697074732e68733a3132393a352d3135001615330284914f5061747465726e206d61746368206661696c75726520696e2027646f2720626c6f636b2061742061676f72612f41676f72612f50726f706f73616c2f536372697074732e68733a3132373a352d34340016302e002302700137546054605200a604c6050002604c604e0026050002604c002604c00246602800201cae8ccc0508894ccc06000440084cc00ccdc000124004604200290001180a11299980b8008a51153300730033021001130023020001223333003002001222300200312250010012233330122253330150011225001153330173371e646eb8c080c084004c07c004010488c008c08400c4c008c078004004888c008c080c07cc08400c489400488ccc05000800400c52811119998020010019199980300100090008a400029000111999802119b8f375c602c00200600244460046eacc07800c489400488cccc00c8cdc79bae30150010030012223002375a603a006244a0026014444a66601c002244a0022a6660206004603000224460046034006266006004602e00297ae022300a22533300d0011003133004301700130023016001223300922533300c00110031330083016001300230150010022300722533300a001100e133006300330140013002301300122333300722533300a00112250011533300c3375e602060280020082446004602c0062600460260020024446004602e006244a0024600a44a6660100022018264a666016600800226600a002600660240042600660240046024002ae808ccc018005282512300222533300500114a02a6600c6006601e00226004601c002464600446600400400246004466004004002aae7c8cc00800400d5cd0a514bd6f7b6302b9c5573a460066004002460046004002ae895d0918011baa0015573c1"},"stakePolicyInfo":{"currencySymbol":"55d252e7c81d25e68ed198a26f07832f838c4d1f078e7d28bd18c209","policy":"5906fa0100003232323232323232323232323232323232323232323232323232323232323232323232323232323232323232223232323232323232533302e3370e90000010991919299981899b884800000454ccc0c4cdc3801240002a66606266e1c00520021533303132323302f2323232323253330393370e90010010a9981e2481455061747465726e206d61746368206661696c75726520696e2027646f2720626c6f636b2061742061676f72612f41676f72612f5574696c732e68733a3533393a31352d3233001613333020375c607e0026eb0c0fcc08cc0f801c8ccc024dd5982000298200030008a503040002303b001375460786076002607400260760026eb0c0e4004c0dcc0e402c888c8c8c94ccc0dccdc3a400000429404c8c94cc09ccc88cc0e0894ccc0ec0045280a99981e99baf304300100314a22600460840020046eb0c0fcc098c0f803cc0fcc0f800854cc09ccc058020cc0b0c070dd6981f80119980f005800a40042a6604e6602c0106605860386eb4c0fc008ccc07802c0052002132337126660320020180046660320120180046605860386eb4c0fc008ccc07802c0052002375c607c004607c008607c00460720026ea8c0e8c0ec008526153303449012941205554584f206d75737420657869737420776974682074686520636f7272656374206f7574707574001615330344911b4d696e746564205354206d7573742062652065786163746c7920310016153303449116535420617420696e70757473206d75737420626520300016153330313370e00490010a99981899b87001337029000240042a66606264646605e46464646464a66607266e1d200000213333020375c607e0026eb0c0fcc8c0fcc090004c0f801c8ccc024dd5982000298200030008a50153303c4901455061747465726e206d61746368206661696c75726520696e2027646f2720626c6f636b2061742061676f72612f41676f72612f5574696c732e68733a3538393a31352d323300163040002303b00137546078607600260740026076646076607400260760026eb0c0e4004c0e402c8894cc088cdc399812803001a40042605e64a66606600229405289bac303a3024303b001149854cc0d12412a416e20756e6c6f636b656420696e707574206578697374656420636f6e7461696e696e6720616e20535400161533034491095354206275726e65640016153303449116535420617420696e70757473206d75737420626520310016330210023756606c00c6604000264666044446604a6eacc8c0e4c0e0004c0e4c8c0e4c0e0004c0e40080040c8004dd6181a8039bae3034001153303149014b5061747465726e206d61746368206661696c75726520696e2027646f2720626c6f636b2061742061676f72612f41676f72612f5374616b652f536372697074732e68733a38313a352d323300163035002303000137546062606000a605e605e002605c002605e002605a002605a0024466e24c00c004c00c0088ccc00800401c01c888cccc01000800c8cccc01800800480045200014800088cccc0108cdc79bae302700100300122230023756605a006244a002446666006466e3cdd7181300080180091118011bad302c0031225001301b222533301f001122500115333021300230270011223002302900313300300230260012333003002002001489002223004337606ea400cdd3180219bb037520046ea00048cc05400407488cccc00c008004888c00800c489400400488cccc058894ccc064004489400454ccc06ccdc7991bae30223023001302100100412230023023003130023020001001222300230223021302300312250012301c30020012301b30020012301a30020012301930040012233301400200100314a04602c602c0024466660126ea40080048ccc01088cdc01bad301c00200148000dd58008a400044601a44a666020002200626600860300026004602e002446660064466600ce00008004008004888cc014cc0188c8cccc02400400c8cdd80011ba633006375660340066eac0044008c050004008cc0208c02ccc0308cdd7980a800980a80100180091119802198029191999804000801919bb000237506600c6eb4c06400cdd680088011809800801198039180519805919baf30140013014002003001223300922533300c00110031330083014001300230130010022300722533300a001100e133006300330120013002301100122333300722533300a00112250011533300c3375e60206024002008244600460280062600460220020024446004602a006244a0024600a44a6660100022018264a666016600800226600a002600660200042600660200046020002ae808ccc018005282512300222533300500114a02a6600c6006601a002260046018002464600446600400400246004466004004002aae7c8cc00800400d5cd0a514bd6f7b6302b9c5573aae895d0918011baa0015573c1"},"stakeValidatorInfo":{"hash":"9aecaa97f19230ddfcd5c67fdab9a032783ed2aba422362b68a63253","script":"590b7101000032323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232222323232323232323232533303e3370e90010010999991199181f91299982100089128008999801982480091118010018980118240009299982099baf003304730480011223002003122500100130440013758608800e464646464646464646464a66609266e1d2000002153330493370e00c90010a999824981f0020a9998248048a9998249980f009911191929981599299982780088008998292490f697353637269707441646472657373000013375e00801e2a6605664a66609e00220022660a405e002a6605666ebcc150c14c050c150c14c00854cc0accdc399b80375a60a80280026eb4c1500084cdc4a40006eb4c1500084c94ccc13c00440044cc1480b80054cc0accc074014cc0f0c8dd5982a982a182b000982a1829982a808180f8008a998159980e8029981e191bab305530543056001305430533055010301f00113233712660920980026609209800c66078646eacc154c150c158004c150c14cc154040c07c004dd6982980298298008a4c2a660980542c2a660980562c2a660980582c2a6609805a2c2a66609266e1d200400215333049009153330493370e00a90010a9998249980f009911299982619baf00200d132533304d001100113305002c0013375e6e98c8dd598299829182a0009829182898298071ba600314a02930a998260150b0a9982600e8b0a998260158b0a99982499b874801800854ccc12402454ccc124cdc3803240042a66609266e1c0152002153330493301e013222533304c3375e00401a264a66609a00220022660a005800266ebcdd3191bab30533052305400130523051305300e374c0062940526153304c02a16153304c01d16153304c02d16153304c02b16153330493370e90040010a99982499b870064800854ccc1254cc1000244cc88cdc42400066608e4466e00004cc88c8ccc0dc00cdd7182b0009bae305630550013056001375660a660a460a86460a860a600260a8004008900000099ba548000cc0f4dd481a9981e9ba90360343758609e0242a6660926603c026444a66609866ebc00803454cc0a4c94ccc13400440044cc1400b0004cdd79ba632375660a660a460a800260a460a260a601c6e9800c4c94ccc13400440044cc1400b4004cdd780080d0a50149854cc1300a85854cc1312401374f776e6572207369676e732074686973207472616e73616374696f6e204f522070726f706f73616c20746f6b656e206973207370656e740016153304c02d16153330493370e00c90010a99982499b87008337029000240042a666092607c0082a6660920122930a998260158b0a998260160b0a9982624810e53686f756c64206275726e2053540016153304c02d163050002304b00137546464646402ea66609266e1d20000021323232324994ccc12c004526153304f031163050003375a002609e0022a66609266e1d20020021324994ccc120004526153304c02e16153330493370e9002001099191924ca6660940022930a998270180b182780118278008a99982499b87480180084c8c8c8c926533304b001149854cc13c0c458c14000ccc8c124894ccc13000440e04cc104c00cc14c004c008c1480048c8c8c8c80154ccc138cdc3a40000042646464646464649329998298008a4c2a660ae0722c60b00066eb4004c15c004c15400cdd6800982a0008a9982881a0b182a80118280009baa0013758002609e0022a66609266e1d20080021324994ccc120004526153304c02e16153304c02f163050002304b001375402664a66608a00229405289bac304b3047304c013323233302d004375c60980026eb8c130c12c004c130004cdd2a4000660706ea40c0cc0e0dd48188179981e8200009919981f91198199bab32304c304b001304c32304c304b001304c00200103c001375860900166607607c6eacc11c020cc88cc100894ccc10c0045280a99982219baf304a00100314a22600460920020046eb0c118c104c11401cc118c114018c8c118c11c004c114c110c11800454cc10524014c5061747465726e206d61746368206661696c75726520696e2027646f2720626c6f636b2061742061676f72612f41676f72612f5374616b652f536372697074732e68733a3232393a352d3138001615330414914c5061747465726e206d61746368206661696c75726520696e2027646f2720626c6f636b2061742061676f72612f41676f72612f5374616b652f536372697074732e68733a3232373a352d323200163045002304000137546082608000c6082010607c607c002607a002607c002607800260780024466e24c00c004c00c0088ccc0600040780788ccc008074074004888c010cdd81ba9003374c600866ec0dd48011ba800123302200102749011150726f706f73616c205354207370656e74002232330242323232323253330343370e90010010a9981b8050b099998059bae303a00137586074601c607200e46660126eacc0ec014c0ec018004528181d801181b0009baa30373036001303500130360013758606800260646068004921455061747465726e206d61746368206661696c75726520696e2027646f2720626c6f636b2061742061676f72612f41676f72612f5574696c732e68733a3533393a31352d3233002233330030020012223002003122500100122333302722533302a00112250011533302b3371e646eb8c0c8c0cc004c0c4004010488c008c0cc00c4c008c0c0004004888c008c0c8c0c4c0cc00c48940048c0b0c0080048c0acc0080048c0a8c09800488ccc09400800400c5282490c76616c7565436f727265637400490112636f72726563744f7574707574446174756d0049012941205554584f206d75737420657869737420776974682074686520636f7272656374206f75747075740049011c4f776e6572207369676e732074686973207472616e73616374696f6e0049010e5374616b6520756e6c6f636b656400490116535420617420696e70757473206d7573742062652031004901186c697374206973206c6f6e676572207468616e207a65726f0049013f7265616368656420656e64206f662073756d207768696c65207374696c6c206e6f7420686176696e6720666f756e642074686520636f6e7374727563746f720022233330040020032333300600200120011480005200022333300423371e6eb8c07c00400c004888c008dd598128018912800911999801919b8f375c603c00200600244460046eb4c09000c4894004c0508894ccc060004489400454ccc064c008c07c004488c008c08400c4cc00c008c0780052f5c091011c8fd3f732c764550a66a42f31e5936b6dc9f85eabb5c08c774cebd3cd0048810022333003223330067000040020040024446600a6600c4646666022002006466ec0008dd3198031bab301f0033756002200460320020046600e4601466016466ebcc068004c06800800c004888cc010cc0148c8cccc04000400c8cdd80011ba833006375a603c0066eb40044008c060004008cc0188c024cc0288cdd7980c800980c80100180091198079129998090008801899803980c8009801180c0008011180691299980800088050998029801980b8009801180b000918061129998078008804899299980898020008998028009801980b00109801980b001180b000aba0233300c0014a09448c024894ccc0300045280a99802180198098008980118090009198050008010a514bd6f7b6301119998019ba9002001233300522337006eb4c04c008005200037560022900011199980291299980400089128008a99980499baf300d300f0010041223002301100313002300e0010012223002301200312250012230042253330070011003133004300e0013002300d00148811c55d252e7c81d25e68ed198a26f07832f838c4d1f078e7d28bd18c209002323002233002002001230022330020020015573eae688c014c0080048c010c0100055ce2ab9d5744ae848c008dd5000aab9e1"},"treasuryValidatorInfo":{"hash":"39ad85486d8624a49f080636324f80938febfb91552709c70a3ac00e","script":"590338010000323232323232323232323232323222232323232533300e3370e90000010992999807a99806992999808000880089980924813373696e676c65417574686f72697479546f6b656e4275726e65643a204d757374206275726e2065786163746c79203120474154000013370e6466660146ea40440048ccc88c03c894ccc048004400c4cc010c064004c008c06800488cdc01bad301a00200148000dd58008a40006eacc04cc8c054c054c054004c054004cdc0a400090010992999808000880089980924814673696e676c65417574686f72697479546f6b656e4275726e65643a20416c6c2047415420746f6b656e73206d7573742062652076616c69642061742074686520696e70757473000013300a23232333300c37520266eacc058c05c0048c8c8c94ccc058cdc3a4004004264a66602e00220022660329213e617574686f72697479546f6b656e7356616c6964496e3a2047415420546f6b656e4e616d6520646f65736e2774206d617463682053637269707448617368000013301123375e60340026ea4dd7180d8011bab0041325333017001100113301949137617574686f72697479546f6b656e7356616c6964496e3a2047415420696e636f72726563746c79206c69766573206174205075624b6579000014a06038004602e0026ea8c05cc064c05c008528980b80099180a980b000980b0009bac30133015001149854cc04524128412073696e676c6520617574686f7269747920746f6b656e20686173206265656e206275726e65640016301200415330104901465061747465726e206d61746368206661696c75726520696e2027646f2720626c6f636b2061742061676f72612f41676f72612f54726561737572792e68733a38353a332d313200163014002300f0013754601e6020002602000244666600844a66600e002244a0022a66601466ebcc034c038004010488c008c04000c4c008c03c004004888c008c04800c48940048c008894ccc0140045288a9980318019806000898011806800919180111980100100091801119801001000aab9f2233300400200100314a0ae692211c6aa44a4ea34725d9bcf9b522babe6b6b94ae55eddac23b9ccaa94427005738aae755d0aba2230023754002aae781"}} \ No newline at end of file diff --git a/agora-test/Spec.hs b/agora-test/Spec.hs index d2c90f7..2d97c1e 100644 --- a/agora-test/Spec.hs +++ b/agora-test/Spec.hs @@ -7,6 +7,7 @@ import Test.Tasty (defaultMain, testGroup) -------------------------------------------------------------------------------- import Spec.AuthorityToken qualified as AuthorityToken +import Spec.Effect.GovernorMutation qualified as GovernorMutation import Spec.Effect.TreasuryWithdrawal qualified as TreasuryWithdrawal import Spec.Governor qualified as Governor import Spec.Model.MultiSig qualified as MultiSig @@ -26,6 +27,9 @@ main = [ testGroup "Treasury Withdrawal Effect" TreasuryWithdrawal.tests + , testGroup + "Governor Mutation Effect" + GovernorMutation.tests ] , testGroup "Stake tests" diff --git a/agora-test/Spec/Effect/GovernorMutation.hs b/agora-test/Spec/Effect/GovernorMutation.hs new file mode 100644 index 0000000..1436b5a --- /dev/null +++ b/agora-test/Spec/Effect/GovernorMutation.hs @@ -0,0 +1,67 @@ +module Spec.Effect.GovernorMutation (tests) where + +import Agora.Effect.GovernorMutation (mutateGovernorValidator) +import Agora.Governor (GovernorDatum (..), GovernorRedeemer (MutateGovernor)) +import Agora.Governor.Scripts (governorValidator) +import Agora.Proposal (ProposalId (..)) +import Plutus.V1.Ledger.Api (ScriptContext (ScriptContext), ScriptPurpose (Spending)) +import Sample.Effect.GovernorMutation ( + effectRef, + govRef, + invalidNewGovernorDatum, + mkEffectDatum, + mkEffectTxInfo, + validNewGovernorDatum, + ) +import Sample.Shared qualified as Shared +import Test.Tasty (TestTree, testGroup) +import Test.Util (effectFailsWith, effectSucceedsWith, validatorFailsWith, validatorSucceedsWith) + +tests :: [TestTree] +tests = + [ testGroup + "validator" + [ testGroup + "valid new governor datum" + [ validatorSucceedsWith + "governor validator should pass" + (governorValidator Shared.governor) + ( GovernorDatum + { proposalThresholds = Shared.defaultProposalThresholds + , nextProposalId = ProposalId 0 + } + ) + MutateGovernor + ( ScriptContext + (mkEffectTxInfo validNewGovernorDatum) + (Spending govRef) + ) + , effectSucceedsWith + "effect validator should pass" + (mutateGovernorValidator Shared.governor) + (mkEffectDatum validNewGovernorDatum) + (ScriptContext (mkEffectTxInfo validNewGovernorDatum) (Spending effectRef)) + ] + , testGroup + "invalid new governor datum" + [ validatorFailsWith + "governor validator should fail" + (governorValidator Shared.governor) + ( GovernorDatum + { proposalThresholds = Shared.defaultProposalThresholds + , nextProposalId = ProposalId 0 + } + ) + MutateGovernor + ( ScriptContext + (mkEffectTxInfo invalidNewGovernorDatum) + (Spending govRef) + ) + , effectFailsWith + "effect validator should fail" + (mutateGovernorValidator Shared.governor) + (mkEffectDatum validNewGovernorDatum) + (ScriptContext (mkEffectTxInfo invalidNewGovernorDatum) (Spending effectRef)) + ] + ] + ] diff --git a/agora.cabal b/agora.cabal index 7d01530..9014c21 100644 --- a/agora.cabal +++ b/agora.cabal @@ -130,6 +130,7 @@ library exposed-modules: Agora.AuthorityToken Agora.Effect + Agora.Effect.GovernorMutation Agora.Effect.NoOp Agora.Effect.TreasuryWithdrawal Agora.Governor @@ -145,19 +146,21 @@ library Agora.Treasury Agora.Utils Agora.Utils.Value + Agora.ScriptInfo other-modules: + Agora.Aeson.Orphans hs-source-dirs: agora library pprelude + default-language: Haskell2010 + exposed-modules: PPrelude + hs-source-dirs: agora + build-depends: , base , plutarch - exposed-modules: PPrelude - hs-source-dirs: agora - default-language: Haskell2010 - library agora-testlib import: lang, deps, test-deps exposed-modules: Test.Util @@ -165,17 +168,18 @@ library agora-testlib library agora-sample import: lang, deps, test-deps - build-depends: agora-testlib exposed-modules: + Sample.Effect.GovernorMutation Sample.Effect.TreasuryWithdrawal Sample.Governor Sample.Proposal Sample.Shared Sample.Stake Sample.Treasury - hs-source-dirs: agora-sample + build-depends: agora-testlib + test-suite agora-test import: lang, deps, test-deps type: exitcode-stdio-1.0 @@ -183,6 +187,7 @@ test-suite agora-test hs-source-dirs: agora-test other-modules: Spec.AuthorityToken + Spec.Effect.GovernorMutation Spec.Effect.TreasuryWithdrawal Spec.Governor Spec.Model.MultiSig @@ -205,16 +210,26 @@ benchmark agora-bench , agora , agora-sample +executable agora-scripts + import: lang, deps, exe-opts + main-is: Scripts.hs + hs-source-dirs: agora-scripts + other-modules: + Options + build-depends: + , agora + , optparse-applicative + executable agora-purescript-bridge import: lang, deps, exe-opts main-is: Bridge.hs + hs-source-dirs: agora-purescript-bridge + other-modules: + AgoraTypes + Options + build-depends: , agora , optparse-applicative , path , purescript-bridge - - hs-source-dirs: agora-purescript-bridge - other-modules: - AgoraTypes - Options diff --git a/agora/Agora/Aeson/Orphans.hs b/agora/Agora/Aeson/Orphans.hs new file mode 100644 index 0000000..522643a --- /dev/null +++ b/agora/Agora/Aeson/Orphans.hs @@ -0,0 +1,146 @@ +{-# OPTIONS_GHC -Wno-orphans #-} + +module Agora.Aeson.Orphans (AsBase16Bytes (..)) where + +-------------------------------------------------------------------------------- + +import Data.Coerce (Coercible, coerce) +import Prelude + +-------------------------------------------------------------------------------- + +import Codec.Serialise qualified as Codec +import Data.Aeson qualified as Aeson +import Data.Aeson.Types qualified as Aeson +import Data.ByteString.Lazy qualified as Lazy +import Data.Text qualified as T +import Data.Text.Encoding qualified as T + +-------------------------------------------------------------------------------- + +import Plutus.V1.Ledger.Api qualified as Plutus +import Plutus.V1.Ledger.Bytes qualified as Plutus +import Plutus.V1.Ledger.Value qualified as Plutus + +-------------------------------------------------------------------------------- + +newtype AsBase16Bytes a = AsBase16Bytes {unAsBase16Bytes :: a} +newtype AsBase16Codec a = AsBase16Codec {unAsBase16Codec :: a} + +deriving via + (Plutus.CurrencySymbol, Plutus.TokenName) + instance + Aeson.ToJSON Plutus.AssetClass + +deriving via + (Plutus.CurrencySymbol, Plutus.TokenName) + instance + Aeson.FromJSON Plutus.AssetClass + +deriving via + AsBase16Bytes Plutus.TxId + instance + Aeson.FromJSON Plutus.TxId + +deriving via + AsBase16Bytes Plutus.TxId + instance + Aeson.ToJSON Plutus.TxId + +deriving anyclass instance Aeson.FromJSON Plutus.TxOutRef +deriving anyclass instance Aeson.ToJSON Plutus.TxOutRef + +instance (Coercible a Plutus.LedgerBytes) => Aeson.ToJSON (AsBase16Bytes a) where + toJSON = + Aeson.String + . Plutus.encodeByteString + . Plutus.bytes + . coerce @(AsBase16Bytes a) @Plutus.LedgerBytes + +instance (Coercible Plutus.LedgerBytes a) => Aeson.FromJSON (AsBase16Bytes a) where + parseJSON v = + Aeson.parseJSON @T.Text v + >>= either (Aeson.parserThrowError []) (pure . coerce @_ @(AsBase16Bytes a)) + . Plutus.fromHex + . T.encodeUtf8 + +instance (Codec.Serialise a) => Aeson.ToJSON (AsBase16Codec a) where + toJSON = + Aeson.String + . Plutus.encodeByteString + . Lazy.toStrict + . Codec.serialise @a + . (.unAsBase16Codec) + +instance (Codec.Serialise a) => Aeson.FromJSON (AsBase16Codec a) where + parseJSON v = + Aeson.parseJSON @T.Text v + >>= either (Aeson.parserThrowError [] . show) (pure . AsBase16Codec) + . Codec.deserialiseOrFail + . Lazy.fromStrict + . T.encodeUtf8 + +-------------------------------------------------------------------------------- + +deriving via + (AsBase16Bytes Plutus.CurrencySymbol) + instance + (Aeson.ToJSON Plutus.CurrencySymbol) +deriving via + (AsBase16Bytes Plutus.CurrencySymbol) + instance + (Aeson.FromJSON Plutus.CurrencySymbol) + +deriving via + (AsBase16Bytes Plutus.TokenName) + instance + (Aeson.ToJSON Plutus.TokenName) +deriving via + (AsBase16Bytes Plutus.TokenName) + instance + (Aeson.FromJSON Plutus.TokenName) + +deriving via + (AsBase16Bytes Plutus.ValidatorHash) + instance + (Aeson.ToJSON Plutus.ValidatorHash) +deriving via + (AsBase16Bytes Plutus.ValidatorHash) + instance + (Aeson.FromJSON Plutus.ValidatorHash) + +deriving via + (AsBase16Codec Plutus.Validator) + instance + (Aeson.ToJSON Plutus.Validator) +deriving via + (AsBase16Codec Plutus.Validator) + instance + (Aeson.FromJSON Plutus.Validator) + +deriving via + (AsBase16Codec Plutus.MintingPolicy) + instance + (Aeson.ToJSON Plutus.MintingPolicy) +deriving via + (AsBase16Codec Plutus.MintingPolicy) + instance + (Aeson.FromJSON Plutus.MintingPolicy) + +deriving via + (AsBase16Codec Plutus.Script) + instance + (Aeson.ToJSON Plutus.Script) +deriving via + (AsBase16Codec Plutus.Script) + instance + (Aeson.FromJSON Plutus.Script) + +deriving via + Integer + instance + (Aeson.ToJSON Plutus.POSIXTime) +deriving via + Integer + instance + (Aeson.FromJSON Plutus.POSIXTime) diff --git a/agora/Agora/Effect.hs b/agora/Agora/Effect.hs index 8fb40ba..c35ed55 100644 --- a/agora/Agora/Effect.hs +++ b/agora/Agora/Effect.hs @@ -23,7 +23,7 @@ import Plutus.V1.Ledger.Value (CurrencySymbol) -} makeEffect :: forall (datum :: PType). - (PIsData datum, PTryFrom PData datum) => + (PIsData datum, PTryFrom PData (PAsData datum)) => CurrencySymbol -> (forall (s :: S). Term s PCurrencySymbol -> Term s datum -> Term s PTxOutRef -> Term s (PAsData PTxInfo) -> Term s POpaque) -> ClosedTerm PValidator @@ -35,7 +35,7 @@ makeEffect gatCs' f = -- convert input datum, PData, into desierable type -- the way this conversion is performed should be defined -- by PTryFrom for each datum in effect script. - (datum', _) <- tctryFrom @datum datum + (pfromData -> datum', _) <- tctryFrom datum -- ensure purpose is Spending. PSpending txOutRef <- tcmatch $ pfromData ctx.purpose diff --git a/agora/Agora/Effect/GovernorMutation.hs b/agora/Agora/Effect/GovernorMutation.hs new file mode 100644 index 0000000..fb28e33 --- /dev/null +++ b/agora/Agora/Effect/GovernorMutation.hs @@ -0,0 +1,214 @@ +{-# LANGUAGE TemplateHaskell #-} + +{- | +Module : Agora.Effect.GovernorMutation +Maintainer : connor@mlabs.city +Description: An effect that mutates governor settings. + +An effect for mutating governor settings. +-} +module Agora.Effect.GovernorMutation ( + -- * Haskell-land + MutateGovernorDatum (..), + + -- * Plutarch-land + PMutateGovernorDatum (..), + + -- * Scripts + mutateGovernorValidator, +) where + +-------------------------------------------------------------------------------- + +import Control.Applicative (Const) +import GHC.Generics qualified as GHC +import Generics.SOP (Generic, I (I)) + +-------------------------------------------------------------------------------- + +import Plutarch.Api.V1 ( + PTxOutRef, + PValidator, + PValue, + ) +import Plutarch.Api.V1.Extra (pvalueOf) +import Plutarch.DataRepr ( + DerivePConstantViaData (..), + PDataFields, + PIsDataReprInstances (PIsDataReprInstances), + ) +import Plutarch.Lift (PConstantDecl, PLifted, PUnsafeLiftDecl) +import Plutarch.TryFrom (PTryFrom (..)) +import Plutarch.Unsafe (punsafeCoerce) + +-------------------------------------------------------------------------------- + +import Plutus.V1.Ledger.Api (TxOutRef) +import Plutus.V1.Ledger.Value (AssetClass (..)) +import PlutusTx qualified + +-------------------------------------------------------------------------------- + +import Agora.Effect (makeEffect) +import Agora.Governor ( + Governor, + GovernorDatum, + PGovernorDatum, + governorDatumValid, + ) +import Agora.Governor.Scripts ( + authorityTokenSymbolFromGovernor, + governorSTAssetClassFromGovernor, + ) +import Agora.Utils ( + isScriptAddress, + mustBePDJust, + mustBePJust, + ptryFindDatum, + tcassert, + ) + +-------------------------------------------------------------------------------- + +-- | Haskell-level datum for the governor mutation effect script. +data MutateGovernorDatum = MutateGovernorDatum + { governorRef :: TxOutRef + -- ^ Referenced governor state UTXO should be updated by the effect. + , newDatum :: GovernorDatum + -- ^ The new settings for the governor. + } + deriving stock (Show, GHC.Generic) + deriving anyclass (Generic) + +PlutusTx.makeIsDataIndexed ''MutateGovernorDatum [('MutateGovernorDatum, 0)] + +-------------------------------------------------------------------------------- + +-- | Plutarch-level version of 'MutateGovernorDatum'. +newtype PMutateGovernorDatum (s :: S) + = PMutateGovernorDatum + ( Term + s + ( PDataRecord + '[ "governorRef" ':= PTxOutRef + , "newDatum" ':= PGovernorDatum + ] + ) + ) + deriving stock (GHC.Generic) + deriving anyclass (Generic) + deriving anyclass (PIsDataRepr) + deriving + (PlutusType, PIsData, PDataFields, PEq) + via (PIsDataReprInstances PMutateGovernorDatum) + +instance PUnsafeLiftDecl PMutateGovernorDatum where type PLifted PMutateGovernorDatum = MutateGovernorDatum +deriving via (DerivePConstantViaData MutateGovernorDatum PMutateGovernorDatum) instance (PConstantDecl MutateGovernorDatum) + +-- TODO: Derive this. +instance PTryFrom PData (PAsData PMutateGovernorDatum) where + type PTryFromExcess PData (PAsData PMutateGovernorDatum) = Const () + ptryFrom' d k = + k (punsafeCoerce d, ()) + +-------------------------------------------------------------------------------- + +{- | Validator for the governor mutation effect. + + This effect is implemented using the 'Agora.Effect.makeEffect' wrapper, + meaning that the burning of GAT is checked in said wrapper. + + In order to locate the governor, the validator is parametrized with a 'Agora.Governor.Governor'. + + All the information it needs to validate the effect is encoded in the 'MutateGovernorDatum', + so regardless what redeemer it's given, it will check: + + - No token is minted/burnt other than GAT. + - Nothing is being paid to the the effect validator. + - The governor's state UTXO must be spent: + + * It carries exactly one GST. + * It's referenced by 'governorRef' in the effect's datum. + + - A new state UTXO is paid to the governor: + + * It contains the GST. + * It has valid governor state datum. + * The datum is exactly the same as the 'newDatum'. +-} +mutateGovernorValidator :: Governor -> ClosedTerm PValidator +mutateGovernorValidator gov = makeEffect (authorityTokenSymbolFromGovernor gov) $ + \_gatCs (datum :: Term _ PMutateGovernorDatum) _ txInfo -> unTermCont $ do + datumF <- tcont $ pletFields @'["newDatum", "governorRef"] datum + txInfoF <- tcont $ pletFields @'["mint", "inputs", "outputs", "datums"] txInfo + + let mint :: Term _ (PBuiltinList _) + mint = pto $ pto $ pto $ pfromData txInfoF.mint + + tcassert "Nothing should be minted/burnt other than GAT" $ + plength # mint #== 1 + + -- Only two script inputs are alloed: one from the effect, one from the governor. + tcassert "Only self and governor script inputs are allowed" $ + pfoldr + # phoistAcyclic + ( plam $ \inInfo count -> + let address = pfield @"address" #$ pfield @"resolved" # inInfo + in pif + (isScriptAddress # address) + (count + 1) + count + ) + # (0 :: Term _ PInteger) + # pfromData txInfoF.inputs + #== 2 + + -- Find the governor input by looking for GST. + let inputWithGST = + mustBePJust # "Governor input not found" #$ pfind + # phoistAcyclic + ( plam $ \inInfo -> + let value = pfield @"value" #$ pfield @"resolved" # inInfo + in gstValueOf # value #== 1 + ) + # pfromData txInfoF.inputs + + govInInfo <- tcont $ pletFields @'["outRef", "resolved"] $ inputWithGST + + -- The effect can only modify the governor UTXO referenced in the datum. + tcassert "Can only modify the pinned governor" $ + govInInfo.outRef #== datumF.governorRef + + -- The transaction can only have one output, which should be sent to the governor. + tcassert "Only governor output is allowed" $ + plength # pfromData txInfoF.outputs #== 1 + + let govAddress = pfield @"address" #$ govInInfo.resolved + govOutput' = pfromData $ phead # pfromData txInfoF.outputs + + govOutput <- tcont $ pletFields @'["address", "value", "datumHash"] govOutput' + + tcassert "No output to the governor" $ + govOutput.address #== govAddress + + tcassert "Governor output doesn't carry the GST" $ + gstValueOf # govOutput.value #== 1 + + let governorOutputDatumHash = + mustBePDJust # "Governor output doesn't have datum" # govOutput.datumHash + governorOutputDatum = + pfromData @PGovernorDatum $ + mustBePJust # "Governor output datum not found" + #$ ptryFindDatum # governorOutputDatumHash # txInfoF.datums + + -- Ensure the output governor datum is what we want. + tcassert "Unexpected governor datum" $ datumF.newDatum #== governorOutputDatum + tcassert "New governor datum should be valid" $ governorDatumValid # governorOutputDatum + + return $ popaque $ pconstant () + where + -- Get the amount of GST in the a given value. + gstValueOf :: Term s (PValue :--> PInteger) + gstValueOf = phoistAcyclic $ plam $ \v -> pvalueOf # v # pconstant cs # pconstant tn + where + AssetClass (cs, tn) = governorSTAssetClassFromGovernor gov diff --git a/agora/Agora/Effect/NoOp.hs b/agora/Agora/Effect/NoOp.hs index a384675..39c63ee 100644 --- a/agora/Agora/Effect/NoOp.hs +++ b/agora/Agora/Effect/NoOp.hs @@ -18,13 +18,13 @@ import Plutus.V1.Ledger.Value (CurrencySymbol) newtype PNoOp (s :: S) = PNoOp (Term s PUnit) deriving (PlutusType, PIsData) via (DerivePNewtype PNoOp PUnit) -instance PTryFrom PData PNoOp where - type PTryFromExcess PData PNoOp = Const () +instance PTryFrom PData (PAsData PNoOp) where + type PTryFromExcess PData (PAsData PNoOp) = Const () ptryFrom' _ cont = -- JUSTIFICATION: -- We don't care anything about data. -- It should always be reduced to Unit. - cont (pcon $ PNoOp (pconstant ()), ()) + cont (pdata $ pcon $ PNoOp (pconstant ()), ()) -- | Dummy effect which can only burn its GAT. noOpValidator :: CurrencySymbol -> ClosedTerm PValidator diff --git a/agora/Agora/Effect/TreasuryWithdrawal.hs b/agora/Agora/Effect/TreasuryWithdrawal.hs index 5bad045..5bf451c 100644 --- a/agora/Agora/Effect/TreasuryWithdrawal.hs +++ b/agora/Agora/Effect/TreasuryWithdrawal.hs @@ -18,7 +18,7 @@ import GHC.Generics qualified as GHC import Generics.SOP (Generic, I (I)) import Agora.Effect (makeEffect) -import Agora.Utils (findTxOutByTxOutRef, paddValue, tcassert, tclet, tcmatch) +import Agora.Utils (findTxOutByTxOutRef, isPubKey, paddValue, tcassert, tclet, tcmatch) import Plutarch.Api.V1 ( PCredential (..), PTuple, @@ -82,8 +82,8 @@ deriving via instance (PConstantDecl TreasuryWithdrawalDatum) -instance PTryFrom PData PTreasuryWithdrawalDatum where - type PTryFromExcess PData PTreasuryWithdrawalDatum = Const () +instance PTryFrom PData (PAsData PTreasuryWithdrawalDatum) where + type PTryFromExcess PData (PAsData PTreasuryWithdrawalDatum) = Const () ptryFrom' opq cont = -- TODO: This should not use 'punsafeCoerce'. -- Blocked by 'PCredential', and 'PTuple'. @@ -140,12 +140,6 @@ treasuryWithdrawalValidator currSymbol = makeEffect currSymbol $ treasuryInputValuesSum = sumValues #$ ofTreasury # inputValues treasuryOutputValuesSum = sumValues #$ ofTreasury # outputValues receiverValuesSum = sumValues # datum.receivers - isPubkey = plam $ \cred -> - pmatch cred $ - \case - PPubKeyCredential _ -> pcon PTrue - PScriptCredential _ -> pcon PFalse - -- Constraints outputContentMatchesRecivers = pall # plam (\out -> pelem # out # outputValues) @@ -165,7 +159,7 @@ treasuryWithdrawalValidator currSymbol = makeEffect currSymbol $ ( \((pfield @"_0" #) . pfromData -> cred) -> cred #== pfield @"credential" # effInput.address #|| pelem # cred # datum.treasuries - #|| isPubkey # pfromData cred + #|| isPubKey # pfromData cred ) # inputValues diff --git a/agora/Agora/SafeMoney.hs b/agora/Agora/SafeMoney.hs index f94ae8d..2a469ea 100644 --- a/agora/Agora/SafeMoney.hs +++ b/agora/Agora/SafeMoney.hs @@ -8,6 +8,9 @@ Tags and extras for "Plutarch.SafeMoney". module Agora.SafeMoney ( ADATag, GTTag, + GovernorSTTag, + StakeSTTag, + ProposalSTTag, adaRef, ) where @@ -18,7 +21,7 @@ import Plutus.V1.Ledger.Value (AssetClass (AssetClass)) import Plutarch.SafeMoney -------------------------------------------------------------------------------- --- Example tags +-- Tags -- | Governance token. data GTTag @@ -26,6 +29,15 @@ data GTTag -- | ADA. data ADATag +-- | Governor ST token. +data GovernorSTTag + +-- | Stake ST token. +data StakeSTTag + +-- | Proposal ST token. +data ProposalSTTag + -------------------------------------------------------------------------------- -- | Resolves ada tags. diff --git a/agora/Agora/ScriptInfo.hs b/agora/Agora/ScriptInfo.hs new file mode 100644 index 0000000..f9643ae --- /dev/null +++ b/agora/Agora/ScriptInfo.hs @@ -0,0 +1,63 @@ +{- | +Module : Agora.ScriptInfo +Maintainer : emi@haskell.fyi +Description: Exportable script bundles for off-chain consumption. + +Exportable script bundles for off-chain consumption. +-} +module Agora.ScriptInfo ( + -- * Types + PolicyInfo (..), + ValidatorInfo (..), + + -- * Introduction functions + mkValidatorInfo, + mkPolicyInfo, +) where + +import Agora.Aeson.Orphans () +import Data.Aeson qualified as Aeson +import GHC.Generics qualified as GHC +import Plutarch.Api.V1 (PMintingPolicy, PValidator, mintingPolicySymbol, mkMintingPolicy, mkValidator, validatorHash) +import Plutus.V1.Ledger.Api (MintingPolicy, Validator, ValidatorHash) +import Plutus.V1.Ledger.Value (CurrencySymbol) + +-- | Bundle containing a 'Validator' and its hash. +data ValidatorInfo = ValidatorInfo + { script :: Validator + -- ^ The validator script. + , hash :: ValidatorHash + -- ^ Hash of the validator. + } + deriving stock (Show, Eq, GHC.Generic) + deriving anyclass (Aeson.ToJSON, Aeson.FromJSON) + +-- | Create a 'ValidatorInfo' given a Plutarch term. +mkValidatorInfo :: ClosedTerm PValidator -> ValidatorInfo +mkValidatorInfo term = + ValidatorInfo + { script = validator + , hash = validatorHash validator + } + where + validator = mkValidator term + +-- | Bundle containing a 'MintingPolicy' and its symbol. +data PolicyInfo = PolicyInfo + { policy :: MintingPolicy + -- ^ The minting policy. + , currencySymbol :: CurrencySymbol + -- ^ The symbol given by the minting policy. + } + deriving stock (Show, Eq, GHC.Generic) + deriving anyclass (Aeson.ToJSON, Aeson.FromJSON) + +-- | Create a 'PolicyInfo' given a Plutarch term. +mkPolicyInfo :: ClosedTerm PMintingPolicy -> PolicyInfo +mkPolicyInfo term = + PolicyInfo + { policy = policy + , currencySymbol = mintingPolicySymbol policy + } + where + policy = mkMintingPolicy term diff --git a/agora/Agora/Treasury.hs b/agora/Agora/Treasury.hs index f472243..475c936 100644 --- a/agora/Agora/Treasury.hs +++ b/agora/Agora/Treasury.hs @@ -72,6 +72,7 @@ deriving via do so in a valid manner. -} treasuryValidator :: + -- | Governance Authority Token that can unlock this validator. CurrencySymbol -> ClosedTerm PValidator treasuryValidator gatCs' = plam $ \_datum redeemer ctx' -> unTermCont $ do diff --git a/agora/Agora/Utils.hs b/agora/Agora/Utils.hs index 8c16d85..62ce8f3 100644 --- a/agora/Agora/Utils.hs +++ b/agora/Agora/Utils.hs @@ -58,6 +58,8 @@ module Agora.Utils ( validatorHashToAddress, pmergeBy, phalve, + isScriptAddress, + isPubKey, ) where -------------------------------------------------------------------------------- @@ -210,7 +212,7 @@ pfromMaybe = phoistAcyclic $ PJust a' -> a' PNothing -> e --- | Yield True if a given PMaybe is of form PJust _. +-- | Yield True if a given PMaybe is of form @'PJust' _@. pisJust :: forall a s. Term s (PMaybe a :--> PBool) pisJust = phoistAcyclic $ plam $ \v' -> @@ -579,6 +581,19 @@ scriptHashFromAddress = phoistAcyclic $ PScriptCredential ((pfield @"_0" #) -> h) -> pcon $ PJust h _ -> pcon PNothing +-- | Return true if the given address is a script address. +isScriptAddress :: Term s (PAddress :--> PBool) +isScriptAddress = phoistAcyclic $ + plam $ \addr -> pnot #$ isPubKey #$ pfromData $ pfield @"credential" # addr + +-- | Return true if the given credential is a pub-key-hash. +isPubKey :: Term s (PCredential :--> PBool) +isPubKey = phoistAcyclic $ + plam $ \cred -> + pmatch cred $ \case + PScriptCredential _ -> pconstant False + _ -> pconstant True + -- | Find all TxOuts sent to an Address findOutputsToAddress :: Term s (PBuiltinList (PAsData PTxOut) :--> PAddress :--> PBuiltinList (PAsData PTxOut)) findOutputsToAddress = phoistAcyclic $