Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/dev' into frontend-passport-inte…
Browse files Browse the repository at this point in the history
…gration
  • Loading branch information
Lucianosc committed Aug 2, 2024
2 parents 429e670 + f1d8c2f commit 58a7ef6
Show file tree
Hide file tree
Showing 97 changed files with 174,381 additions and 4,473 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ jobs:
SCORER_ID: ${{ secrets.SCORER_ID }}
GITCOIN_PASSPORT_API_KEY: ${{ secrets.GITCOIN_PASSPORT_API_KEY }}
LIST_MANAGER_PRIVATE_KEY: ${{ secrets.LIST_MANAGER_PRIVATE_KEY }}
FOUNDRY_OUT: pkg/contracts/out

steps:
- name: Check out code
Expand Down
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>
);
}

21 changes: 8 additions & 13 deletions apps/web/app/(app)/gardens/[chain]/[garden]/[community]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import React, { useEffect, useState } from "react";
import {
CurrencyDollarIcon,
ExclamationCircleIcon,
PlusIcon,
RectangleGroupIcon,
} from "@heroicons/react/24/outline";
Expand All @@ -24,6 +23,7 @@ import {
PoolCard,
RegisterMember,
Statistic,
InfoIcon,
} from "@/components";
import { LoadingSpinner } from "@/components/LoadingSpinner";
import { TokenGardenFaucet } from "@/components/TokenGardenFaucet";
Expand Down Expand Up @@ -214,23 +214,18 @@ export default function Page({
</Statistic>
<div className="flex">
<p className="font-medium">Registration cost:</p>
<div
className="tooltip ml-2 flex cursor-pointer items-center text-primary-content"
data-tip={`Registration amount: ${parseToken(registrationAmount)} ${tokenGarden.symbol}\nCommunity fee: ${parseToken(parsedCommunityFee())} ${tokenGarden.symbol}`}
>
<InfoIcon content={`Registration amount: ${parseToken(registrationAmount)} ${tokenGarden.symbol}\nCommunity fee: ${parseToken(parsedCommunityFee())} ${tokenGarden.symbol}`}>
<DisplayNumber
number={[getTotalRegistrationCost(), tokenGarden?.decimals]}
className="font-semibold"
number={[
getTotalRegistrationCost(),
tokenGarden?.decimals,
]}
className="font-semibold text-primary-content"
disableTooltip={true}
compact={true}
tokenSymbol={tokenGarden.symbol}
/>
<ExclamationCircleIcon
className="ml-2 stroke-2"
width={22}
height={22}
/>
</div>
</InfoIcon>
</div>
</div>
</div>
Expand Down
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
Binary file added apps/web/app/favicon.ico
Binary file not shown.
Loading

0 comments on commit 58a7ef6

Please sign in to comment.