From 5973c3bcf902791e63844e8170101ba3b12ceacd Mon Sep 17 00:00:00 2001 From: Ciabas Date: Tue, 29 Jul 2025 09:53:52 +0200 Subject: [PATCH] (fix#3954): Incorrect Display of New Committee Parameters in Governance Action Details --- CHANGELOG.md | 1 + govtool/backend/sql/list-proposals.sql | 66 +++++++++++++++-- ...nceActionNewCommitteeDetailsTabContent.tsx | 71 ++++++++++--------- govtool/frontend/src/i18n/locales/en.json | 2 +- 4 files changed, 97 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c630fce7..911cd6242 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ changes. ### Fixed - Fix disappearing proposals in the governance actions list for the same tx hashes [Issue 3918](https://github.com/IntersectMBO/govtool/issues/3918) +- Fix incorrect display of new committee parameters in Governance Action details [Issue 3954](https://github.com/IntersectMBO/govtool/issues/3954) ### Changed diff --git a/govtool/backend/sql/list-proposals.sql b/govtool/backend/sql/list-proposals.sql index c0779c6d8..931c069b8 100644 --- a/govtool/backend/sql/list-proposals.sql +++ b/govtool/backend/sql/list-proposals.sql @@ -42,6 +42,21 @@ CommitteeData AS ( FROM committee_member cm JOIN committee_hash ch ON cm.committee_hash_id = ch.id + WHERE EXISTS ( + SELECT 1 + FROM committee_registration cr + WHERE cr.cold_key_id = ch.id + ) + AND NOT EXISTS ( + SELECT 1 + FROM committee_de_registration cdr + WHERE cdr.cold_key_id = ch.id + AND cdr.tx_id > ( + SELECT MAX(cr2.tx_id) + FROM committee_registration cr2 + WHERE cr2.cold_key_id = ch.id + ) + ) ORDER BY ch.raw, cm.expiration_epoch DESC ), @@ -61,10 +76,31 @@ ParsedDescription AS ( MembersToBeRemoved AS ( SELECT id, - json_agg(VALUE->>'keyHash') AS members_to_be_removed + json_agg( + json_build_object( + 'hash', COALESCE( + VALUE->>'keyHash', + VALUE->>'scriptHash' + ), + 'type', CASE + WHEN VALUE->>'keyHash' IS NOT NULL THEN 'keyHash' + WHEN VALUE->>'scriptHash' IS NOT NULL THEN 'scriptHash' + ELSE 'unknown' + END, + 'hasScript', CASE + WHEN VALUE->>'scriptHash' IS NOT NULL THEN true + ELSE false + END + ) + ) AS members_to_be_removed FROM ParsedDescription pd, - json_array_elements(members_to_be_removed::json) AS value + json_array_elements( + CASE + WHEN pd.members_to_be_removed IS NULL THEN '[]'::json + ELSE pd.members_to_be_removed::json + END + ) AS value GROUP BY id ), @@ -73,7 +109,15 @@ ProcessedCurrentMembers AS ( pd.id, json_agg( json_build_object( - 'hash', regexp_replace(kv.key, '^keyHash-', ''), + 'hash', COALESCE( + regexp_replace(kv.key, '^keyHash-', ''), + regexp_replace(kv.key, '^scriptHash-', '') + ), + 'type', CASE + WHEN kv.key LIKE 'keyHash-%' THEN 'keyHash' + WHEN kv.key LIKE 'scriptHash-%' THEN 'scriptHash' + ELSE 'unknown' + END, 'newExpirationEpoch', kv.value::int ) ) AS current_members @@ -88,9 +132,17 @@ EnrichedCurrentMembers AS ( pcm.id, json_agg( json_build_object( - 'hash', cm.hash, + 'hash', CASE + WHEN (member->>'hash') LIKE 'scriptHash-%' THEN + regexp_replace(member->>'hash', '^scriptHash-', '') + WHEN (member->>'hash') LIKE 'keyHash-%' THEN + regexp_replace(member->>'hash', '^keyHash-', '') + ELSE + member->>'hash' + END, + 'type', member->>'type', 'expirationEpoch', cm.expiration_epoch, - 'hasScript', cm.has_script, + 'hasScript', COALESCE(cm.has_script, member->>'type' = 'scriptHash'), 'newExpirationEpoch', (member->>'newExpirationEpoch')::int ) ) AS enriched_members @@ -247,9 +299,9 @@ SELECT ) FROM ParsedDescription pd - JOIN + LEFT JOIN MembersToBeRemoved mtr ON pd.id = mtr.id - JOIN + LEFT JOIN EnrichedCurrentMembers em ON pd.id = em.id WHERE pd.id = gov_action_proposal.id diff --git a/govtool/frontend/src/components/molecules/GovernanceActionNewCommitteeDetailsTabContent.tsx b/govtool/frontend/src/components/molecules/GovernanceActionNewCommitteeDetailsTabContent.tsx index e3a2a286a..ffa415025 100644 --- a/govtool/frontend/src/components/molecules/GovernanceActionNewCommitteeDetailsTabContent.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceActionNewCommitteeDetailsTabContent.tsx @@ -12,41 +12,51 @@ type CCMember = { hash: string; newExpirationEpoch?: number; }; +type CCMemberToBeRemoved = { + hash: string; + hasScript?: boolean; +}; -function isArrayOfStrings(value: unknown): value is string[] { - return ( - Array.isArray(value) && value.every((item) => typeof item === "string") - ); -} +const getCip129Identifier = (hash: string, hasScript?: boolean) => + encodeCIP129Identifier({ + txID: (hasScript ? "13" : "12") + hash, + bech32Prefix: "cc_cold", + }); export const GovernanceActionNewCommitteeDetailsTabContent = ({ details, }: Pick) => { const { t } = useTranslation(); const membersToBeAdded = ((details?.members as CCMember[]) || []) - .filter((member) => member.newExpirationEpoch === undefined) + .filter( + (member) => + member?.expirationEpoch === undefined || + member?.expirationEpoch === null, + ) + .filter((member) => member?.hash) .map((member) => ({ - cip129Identifier: encodeCIP129Identifier({ - txID: (member.hasScript ? "02" : "13") + member.hash, - bech32Prefix: member.hasScript ? "cc_hot" : "cc_cold", - }), + cip129Identifier: getCip129Identifier(member.hash, member.hasScript), expirationEpoch: member.expirationEpoch, })); const membersToBeUpdated = ((details?.members as CCMember[]) || []) - .filter((member) => member.newExpirationEpoch !== undefined) + .filter( + (member) => !!member?.expirationEpoch && !!member?.newExpirationEpoch, + ) + .filter((member) => member?.hash) .map((member) => ({ - cip129Identifier: encodeCIP129Identifier({ - txID: (member.hasScript ? "02" : "13") + member.hash, - bech32Prefix: member.hasScript ? "cc_hot" : "cc_cold", - }), + cip129Identifier: getCip129Identifier(member.hash, member.hasScript), expirationEpoch: member.expirationEpoch, newExpirationEpoch: member.newExpirationEpoch, })); - const membersToBeRemoved = isArrayOfStrings(details?.membersToBeRemoved) - ? details.membersToBeRemoved - : []; + const membersToBeRemoved = ( + (details?.membersToBeRemoved as CCMemberToBeRemoved[]) || [] + ) + .filter((member) => member?.hash && member.hash.trim() !== "") + .map((member) => ({ + cip129Identifier: getCip129Identifier(member.hash, member.hasScript), + })); return ( @@ -63,7 +73,7 @@ export const GovernanceActionNewCommitteeDetailsTabContent = ({ whiteSpace: "nowrap", }} > - {t("govActions.membersToBeAdded")} + {t("govActions.membersToBeAddedToTheCommittee")} {membersToBeAdded.map(({ cip129Identifier }) => ( @@ -101,10 +111,10 @@ export const GovernanceActionNewCommitteeDetailsTabContent = ({ whiteSpace: "nowrap", }} > - {t("govActions.membersToBeRemoved")} + {t("govActions.membersToBeRemovedFromTheCommittee")} - {membersToBeRemoved.map((hash) => ( - + {membersToBeRemoved.map(({ cip129Identifier }) => ( + - {encodeCIP129Identifier({ - txID: hash, - bech32Prefix: "cc_cold", - })} + {cip129Identifier} - + ))} @@ -179,8 +180,8 @@ export const GovernanceActionNewCommitteeDetailsTabContent = ({ }} > {t("govActions.changeToTermsEpochs", { - epochTo: newExpirationEpoch, - epochFrom: expirationEpoch, + epochTo: newExpirationEpoch ?? "N/A", + epochFrom: expirationEpoch ?? "N/A", })} diff --git a/govtool/frontend/src/i18n/locales/en.json b/govtool/frontend/src/i18n/locales/en.json index a60dfb632..f8ebb5efd 100644 --- a/govtool/frontend/src/i18n/locales/en.json +++ b/govtool/frontend/src/i18n/locales/en.json @@ -479,7 +479,7 @@ "membersToBeRemovedFromTheCommittee": "Members to be removed from the Committee", "membersToBeAddedToTheCommittee": "Members to be added to the Committee", "changeToTermsOfExistingMembers": "Change to terms of existing members", - "changeToTermsEpochs": "To {{epochTo}} epoch {{epochFrom}} epoch", + "changeToTermsEpochs": "To {{epochTo}} epoch, from {{epochFrom}} epoch", "newThresholdValue": "New threshold value", "protocolParamsDetails": { "existing": "Existing",