Skip to content

Commit

Permalink
[1897] Refactor useAdvancedVoting (#228)
Browse files Browse the repository at this point in the history
* refactor use advanced voting

* fix prettier

* Revert "fix prettier"

This reverts commit e8e440f.

* fix prettier only in src

* second try prettier, had different local version

* minor comment
  • Loading branch information
ferrodri authored Apr 11, 2024
1 parent da09486 commit 0315f7b
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 121 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"generate-typechain": "typechain --target ethers-v6 --out-dir src/lib/contracts/generated src/lib/contracts/abis/*.json",
"typecheck": "tsc --pretty --noEmit",
"generate-apikey": "ts-node src/scripts/generateApiKey.ts",
"check-prettier": "prettier --check src/"
"check-prettier": "prettier --check src/",
"prettier-src": "prettier -w src/"
},
"dependencies": {
"@aws-sdk/client-dynamodb": "^3.454.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ export function ApprovalCastVoteDialog({
const { isLoading, isSuccess, write, isError, data } = useAdvancedVoting({
proposalId: proposal.id,
support: abstain ? 2 : 1,
standardVP: BigInt(votingPower.directVP),
advancedVP: BigInt(votingPower.advancedVP),
authorityChains,
reason,
Expand All @@ -93,17 +92,13 @@ export function ApprovalCastVoteDialog({
useEffect(() => {
if (
missingVote == "BOTH" &&
data?.standardVoteData &&
!data?.advancedVoteData
data?.standardTxHash &&
!data?.advancedTxHash
) {
setLocalMissingVote("ADVANCED");
}
}, [data, missingVote]);
if (
missingVote === "BOTH" &&
!data.advancedVoteData &&
data.standardVoteData
) {
if (missingVote === "BOTH" && !data.advancedTxHash && data.standardTxHash) {
return (
<VStack gap={3}>
<VStack className={styles.title_box}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ function CastVoteDialogContents({
const { write, isLoading, isSuccess, data } = useAdvancedVoting({
proposalId,
support: ["AGAINST", "FOR", "ABSTAIN"].indexOf(supportType),
standardVP: BigInt(votingPower.directVP),
advancedVP: BigInt(votingPower.advancedVP),
authorityChains,
reason,
Expand All @@ -52,8 +51,8 @@ function CastVoteDialogContents({
useEffect(() => {
if (
missingVote == "BOTH" &&
data?.standardVoteData &&
!data?.advancedVoteData
data?.standardTxHash &&
!data?.advancedTxHash
) {
setLocalMissingVote("ADVANCED");
}
Expand All @@ -64,11 +63,11 @@ function CastVoteDialogContents({
return null;
}

if (
missingVote === "BOTH" &&
!data.advancedVoteData &&
data.standardVoteData
) {
if (isLoading) {
return <LoadingVote />;
}

if (missingVote === "BOTH" && !data.advancedTxHash && data.standardTxHash) {
return (
<VStack gap={4} className={styles.dialog_container}>
<HStack justifyContent="justify-between">
Expand Down Expand Up @@ -105,7 +104,7 @@ function CastVoteDialogContents({

return (
<>
{!isLoading && !isSuccess && (
{!isSuccess && (
<VStack gap={4} className={styles.dialog_container}>
<HStack justifyContent="justify-between">
<VStack>
Expand Down Expand Up @@ -146,10 +145,7 @@ function CastVoteDialogContents({
{missingVote === "BOTH" && <AdvancedVoteAlert />}
</VStack>
)}
{isLoading && <LoadingVote />}
{isSuccess && data && (
<SuccessMessage closeDialog={closeDialog} data={data} />
)}
{isSuccess && <SuccessMessage closeDialog={closeDialog} data={data} />}
</>
);
}
Expand Down Expand Up @@ -184,8 +180,8 @@ export function SuccessMessage({
}: {
closeDialog: () => void;
data: {
advancedVoteData: { hash: string } | undefined;
standardVoteData: { hash: string } | undefined;
standardTxHash: string | undefined;
advancedTxHash: string | undefined;
};
}) {
return (
Expand All @@ -210,8 +206,8 @@ export function SuccessMessage({
</div>
</div>
<BlockScanUrls
hash1={data.standardVoteData?.hash}
hash2={data.advancedVoteData?.hash}
hash1={data?.standardTxHash}
hash2={data?.advancedTxHash}
/>
</VStack>
);
Expand Down
225 changes: 130 additions & 95 deletions src/hooks/useAdvancedVoting.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { MissingVote } from "@/lib/voteUtils";
import { useCallback, useEffect, useState } from "react";
import { useCallback, useState } from "react";
import { useContractWrite } from "wagmi";
import { track } from "@vercel/analytics";
import { optimism } from "viem/chains";
import Tenant from "@/lib/tenant/tenant";
import { waitForTransaction } from "wagmi/actions";

const useAdvancedVoting = ({
proposalId,
support,
standardVP,
advancedVP,
authorityChains,
reason = "",
Expand All @@ -17,69 +17,103 @@ const useAdvancedVoting = ({
}: {
proposalId: string;
support: number;
standardVP: bigint;
advancedVP: bigint;
authorityChains: string[][];
reason?: string;
params?: `0x${string}`;
missingVote: MissingVote;
}) => {
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
const [isSuccess, setIsSuccess] = useState(false);
const { contracts } = Tenant.current();
const {
write: advancedVote,
isLoading: advancedVoteIsLoading,
isError: advancedVoteIsError,
isSuccess: advancedVoteIsSuccess,
data: advancedVoteData,
} = useContractWrite({
address: contracts.alligator!.address as `0x${string}`,
abi: contracts.alligator!.abi,
functionName: "limitedCastVoteWithReasonAndParamsBatched",
args: [
advancedVP,
authorityChains as any,
BigInt(proposalId),
support,
reason,
params ?? "0x",
],
chainId: optimism.id,
});
const { writeAsync: advancedVote, isError: _advancedVoteError } =
useContractWrite({
address: contracts.alligator!.address as `0x${string}`,
abi: contracts.alligator!.abi,
functionName: "limitedCastVoteWithReasonAndParamsBatched",
args: [
advancedVP,
authorityChains as any,
BigInt(proposalId),
support,
reason,
params ?? "0x",
],
chainId: optimism.id,
});

const {
write: standardVote,
isLoading: standardVoteIsLoading,
isError: standardVoteIsError,
isSuccess: standardVoteIsSuccess,
data: standardVoteData,
} = useContractWrite({
address: contracts.governor.address as `0x${string}`,
abi: contracts.governor.abi,
functionName: reason
? params
? "castVoteWithReasonAndParams"
: "castVoteWithReason"
: params
? "castVoteWithReasonAndParams"
: "castVote",
args: reason
? params
? [BigInt(proposalId), support, reason, params]
: [BigInt(proposalId), support, reason]
: params
? [BigInt(proposalId), support, reason, params]
: ([BigInt(proposalId), support] as any),
chainId: optimism.id,
});
const { writeAsync: standardVote, isError: _standardVoteError } =
useContractWrite({
address: contracts.governor.address as `0x${string}`,
abi: contracts.governor.abi,
functionName: reason
? params
? "castVoteWithReasonAndParams"
: "castVoteWithReason"
: params
? "castVoteWithReasonAndParams"
: "castVote",
args: reason
? params
? [BigInt(proposalId), support, reason, params]
: [BigInt(proposalId), support, reason]
: params
? [BigInt(proposalId), support, reason, params]
: ([BigInt(proposalId), support] as any),
chainId: optimism.id,
});
const [standardVoteError, setStandardVoteError] =
useState(_standardVoteError);
const [advancedVoteError, setAdvancedVoteError] =
useState(_advancedVoteError);
const [standardVoteLoading, setStandardVoteLoading] = useState(false);
const [advancedVoteLoading, setAdvancedVoteLoading] = useState(false);
const [standardVoteSuccess, setStandardVoteSuccess] = useState(false);
const [advancedVoteSuccess, setAdvancedVoteSuccess] = useState(false);
const [standardTxHash, setStandardTxHash] = useState<string | undefined>(
undefined
);
const [advancedTxHash, setAdvancedTxHash] = useState<string | undefined>(
undefined
);

const write = useCallback(() => {
const vote = async () => {
setIsError(false);
setIsSuccess(false);
const _standardVote = async () => {
setStandardVoteLoading(true);
const directTx = await standardVote();
try {
const { status } = await waitForTransaction({
hash: directTx.hash,
});
if (status === "success") {
setStandardTxHash(directTx.hash);
setStandardVoteSuccess(true);
}
} catch (error) {
console.error(error);
setStandardVoteError(true);
} finally {
setStandardVoteLoading(false);
}
};

const _advancedVote = async () => {
setAdvancedVoteLoading(true);
const advancedTx = await advancedVote();
try {
const { status } = await waitForTransaction({
hash: advancedTx.hash,
});
if (status === "success") {
setAdvancedTxHash(advancedTx.hash);
setAdvancedVoteSuccess(true);
}
} catch (error) {
console.error(error);
setAdvancedVoteError(true);
} finally {
setAdvancedVoteLoading(false);
}
};
const vote = async () => {
const trackingData: any = {
dao_slug: "OP",
proposal_id: BigInt(proposalId),
Expand All @@ -97,64 +131,65 @@ const useAdvancedVoting = ({
switch (missingVote) {
case "DIRECT":
track("Standard Vote", trackingData);
standardVote();
await _standardVote();
break;

case "ADVANCED":
track("Advanced Vote", trackingData);
advancedVote();
await _advancedVote();
break;

case "BOTH":
track("Standard + Advanced Vote", trackingData);
standardVote();
advancedVote();
await _standardVote();
await _advancedVote();
break;
}
};

vote();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [standardVote, advancedVote, missingVote]);

useEffect(() => {
if (advancedVoteIsLoading || standardVoteIsLoading) {
setIsLoading(true);
}
if (advancedVoteIsError || standardVoteIsError) {
setIsError(true);
setIsLoading(false);
}
switch (missingVote) {
case "BOTH":
if (advancedVoteIsSuccess && standardVoteIsSuccess) {
setIsSuccess(true);
setIsLoading(false);
}
break;
default:
if (advancedVoteIsSuccess || standardVoteIsSuccess) {
setIsSuccess(true);
setIsLoading(false);
}
break;
}
}, [
advancedVoteIsLoading,
standardVoteIsLoading,
advancedVoteIsError,
standardVoteIsError,
advancedVoteIsSuccess,
standardVoteIsSuccess,
authorityChains,
standardVP,
standardVote,
advancedVote,
missingVote,
params,
proposalId,
reason,
support,
]);

return {
isLoading,
isError,
isSuccess,
isLoading:
missingVote === "DIRECT"
? standardVoteLoading
: missingVote === "ADVANCED"
? advancedVoteLoading
: standardVoteLoading && advancedVoteLoading,
/**
* TODO: frh -> what to do with the errors in SAFE:
* - If two txs, they probably go under the same nonce and therefore the second will fail. How are we informing this in the UI?
* - The user could also not execute the first tx and leave it for later. How are we informing this in the UI?
* - The user could also not execute the second tx and leave it for later. How are we informing this in the UI?
* - Sometimes the tx does not execute instantly because the user has some other SAFE txs in the queue and these
* have to be executed first.
*
* Remember that if waitForTransaction fails it means the txHash does not exist and therefore the SAFE transaction
* failed, probably due to a nonce error
*/
isError:
missingVote === "DIRECT"
? standardVoteError
: missingVote === "ADVANCED"
? advancedVoteError
: standardVoteError && advancedVoteError,
isSuccess:
missingVote === "DIRECT"
? standardVoteSuccess
: missingVote === "ADVANCED"
? advancedVoteSuccess
: standardVoteSuccess && advancedVoteSuccess,
write,
data: { advancedVoteData, standardVoteData },
data: { advancedTxHash, standardTxHash },
};
};

Expand Down

0 comments on commit 0315f7b

Please sign in to comment.