@@ -108,22 +115,6 @@ function ProposalList() {
);
}
-function ProposalCard({ data }: { data: MergedProposalData }) {
- const { stage, proposal, metadata } = data;
-
- const title =
- metadata?.title || metadata?.cgp ? `Proposal CGP-${metadata.cgp}` : `Proposal #${proposal?.id}`;
-
- return (
-
@@ -182,11 +173,11 @@ function useFilteredProposals({
.filter(
(p) =>
!query ||
- p.proposal?.proposer.includes(query) ||
- p.proposal?.url.includes(query) ||
- p.metadata?.title.includes(query) ||
- p.metadata?.author.includes(query) ||
- p.metadata?.url?.includes(query),
+ p.proposal?.proposer.toLowerCase().includes(query) ||
+ p.proposal?.url.toLowerCase().includes(query) ||
+ p.metadata?.title.toLowerCase().includes(query) ||
+ p.metadata?.author.toLowerCase().includes(query) ||
+ p.metadata?.url?.toLowerCase().includes(query),
);
}, [proposals, filter, searchQuery]);
}
diff --git a/src/config/consts.ts b/src/config/consts.ts
index 20c7b1b..ee70f9b 100644
--- a/src/config/consts.ts
+++ b/src/config/consts.ts
@@ -1,13 +1,20 @@
export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
export const DEFAULT_DISPLAY_DECIMALS = 2;
export const DEFAULT_TOKEN_DECIMALS = 18;
-export const MIN_REMAINING_BALANCE = 10000000000000000n; // 0.01 CELO
export const AVG_BLOCK_TIMES_MS = 5_000; // 5 seconds
export const EPOCH_DURATION_MS = 86_400_000; // 1 day
export const BALANCE_REFRESH_INTERVAL = 5_000; // 5 seconds
+
+// Locking
+export const MIN_REMAINING_BALANCE = 10000000000000000n; // 0.01 CELO
+
+// Staking
export const MIN_GROUP_SCORE_FOR_RANDOM = 90;
export const MIN_INCREMENTAL_VOTE_AMOUNT = 10000000000000000n; // 0.01 CELO
-
-// From the Election contract electableValidators config
export const MAX_NUM_ELECTABLE_VALIDATORS = 110;
export const MAX_NUM_GROUPS_VOTED_FOR = 10;
+
+// Governance
+export const QUEUED_STAGE_EXPIRY_TIME = 2_419_200_000; // 4 weeks
+export const APPROVAL_STAGE_EXPIRY_TIME = 86_400_000; // 1 day
+export const EXECUTION_STAGE_EXPIRY_TIME = 259_200_000; // 3 days
diff --git a/src/features/governance/ProposalCard.tsx b/src/features/governance/ProposalCard.tsx
new file mode 100644
index 0000000..19d06ee
--- /dev/null
+++ b/src/features/governance/ProposalCard.tsx
@@ -0,0 +1,49 @@
+import Image from 'next/image';
+import { StageBadge } from 'src/features/governance/StageBadge';
+import { MergedProposalData } from 'src/features/governance/useGovernanceProposals';
+import ClockIcon from 'src/images/icons/clock.svg';
+import { trimToLength } from 'src/utils/strings';
+import { getHumanReadableTimeString } from 'src/utils/time';
+
+export function ProposalCard({ data }: { data: MergedProposalData }) {
+ const { stage, proposal, metadata } = data;
+
+ const { id, timestamp, expiryTimestamp, votes } = proposal || {};
+ const { title, timestamp: cgpTimestamp, timestampExecuted, cgp } = metadata || {};
+
+ const idValue = id ? `# ${id}` : cgp ? `CGP ${cgp}` : undefined;
+ const titleValue = title ? trimToLength(title, 50) : undefined;
+ const proposedTimestamp = timestamp || cgpTimestamp;
+ const proposedTimeValue = proposedTimestamp
+ ? new Date(proposedTimestamp).toLocaleDateString()
+ : undefined;
+ const endTimestamp = timestampExecuted || expiryTimestamp;
+ const endTimeValue = endTimestamp ? getHumanReadableTimeString(endTimestamp) : undefined;
+ const endTimeLabel = timestampExecuted ? 'Executed' : 'Expires';
+
+ return (
+
+
+ {idValue && (
+
+ {idValue}
+
+ )}
+
+ {proposedTimeValue && (
+
{`Proposed ${proposedTimeValue}`}
+ )}
+
+ {titleValue &&
{titleValue}
}
+ {votes && (
+
{`Votes: ${votes.yes} Yes, ${votes.no} No, ${votes.abstain} Abstain`}
+ )}
+ {endTimeValue && (
+
+
+
{`${endTimeLabel} ${endTimeValue}`}
+
+ )}
+
+ );
+}
diff --git a/src/features/governance/StageBadge.tsx b/src/features/governance/StageBadge.tsx
new file mode 100644
index 0000000..8770960
--- /dev/null
+++ b/src/features/governance/StageBadge.tsx
@@ -0,0 +1,14 @@
+import clsx from 'clsx';
+import { ProposalStage, ProposalStageToStyle } from 'src/features/governance/contractTypes';
+
+export function StageBadge({ stage, className }: { stage: ProposalStage; className?: string }) {
+ const { color, label } = ProposalStageToStyle[stage];
+ return (
+
+ {label}
+
+ );
+}
diff --git a/src/features/governance/contractTypes.ts b/src/features/governance/contractTypes.ts
index ae37387..72800b2 100644
--- a/src/features/governance/contractTypes.ts
+++ b/src/features/governance/contractTypes.ts
@@ -1,3 +1,5 @@
+import { Color } from 'src/styles/Color';
+
export enum VoteValue {
None = 'none',
Abstain = 'abstain',
@@ -22,6 +24,18 @@ export enum ProposalStage {
Rejected = 8,
}
+export const ProposalStageToStyle: Record
= {
+ [ProposalStage.None]: { color: Color.Sky, label: 'Draft' },
+ [ProposalStage.Queued]: { color: Color.Lavender, label: 'Upvoting' },
+ [ProposalStage.Approval]: { color: Color.Lavender, label: 'Approval' },
+ [ProposalStage.Referendum]: { color: Color.Jade, label: 'Voting' },
+ [ProposalStage.Execution]: { color: Color.Jade, label: 'Passed' },
+ [ProposalStage.Expiration]: { color: Color.Red, label: 'Expired' },
+ [ProposalStage.Executed]: { color: Color.Jade, label: 'Executed' },
+ [ProposalStage.Withdrawn]: { color: Color.Red, label: 'Withdrawn' },
+ [ProposalStage.Rejected]: { color: Color.Red, label: 'Rejected' },
+};
+
export const FAILED_PROPOSAL_STAGES = [
ProposalStage.Expiration,
ProposalStage.Rejected,
@@ -31,6 +45,7 @@ export const FAILED_PROPOSAL_STAGES = [
export interface Proposal {
id: number;
timestamp: number;
+ expiryTimestamp?: number;
url: string;
proposer: Address;
deposit: bigint;
diff --git a/src/features/governance/useGovernanceProposals.ts b/src/features/governance/useGovernanceProposals.ts
index a839e58..11e8976 100644
--- a/src/features/governance/useGovernanceProposals.ts
+++ b/src/features/governance/useGovernanceProposals.ts
@@ -142,6 +142,11 @@ async function fetchGovernanceProposals(publicClient: PublicClient): Promise
+
+
+
diff --git a/src/styles/Color.ts b/src/styles/Color.ts
index c97ab09..cb9e2dc 100644
--- a/src/styles/Color.ts
+++ b/src/styles/Color.ts
@@ -7,6 +7,7 @@ export enum Color {
// Accents
Yellow = '#FCFF52',
+ Red = '#F68098',
Gypsum = '#FCF6F1',
Sand = '#E7E3D4',
Wood = '#655947',