Skip to content

Commit

Permalink
Merge branch 'develop' into cooler-loans-api-bigquery
Browse files Browse the repository at this point in the history
  • Loading branch information
0xJem committed Oct 15, 2024
2 parents 467a4ad + 284723e commit 89c45bf
Show file tree
Hide file tree
Showing 13 changed files with 341 additions and 116 deletions.
9 changes: 9 additions & 0 deletions src/helpers/environment/Environment/Environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ export class Environment {
}),
);

public static getGovernanceSubgraphUrl = (): string => {
const subgraphApiKey = this.getSubgraphApiKey();
return this._get({
first: true,
key: "VITE_GOVERNANCE_SUBGRAPH_URL",
fallback: `https://gateway.thegraph.com/api/${subgraphApiKey}/subgraphs/id/AQoLCXebY1Ga7DrqVaVQ85KMwS7iFof73tv9XMVGRtyJ`,
});
};

public static getNodeUrls = (networkId: NetworkId) => {
switch (networkId) {
case NetworkId.MAINNET:
Expand Down
5 changes: 3 additions & 2 deletions src/helpers/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,10 @@ export function shorten(str: string) {
}

export function formatCurrency(c: number, precision = 0, currency = "USD") {
console.log(c, precision, currency);
const formatted = new Intl.NumberFormat("en-US", {
style: currency === "USD" ? "currency" : undefined,
currency,
style: currency === "USD" || currency === "" ? "currency" : undefined,
currency: "USD",
maximumFractionDigits: precision,
minimumFractionDigits: precision,
}).format(c);
Expand Down
34 changes: 19 additions & 15 deletions src/views/Governance/Components/Status.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { Box, Typography } from "@mui/material";
import { Box, Tooltip, Typography } from "@mui/material";
import { Paper } from "@olympusdao/component-library";
import { useGetCanceledTime } from "src/views/Governance/hooks/useGetCanceledTime";
import { useGetExecutedTime } from "src/views/Governance/hooks/useGetExecutedTime";
import { useGetProposalDetails } from "src/views/Governance/hooks/useGetProposalDetails";
import { useGetProposal } from "src/views/Governance/hooks/useGetProposals";
import { useGetProposalFromSubgraph } from "src/views/Governance/hooks/useGetProposalFromSubgraph";
import { useGetQueuedTime } from "src/views/Governance/hooks/useGetQueuedTime";
import { useGetVetoedTime } from "src/views/Governance/hooks/useGetVetoedTime";

export const Status = ({ proposalId }: { proposalId: number }) => {
const { data: proposal } = useGetProposal({ proposalId });
const { data: proposal } = useGetProposalFromSubgraph({ proposalId: proposalId.toString() });
const { data: proposalDetails } = useGetProposalDetails({ proposalId });
const { data: queueTime } = useGetQueuedTime({ proposalId });
const { data: executedTime } = useGetExecutedTime({ proposalId, status: proposalDetails?.status });
Expand All @@ -29,19 +29,23 @@ export const Status = ({ proposalId }: { proposalId: number }) => {
<Typography fontSize="12px">{proposal?.createdAtBlock?.toLocaleString()}</Typography>
<Typography fontWeight="500">Published Onchain</Typography>
</div>
<div>
<Typography fontSize="12px">{proposalDetails?.startDate?.toLocaleString()}</Typography>
<Typography fontWeight="500">Voting Period Starts</Typography>
</div>
{(proposalDetails?.endDate || placeholderEndDate) && (
<Tooltip title={`Block ${proposalDetails?.startBlock}`}>
<div>
<Typography fontSize="12px">
{proposalDetails.endDate
? proposalDetails?.endDate?.toLocaleString()
: placeholderEndDate?.toLocaleString()}
</Typography>
<Typography fontWeight="500">Voting Period Ends</Typography>
<Typography fontSize="12px">{proposalDetails?.startDate?.toLocaleString()}</Typography>
<Typography fontWeight="500">Voting Period Starts</Typography>
</div>
</Tooltip>
{(proposalDetails?.endDate || placeholderEndDate) && (
<Tooltip title={`Block ${proposalDetails.endBlock}`}>
<div>
<Typography fontSize="12px">
{proposalDetails.endDate
? proposalDetails?.endDate?.toLocaleString()
: placeholderEndDate?.toLocaleString()}
</Typography>
<Typography fontWeight="500">Voting Period Ends </Typography>
</div>
</Tooltip>
)}
{proposalDetails?.status === "Queued" && (
<>
Expand All @@ -52,7 +56,7 @@ export const Status = ({ proposalId }: { proposalId: number }) => {
</div>
)}
<div>
<Typography fontSize="12px">{proposalDetails.eta.toLocaleString()}</Typography>
<Typography fontSize="12px">{proposalDetails.etaDate.toLocaleString()}</Typography>
<Typography fontWeight="500">Estimated Execution Time</Typography>
</div>
</>
Expand Down
104 changes: 104 additions & 0 deletions src/views/Governance/Proposals/VoteDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import {
Box,
Paper,
Tab,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Tabs,
Typography,
} from "@mui/material";
import React, { useState } from "react";
import { useParams } from "react-router-dom";
import { useGetVotes } from "src/views/Governance/hooks/useGetVotes";
import { VoteRow } from "src/views/Governance/Proposals/VoteRow";

// Data for the tables, including a 'reason' field for each row
const tablesData = [
{
id: "For",
contractValue: 1,
},
{
id: "Against",
contractValue: 0,
},
{
id: "Abstain",
contractValue: 2,
},
];

const TabPanel = (props: { children: React.ReactNode; value: number; index: number }) => {
const { children, value, index, ...other } = props;
return (
<div role="tabpanel" hidden={value !== index} id={`tabpanel-${index}`} aria-labelledby={`tab-${index}`} {...other}>
{value === index && <Box p={3}>{children}</Box>}
</div>
);
};

export default function GovernanceTable() {
const { id } = useParams();
const [tabIndex, setTabIndex] = useState(0);
const [supportValue, setSupportValue] = useState(1);
const { data: voteData } = useGetVotes({ proposalId: id, support: supportValue });

const handleTabChange = (event: React.SyntheticEvent, newIndex: number) => {
setSupportValue(tablesData[newIndex].contractValue);
setTabIndex(newIndex);
};

return (
<Box sx={{ width: "100%" }}>
<Tabs
textColor="primary"
aria-label="proposal tabs"
indicatorColor="primary"
value={tabIndex}
onChange={handleTabChange}
//hides the tab underline sliding animation in while <Zoom> is loading
TabIndicatorProps={{ style: { display: "none" } }}
centered
>
{" "}
{tablesData.map((table, index) => (
<Tab label={table.id} key={index} />
))}
</Tabs>

{tablesData.map((table, index) => (
<TabPanel value={tabIndex} index={index} key={index}>
<TableContainer component={Paper}>
<Table>
<TableHead>
<TableRow>
<TableCell>{tablesData[tabIndex].id}</TableCell>
<TableCell align="right">Votes</TableCell>
</TableRow>
</TableHead>
<TableBody>
{voteData?.length ? (
voteData.map((row, i) => (
<TableRow key={i}>
<VoteRow voter={row.voter} reason={row.reason} votes={row.votes} tx={row.transactionHash} />
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={2} align="center">
<Typography>No votes yet</Typography>
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</TableContainer>
</TabPanel>
))}
</Box>
);
}
37 changes: 37 additions & 0 deletions src/views/Governance/Proposals/VoteRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Box, Link, TableCell, Tooltip, Typography } from "@mui/material";
import { formatEther } from "ethers/lib/utils.js";
import { abbreviatedNumber } from "src/helpers";
import { truncateEthereumAddress } from "src/helpers/truncateAddress";
import { useEnsName } from "wagmi";

export const VoteRow = ({
voter,
reason,
votes,
tx,
}: {
voter: string;
reason?: string;
votes: string;
tx: string;
}) => {
const { data: ensName } = useEnsName({ address: voter as `0x${string}` });
return (
<>
<TableCell>
<Link href={`https://etherscan.io/tx/${tx}`} target="_blank" rel="noopener noreferrer">
<Tooltip title={voter}>
<Box>{ensName || truncateEthereumAddress(voter)}</Box>
</Tooltip>
</Link>
{/* Render the reason if provided, and style it as a comment */}
{reason && (
<Typography variant="body2" sx={{ color: "gray", fontStyle: "italic", mt: 1 }}>
"{reason}"
</Typography>
)}
</TableCell>
<TableCell align="right">{abbreviatedNumber.format(Number(formatEther(votes) || 0))} gOHM</TableCell>
</>
);
};
12 changes: 5 additions & 7 deletions src/views/Governance/Proposals/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@ import { useActivateProposal } from "src/views/Governance/hooks/useActivatePropo
import { useExecuteProposal } from "src/views/Governance/hooks/useExecuteProposal";
import { useGetCurrentBlockTime } from "src/views/Governance/hooks/useGetCurrentBlockTime";
import { useGetProposalDetails } from "src/views/Governance/hooks/useGetProposalDetails";
import { useGetProposal } from "src/views/Governance/hooks/useGetProposals";
import { useGetProposalFromSubgraph } from "src/views/Governance/hooks/useGetProposalFromSubgraph";
import { useQueueProposal } from "src/views/Governance/hooks/useQueueProposal";
import VoteDetails from "src/views/Governance/Proposals/VoteDetails";
import { useEnsName, useNetwork, useSwitchNetwork } from "wagmi";

export const ProposalPage = () => {
const { id } = useParams();
const { data: proposal } = useGetProposal({ proposalId: Number(id) });
const { data: proposal } = useGetProposalFromSubgraph({ proposalId: id });
const { data: proposalDetails } = useGetProposalDetails({ proposalId: Number(id) });
const { data: ensAddress } = useEnsName({ address: proposalDetails?.proposer as `0x${string}` });
const [voteModalOpen, setVoteModalOpen] = useState(false);
Expand Down Expand Up @@ -160,7 +161,7 @@ export const ProposalPage = () => {
>
<Tab label="Description" />
<Tab label="Executable Code" />
{/* <Tab label="Comments" /> */}
<Tab label="Participation" />
</Tabs>
</Box>
<Grid container spacing={"24px"}>
Expand Down Expand Up @@ -197,11 +198,8 @@ export const ProposalPage = () => {
)}
{tabIndex === 2 && (
<>
<Typography fontSize="21px" fontWeight={600} mb="15px">
Comments
</Typography>
<Box display="flex" flexDirection="column" gap="15px">
<Typography>No comments yet</Typography>
<VoteDetails />
</Box>
</>
)}
Expand Down
20 changes: 20 additions & 0 deletions src/views/Governance/helpers/normalizeProposal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Proposal } from "src/views/Governance/hooks/useGetProposalFromSubgraph";

// Normalizes the proposal data to match the onchain format
export const normalizeProposal = (proposal: Proposal) => {
return {
createdAtBlock: new Date(Number(proposal.blockTimestamp) * 1000),
details: {
id: proposal.proposalId,
proposer: proposal.proposer,
targets: proposal.targets,
values: proposal.values,
signatures: proposal.signatures,
calldatas: proposal.calldatas,
startBlock: proposal.startBlock,
description: proposal.description,
},
title: proposal.description.split(/#+\s|\n/g)[1] || `${proposal.description.slice(0, 20)}...`,
txHash: proposal.transactionHash,
};
};
4 changes: 2 additions & 2 deletions src/views/Governance/hooks/useGetProposalDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ export const useGetProposalDetails = ({ proposalId }: { proposalId: number }) =>
const blockTime = await archiveProvider.getBlock("latest");
const endDateBlockTimestamp = await archiveProvider.getBlock(Number(proposalDetails.endBlock));
const startDateBlockTimestamp = await archiveProvider.getBlock(Number(proposalDetails.startBlock));
const startDate = getDateFromBlock(Number(proposalDetails.startBlock), blockTime.number, 15, blockTime.timestamp);
const endDate = getDateFromBlock(Number(proposalDetails.endBlock), blockTime.number, 15, blockTime.timestamp);
const startDate = getDateFromBlock(Number(proposalDetails.startBlock), blockTime.number, 12, blockTime.timestamp);
const endDate = getDateFromBlock(Number(proposalDetails.endBlock), blockTime.number, 12, blockTime.timestamp);

return {
id: proposalDetails.id.toNumber(),
Expand Down
60 changes: 60 additions & 0 deletions src/views/Governance/hooks/useGetProposalFromSubgraph.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { useQuery } from "@tanstack/react-query";
import request, { gql } from "graphql-request";
import { Environment } from "src/helpers/environment/Environment/Environment";
import { normalizeProposal } from "src/views/Governance/helpers/normalizeProposal";

export type Proposal = {
proposalId: string;
proposer: string;
targets: string[];
signatures: string[];
calldatas: string[];
transactionHash: string;
description: string;
blockTimestamp: string;
blockNumber: string;
startBlock: string;
values: string[];
};

type ProposalResponse = {
proposalCreated: Proposal;
};

export const useGetProposalFromSubgraph = ({ proposalId }: { proposalId?: string }) => {
const query = gql`
query {
proposalCreated(id: ${proposalId}) {
proposalId
proposer
targets
signatures
calldatas
transactionHash
description
blockTimestamp
blockNumber
startBlock
values
}
}
`;

return useQuery(
["getProposal", proposalId],
async () => {
try {
const subgraphUrl = Environment.getGovernanceSubgraphUrl();
const response = await request<ProposalResponse>(subgraphUrl, query);
if (!response.proposalCreated) {
return null;
}
return normalizeProposal(response.proposalCreated);
} catch (error) {
console.error("useGetProposalFromSubgraph", error);
return null;
}
},
{ enabled: !!proposalId },
);
};
Loading

0 comments on commit 89c45bf

Please sign in to comment.