Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ changes.
- Add CIP-129 support for gov_actions hashes in Live Voting (governance actions) [Issue 3619](https://github.com/IntersectMBO/govtool/issues/3619)

- Add maintenance ending banner [Issue 3647](https://github.com/IntersectMBO/govtool/issues/3647)
- Add support for the Protocol Parameter Change and Hard Fork Initiation governance actions

### Fixed

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
SELECT
gap.id,
tx_id,
index,
description,
encode(hash, 'hex') AS hash
FROM
gov_action_proposal gap
JOIN
tx ON gap.tx_id = tx.id
WHERE
gap.type = ? AND gap.enacted_epoch IS NOT NULL
ORDER BY
gap.id DESC
LIMIT 1;
29 changes: 29 additions & 0 deletions govtool/backend/src/VVA/API.hs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ type VVAApi =
:> QueryParam "search" Text
:> Get '[JSON] ListProposalsResponse
:<|> "proposal" :> "get" :> Capture "proposalId" GovActionId :> QueryParam "drepId" HexText :> Get '[JSON] GetProposalResponse
:<|> "proposal" :> "enacted-details" :> QueryParam "type" GovernanceActionType :> Get '[JSON] (Maybe EnactedProposalDetailsResponse)
:<|> "epoch" :> "params" :> Get '[JSON] GetCurrentEpochParamsResponse
:<|> "transaction" :> "status" :> Capture "transactionId" HexText :> Get '[JSON] GetTransactionStatusResponse
:<|> "throw500" :> Get '[JSON] ()
Expand All @@ -95,6 +96,7 @@ server = drepList
:<|> getStakeKeyVotingPower
:<|> listProposals
:<|> getProposal
:<|> getEnactedProposalDetails
:<|> getCurrentEpochParams
:<|> getTransactionStatus
:<|> throw500
Expand Down Expand Up @@ -442,6 +444,33 @@ getProposal g@(GovActionId govActionTxHash govActionIndex) mDrepId' = do
, getProposalResponseVote = voteResponse
}

getEnactedProposalDetails :: App m => Maybe GovernanceActionType -> m (Maybe EnactedProposalDetailsResponse)
getEnactedProposalDetails maybeType = do
let proposalType = maybe "HardForkInitiation" governanceActionTypeToText maybeType

mDetails <- Proposal.getPreviousEnactedProposal proposalType

let response = enactedProposalDetailsToResponse <$> mDetails

return response
where
governanceActionTypeToText :: GovernanceActionType -> Text
governanceActionTypeToText actionType =
case actionType of
HardForkInitiation -> "HardForkInitiation"
ParameterChange -> "ParameterChange"
_ -> "HardForkInitiation"

enactedProposalDetailsToResponse :: Types.EnactedProposalDetails -> EnactedProposalDetailsResponse
enactedProposalDetailsToResponse Types.EnactedProposalDetails{..} =
EnactedProposalDetailsResponse
{ enactedProposalDetailsResponseId = enactedProposalDetailsId
, enactedProposalDetailsResponseTxId = enactedProposalDetailsTxId
, enactedProposalDetailsResponseIndex = enactedProposalDetailsIndex
, enactedProposalDetailsResponseDescription = enactedProposalDetailsDescription
, enactedProposalDetailsResponseHash = HexText enactedProposalDetailsHash
}

getCurrentEpochParams :: App m => m GetCurrentEpochParamsResponse
getCurrentEpochParams = do
CacheEnv {currentEpochCache} <- asks vvaCache
Expand Down
34 changes: 34 additions & 0 deletions govtool/backend/src/VVA/API/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,40 @@ instance ToSchema ProposalResponse where
& example
?~ toJSON exampleProposalResponse

data EnactedProposalDetailsResponse
= EnactedProposalDetailsResponse
{ enactedProposalDetailsResponseId :: Integer
, enactedProposalDetailsResponseTxId :: Integer
, enactedProposalDetailsResponseIndex :: Integer
, enactedProposalDetailsResponseDescription :: Maybe Value
, enactedProposalDetailsResponseHash :: HexText
}
deriving (Generic, Show)

deriveJSON (jsonOptions "enactedProposalDetailsResponse") ''EnactedProposalDetailsResponse

exampleEnactedProposalDetailsResponse :: Text
exampleEnactedProposalDetailsResponse = "{ \"id\": 123,"
<> "\"txId\": 456,"
<> "\"index\": 0,"
<> "\"description\": {\"key\": \"value\"},"
<> "\"hash\": \"9af10e89979e51b8cdc827c963124a1ef4920d1253eef34a1d5cfe76438e3f11\"}"

instance ToSchema EnactedProposalDetailsResponse where
declareNamedSchema proxy = do
NamedSchema name_ schema_ <-
genericDeclareNamedSchema
( fromAesonOptions $
jsonOptions "enactedProposalDetailsResponse"
)
proxy
return $
NamedSchema name_ $
schema_
& description ?~ "Enacted Proposal Details Response"
& example
?~ toJSON exampleEnactedProposalDetailsResponse

exampleListProposalsResponse :: Text
exampleListProposalsResponse =
"{ \"page\": 0,"
Expand Down
59 changes: 50 additions & 9 deletions govtool/backend/src/VVA/Proposal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,34 @@ import Data.Aeson.Types (Parser, parseMaybe)
import Data.ByteString (ByteString)
import Data.FileEmbed (embedFile)
import Data.Foldable (fold)
import Data.Has (Has)
import qualified Data.Map as Map
import Data.Maybe (fromMaybe)
import Data.Has (Has, getter)
import qualified Data.Map as Map
import Data.Maybe (fromMaybe, isJust)
import Data.Monoid (Sum (..), getSum)
import Data.Scientific
import Data.String (fromString)
import Data.Text (Text, pack, unpack)
import qualified Data.Text.Encoding as Text
import qualified Data.Text.IO as Text
import qualified Data.Text.Encoding as Text
import qualified Data.Text.IO as Text
import Data.Time

import qualified Database.PostgreSQL.Simple as SQL
import qualified Database.PostgreSQL.Simple.Types as PG
import qualified Database.PostgreSQL.Simple as SQL
import qualified Database.PostgreSQL.Simple.Types as PG
import Database.PostgreSQL.Simple.ToField (ToField(..))
import Database.PostgreSQL.Simple.ToRow (ToRow(..))

import GHC.IO.Unsafe (unsafePerformIO)

import VVA.Config
import VVA.Pool (ConnectionPool, withPool)
import VVA.Types (AppError (..), Proposal (..))
import VVA.Types (AppError (..), Proposal (..), EnactedProposalDetails (..))

query1 :: (SQL.ToRow q, SQL.FromRow r) => SQL.Connection -> SQL.Query -> q -> IO (Maybe r)
query1 conn q params = do
results <- SQL.query conn q params
case results of
[x] -> return (Just x)
_ -> return Nothing

sqlFrom :: ByteString -> SQL.Query
sqlFrom bs = fromString $ unpack $ Text.decodeUtf8 bs
Expand Down Expand Up @@ -84,4 +93,36 @@ getProposals mSearchTerms = withPool $ \conn -> do
Left (e :: SomeException) -> do
putStrLn $ "Error fetching proposals: " <> show e
return []
Right rows -> return rows
Right rows -> return rows

latestEnactedProposalSql :: SQL.Query
latestEnactedProposalSql =
let rawSql = sqlFrom $(embedFile "sql/get-previous-enacted-governance-action-proposal-details.sql")
in unsafePerformIO $ do
putStrLn $ "[DEBUG] SQL query content: " ++ show rawSql
return rawSql

getPreviousEnactedProposal ::
(Has ConnectionPool r, Has VVAConfig r, MonadReader r m, MonadIO m, MonadFail m, MonadError AppError m) =>
Text ->
m (Maybe EnactedProposalDetails)
getPreviousEnactedProposal proposalType = withPool $ \conn -> do
let query = latestEnactedProposalSql
let params = [proposalType]

result <- liftIO $ try $ do
rows <- SQL.query conn query params :: IO [EnactedProposalDetails]
case rows of
[x] -> return (Just x)
_ -> return Nothing

case result of
Left err -> do
throwError $ CriticalError $ "Database error: " <> pack (show (err :: SomeException))
Right proposal -> do
case proposal of
Just details -> do
liftIO $ putStrLn $ "[DEBUG] Previous enacted proposal details: " ++ show details
Nothing ->
liftIO $ putStrLn "[DEBUG] No previous enacted proposal found"
return proposal
34 changes: 34 additions & 0 deletions govtool/backend/src/VVA/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,40 @@ instance ToJSON TransactionStatus where
, "votingProcedure" .= votingProcedure
]

data EnactedProposalDetails = EnactedProposalDetails
{ enactedProposalDetailsId :: Integer
, enactedProposalDetailsTxId :: Integer
, enactedProposalDetailsIndex :: Integer
, enactedProposalDetailsDescription :: Maybe Value
, enactedProposalDetailsHash :: Text
}
deriving (Show)

instance FromRow EnactedProposalDetails where
fromRow =
EnactedProposalDetails
<$> field
<*> field
<*> (floor @Scientific <$> field)
<*> field
<*> field

instance ToJSON EnactedProposalDetails where
toJSON EnactedProposalDetails
{ enactedProposalDetailsId
, enactedProposalDetailsTxId
, enactedProposalDetailsIndex
, enactedProposalDetailsDescription
, enactedProposalDetailsHash
} =
object
[ "id" .= enactedProposalDetailsId
, "tx_id" .= enactedProposalDetailsTxId
, "index" .= enactedProposalDetailsIndex
, "description" .= enactedProposalDetailsDescription
, "hash" .= enactedProposalDetailsHash
]

data CacheEnv
= CacheEnv
{ proposalListCache :: Cache.Cache () [Proposal]
Expand Down
1 change: 1 addition & 0 deletions govtool/backend/vva-be.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ extra-source-files:
sql/get-network-total-stake.sql
sql/get-dreps-voting-power-list.sql
sql/get-filtered-dreps-voting-power.sql
sql/get-previous-enacted-governance-action-proposal-details.sql
executable vva-be
main-is: Main.hs

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ export const CreateGovernanceActionForm = ({
| GovernanceActionType.NewCommittee
| GovernanceActionType.NewConstitution
| GovernanceActionType.NoConfidence
| GovernanceActionType.HardForkInitiation
| GovernanceActionType.ParameterChange
],
).some(
(field) => !watch(field as unknown as Parameters<typeof watch>[0]),
Expand All @@ -73,6 +75,8 @@ export const CreateGovernanceActionForm = ({
| GovernanceActionType.NewCommittee
| GovernanceActionType.NewConstitution
| GovernanceActionType.NoConfidence
| GovernanceActionType.HardForkInitiation
| GovernanceActionType.ParameterChange
],
).map(([key, field]) => {
const fieldProps = {
Expand All @@ -85,6 +89,7 @@ export const CreateGovernanceActionForm = ({
? t(field.placeholderI18nKey)
: undefined,
rules: field.rules,
maxLength: field.maxLength,
};

if (field.component === GovernanceActionField.Input) {
Expand Down
82 changes: 82 additions & 0 deletions govtool/frontend/src/consts/governanceAction/fields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,88 @@ export const GOVERNANCE_ACTION_FIELDS: GovernanceActionFields = {
"createGovernanceAction.fields.declarations.scriptHash.placeholder",
},
},
[GovernanceActionType.HardForkInitiation]: {
...sharedGovernanceActionFields,
prevGovernanceActionHash: {
component: GovernanceActionField.Input,
labelI18nKey:
"createGovernanceAction.fields.declarations.prevGovernanceActionHash.label",
placeholderI18nKey:
"createGovernanceAction.fields.declarations.prevGovernanceActionHash.placeholder",
},
prevGovernanceActionIndex: {
component: GovernanceActionField.Input,
labelI18nKey:
"createGovernanceAction.fields.declarations.prevGovernanceActionIndex.label",
placeholderI18nKey:
"createGovernanceAction.fields.declarations.prevGovernanceActionIndex.placeholder",
rules: {
validate: numberValidation,
},
},
major: {
component: GovernanceActionField.Input,
labelI18nKey: "createGovernanceAction.fields.declarations.major.label",
placeholderI18nKey:
"createGovernanceAction.fields.declarations.major.placeholder",
rules: {
required: {
value: true,
message: I18n.t("createGovernanceAction.fields.validations.required"),
},
validate: numberValidation,
},
},
minor: {
component: GovernanceActionField.Input,
labelI18nKey: "createGovernanceAction.fields.declarations.minor.label",
placeholderI18nKey:
"createGovernanceAction.fields.declarations.minor.placeholder",
rules: {
required: {
value: true,
message: I18n.t("createGovernanceAction.fields.validations.required"),
},
validate: numberValidation,
},
},
},
[GovernanceActionType.ParameterChange]: {
...sharedGovernanceActionFields,
prevGovernanceActionHash: {
component: GovernanceActionField.Input,
labelI18nKey:
"createGovernanceAction.fields.declarations.prevGovernanceActionHash.label",
placeholderI18nKey:
"createGovernanceAction.fields.declarations.prevGovernanceActionHash.placeholder",
},
prevGovernanceActionIndex: {
component: GovernanceActionField.Input,
labelI18nKey:
"createGovernanceAction.fields.declarations.prevGovernanceActionIndex.label",
placeholderI18nKey:
"createGovernanceAction.fields.declarations.prevGovernanceActionIndex.placeholder",
rules: {
validate: numberValidation,
},
},
protocolParameters: {
component: GovernanceActionField.TextArea,
maxLength: 5000,
labelI18nKey:
"createGovernanceAction.fields.declarations.protocolParameters.label",
placeholderI18nKey:
"createGovernanceAction.fields.declarations.protocolParameters.placeholder",
rules: {
required: {
value: true,
message: I18n.t("createGovernanceAction.fields.validations.required"),
},
},
tipI18nKey:
"createGovernanceAction.fields.declarations.protocolParameters.tip",
},
},
} as const;

export const GOVERNANCE_ACTION_CONTEXT = {
Expand Down
5 changes: 2 additions & 3 deletions govtool/frontend/src/context/wallet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ type TreasuryProps = {

type ProtocolParameterChangeProps = {
prevGovernanceActionHash: string;
prevGovernanceActionIndex: number;
prevGovernanceActionIndex: string;
protocolParamsUpdate: Partial<ProtocolParamsUpdate>;
} & VotingAnchor;

Expand Down Expand Up @@ -166,7 +166,6 @@ export type QuorumThreshold = {
numerator: string;
denominator: string;
};

type ProtocolParamsUpdate = {
adaPerUtxo: string;
collateralPercentage: number;
Expand Down Expand Up @@ -1343,7 +1342,7 @@ const CardanoProvider = (props: Props) => {
if (prevGovernanceActionHash && prevGovernanceActionIndex) {
const prevGovernanceActionId = GovernanceActionId.new(
TransactionHash.from_hex(prevGovernanceActionHash),
prevGovernanceActionIndex,
Number(prevGovernanceActionIndex),
);
protocolParamChangeAction =
ParameterChangeAction.new_with_policy_hash_and_action_id(
Expand Down
Loading