Skip to content

Commit

Permalink
Merge pull request #274 from 1Hive/upgrade-proposal-component-identicon
Browse files Browse the repository at this point in the history
Proposal component with identicon and stats
  • Loading branch information
Mati0x authored Jul 29, 2024
2 parents ec6a90f + 853036d commit 733909e
Show file tree
Hide file tree
Showing 22 changed files with 3,602 additions and 3,272 deletions.
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
"use client";

import { useEffect, useState } from "react";
import { Hashicon } from "@emeraldpay/hashicon-react";
import { InformationCircleIcon, UserIcon } from "@heroicons/react/24/outline";
import Image from "next/image";
import { Address, formatUnits } from "viem";
import { useContractRead } from "wagmi";
import { toast } from "react-toastify";
import { Address, encodeAbiParameters, formatUnits } from "viem";
import {
getProposalDataDocument,
getProposalDataQuery,
} from "#/subgraph/.graphclient";
import { proposalImg } from "@/assets";
import { Badge, DisplayNumber, EthAddress, Statistic } from "@/components";
import { Badge, Button, DisplayNumber, EthAddress, Statistic } from "@/components";
import { ConvictionBarChart } from "@/components/Charts/ConvictionBarChart";
import { LoadingSpinner } from "@/components/LoadingSpinner";
import { usePubSubContext } from "@/contexts/pubsub.context";
import { useChainIdFromPath } from "@/hooks/useChainIdFromPath";
import { useContractWriteWithConfirmations } from "@/hooks/useContractWriteWithConfirmations";
import { useConvictionRead } from "@/hooks/useConvictionRead";
import { useSubgraphQuery } from "@/hooks/useSubgraphQuery";
import { cvStrategyABI } from "@/src/generated";
import { alloABI } from "@/src/generated";
import { poolTypes, proposalStatus } from "@/types";
import { abiWithErrors } from "@/utils/abiWithErrors";
import { useErrorDetails } from "@/utils/getErrorName";
import { getIpfsMetadata } from "@/utils/ipfsUtils";
import { calculatePercentageBigInt } from "@/utils/numbers";
import { logOnce } from "@/utils/log";

const prettyTimestamp = (timestamp: number) => {
const date = new Date(timestamp * 1000);
Expand All @@ -30,7 +34,7 @@ const prettyTimestamp = (timestamp: number) => {
};

export default function Page({
params: { proposalId, garden },
params: { proposalId, garden, poolId },
}: {
params: {
proposalId: string;
Expand All @@ -39,7 +43,6 @@ export default function Page({
garden: string;
};
}) {
// TODO: fetch garden decimals in query
const { data } = useSubgraphQuery<getProposalDataQuery>({
query: getProposalDataDocument,
variables: {
Expand All @@ -57,6 +60,11 @@ export default function Page({

const metadata = proposalData?.metadata;

const proposalIdNumber = proposalData?.proposalNumber as number;

const { publish } = usePubSubContext();
const chainId = useChainIdFromPath();

const [ipfsResult, setIpfsResult] =
useState<Awaited<ReturnType<typeof getIpfsMetadata>>>();

Expand All @@ -68,85 +76,76 @@ export default function Page({
}
}, [metadata]);

const cvStrategyContract = {
address: proposalData?.strategy.id as Address,
abi: cvStrategyABI,
};

const proposalIdNumber = proposalData?.proposalNumber;

const { data: thFromContract } = useContractRead({
...cvStrategyContract,
functionName: "calculateThreshold",
args: [proposalIdNumber],
enabled: !!proposalIdNumber,
});

const { data: totalEffectiveActivePoints } = useContractRead({
...cvStrategyContract,
functionName: "totalEffectiveActivePoints",
});

const { data: stakeAmountFromContract } = useContractRead({
...cvStrategyContract,
functionName: "getProposalStakedAmount",
args: [proposalIdNumber],
enabled: !!proposalIdNumber,
});

const isProposalEnded =
!!proposalData &&
(proposalStatus[proposalData.proposalStatus] !== "executed" ||
proposalStatus[proposalData.proposalStatus] !== "cancelled");

const { data: updateConvictionLast } = useContractRead({
...cvStrategyContract,
functionName: "updateProposalConviction" as any, // TODO: fix CVStrategy.updateProposalConviction to view in contract
args: [proposalIdNumber],
enabled: !!proposalIdNumber,
}) as { data: bigint | undefined };
(proposalStatus[proposalData.proposalStatus] === "executed" ||
proposalStatus[proposalData.proposalStatus] === "cancelled");
logOnce("debug", { isProposalEnded, proposalStatus: proposalStatus[proposalData?.proposalStatus] });

const { data: maxCVSupply } = useContractRead({
...cvStrategyContract,
functionName: "getMaxConviction",
args: [totalEffectiveActivePoints ?? 0n],
enabled: !!totalEffectiveActivePoints,
const { currentConvictionPct, thresholdPct, totalSupportPct, updateConvictionLast } = useConvictionRead({
proposalData,
tokenData: data?.tokenGarden,
});

const tokenSymbol = data?.tokenGarden?.symbol;
const tokenDecimals = data?.tokenGarden?.decimals;
const convictionLast = proposalData?.convictionLast;
const threshold = proposalData?.threshold;
const proposalType = proposalData?.strategy.config?.proposalType;
const requestedAmount = proposalData?.requestedAmount;
const beneficiary = proposalData?.beneficiary as Address | undefined;
const submitter = proposalData?.submitter as Address | undefined;
const status = proposalData?.proposalStatus;
const stakedAmount = proposalData?.stakedAmount;

useEffect(() => {
if (!proposalData) {
const isSignalingType = poolTypes[proposalType] === "signaling";

//encode proposal id to pass as argument to distribute function
const encodedDataProposalId = (proposalId_: number) => {
if (!proposalId_) {
return;
}
const encodedProposalId = encodeAbiParameters(
[{ name: "proposalId", type: "uint" }],
[BigInt(proposalId_)],
);
return encodedProposalId;
};

console.debug({
requestedAmount,
maxCVSupply,
threshold,
thFromContract,
stakedAmount,
stakeAmountFromContract: stakeAmountFromContract,
totalEffectiveActivePoints,
updateConvictionLast,
convictionLast,
});
}, [proposalData]);
//distribution function from Allo contract
//args: poolId, strategyId, encoded proposalId
const {
write: writeDistribute,
error: errorDistribute,
isError: isErrorDistribute,
} = useContractWriteWithConfirmations({
address: data?.allos[0]?.id as Address,
abi: abiWithErrors(alloABI),
functionName: "distribute",
contractName: "Allo",
fallbackErrorMessage: "Error executing proposal. Please try again.",
onConfirmations: () => {
publish({
topic: "proposal",
type: "update",
function: "distribute",
id:proposalId,
containerId: data?.cvproposal?.strategy?.id,
chainId,
});
},
});

const distributeErrorName = useErrorDetails(errorDistribute);
useEffect(() => {
if (isErrorDistribute && distributeErrorName.errorName !== undefined) {
toast.error("NOT EXECUTABLE:" + " " + distributeErrorName.errorName);
}
}, [isErrorDistribute]);

if (
!proposalData ||
!ipfsResult ||
!maxCVSupply ||
!totalEffectiveActivePoints ||
currentConvictionPct == null ||
thresholdPct == null ||
totalSupportPct == null ||
!proposalIdNumber ||
(updateConvictionLast == null && !isProposalEnded)
) {
return (
Expand All @@ -156,43 +155,11 @@ export default function Page({
);
}

const isSignalingType = poolTypes[proposalType] === "signaling";

let thresholdPct = calculatePercentageBigInt(
threshold,
maxCVSupply,
tokenDecimals,
);

let totalSupportPct = calculatePercentageBigInt(
stakedAmount,
totalEffectiveActivePoints,
tokenDecimals,
);

let currentConvictionPct = calculatePercentageBigInt(
BigInt(updateConvictionLast ?? 0),
maxCVSupply,
tokenDecimals,
);

console.debug({
thresholdPct,
totalSupportPct,
currentConvictionPct,
});

return (
<div className="page-layout">
<header className="section-layout flex flex-col items-start gap-10 sm:flex-row">
<div className="flex w-full items-center justify-center sm:w-auto">
<Image
src={proposalImg}
alt={`proposal image ${proposalIdNumber}`}
height={160}
width={160}
className="min-h-[160px] min-w-[160px]"
/>
<Hashicon value={proposalId} size={90} />
</div>
<div className="flex w-full flex-col gap-8">
<div>
Expand Down Expand Up @@ -244,15 +211,21 @@ export default function Page({
Proposal passed and executed successfully
</div>
</div>
: <ConvictionBarChart
currentConvictionPct={currentConvictionPct}
thresholdPct={thresholdPct}
proposalSupportPct={totalSupportPct}
isSignalingType={isSignalingType}
proposalId={proposalIdNumber}
/>
}
: (
<ConvictionBarChart
currentConvictionPct={currentConvictionPct}
thresholdPct={thresholdPct}
proposalSupportPct={totalSupportPct}
isSignalingType={isSignalingType}
proposalId={proposalIdNumber}
/>
)}
<div className="absolute top-8 right-10">
{proposalStatus[status] !== "executed" && !isSignalingType && ( <Button onClick={() => writeDistribute?.({ args:[poolId, [data.cvproposal?.strategy.id], encodedDataProposalId(Number(proposalIdNumber))] })} disabled={currentConvictionPct < thresholdPct} tooltip="Proposal not executable">Execute</Button>)}

</div>
</section>
</div>
);
}

1 change: 0 additions & 1 deletion apps/web/app/api/passport-oracle/writeScore/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import { CV_PERCENTAGE_SCALE } from "@/utils/numbers";
import { getViemChain } from "@/utils/viem";

const LIST_MANAGER_PRIVATE_KEY = process.env.LIST_MANAGER_PRIVATE_KEY;

const CHAIN_ID = process.env.CHAIN_ID ? parseInt(process.env.CHAIN_ID) : 1337;
const LOCAL_RPC = "http://127.0.0.1:8545";

Expand Down
2 changes: 1 addition & 1 deletion apps/web/components/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const Card = ({ children, href, className }: CardProps) => {
//todo: div or section ?
<Link href={href}>
<div
className={`border1 group relative w-[313px] cursor-pointer rounded-2xl bg-neutral p-6 transition-all duration-200 ease-in-out hover:border-secondary-content hover:bg-secondary-soft ${className}`}
className={`border1 group relative cursor-pointer rounded-2xl bg-neutral p-6 transition-all duration-200 ease-in-out hover:border-secondary-content hover:bg-secondary-soft ${className}`}
>
{children}
</div>
Expand Down
2 changes: 1 addition & 1 deletion apps/web/components/Charts/ChartWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const ChartWrapper = ({

return (
<>
<div className="mt-7 flex flex-col gap-6">
<div className="mt-7 flex flex-col gap-12">
<h3>Conviction voting chart</h3>
<div className="flex gap-4">
{legend
Expand Down
16 changes: 11 additions & 5 deletions apps/web/components/Charts/ConvictionBarChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type ConvictionBarChartProps = {
thresholdPct: number;
proposalSupportPct: number;
isSignalingType: boolean;
proposalId: string;
proposalId: number;
compact?: boolean;
};

Expand Down Expand Up @@ -189,7 +189,7 @@ export const ConvictionBarChart = ({
};

const markLineTh: MarkLineComponentOption =
isSignalingType || compact ?
isSignalingType ?
{}
: {
...markLine,
Expand All @@ -203,7 +203,7 @@ export const ConvictionBarChart = ({
},
],
lineStyle: {
width: 1,
width: compact ? 0.5 : 1,
color: "#191919",
dashOffset: 30,
},
Expand Down Expand Up @@ -248,8 +248,9 @@ export const ConvictionBarChart = ({
bottom: compact ? "0%" : "25%",
containLabel: false,
},

animationDurationUpdate: 1200,
// TODO: realted to re render in PoolId page (check)
animation: false,
//animationDurationUpdate: 1200,
barGap: "-100%",
series: [
{
Expand All @@ -259,6 +260,11 @@ export const ConvictionBarChart = ({
color: "#A8E066",
borderRadius: [20, 20, 20, 20],
},
showBackground: true,
backgroundStyle:{
color: "#F0F0F0",
borderRadius: [20, 20, 20, 20],
},
label: {
show: !compact ?? false,
position: "insideRight",
Expand Down
1 change: 1 addition & 0 deletions apps/web/components/PoolGovernance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export const PoolGovernance = ({

const showPoolGovernanceData =
isMember && isMemberActivated !== undefined && isMemberActivated;

return (
<section className="section-layout">
<header>
Expand Down
Loading

0 comments on commit 733909e

Please sign in to comment.