)}
{councilPeriod === '2' && (
-
+ >
)}
{!isOwn && councilPeriod !== '2' && (
@@ -352,3 +397,23 @@ export const UserProfileDetails = ({
>
);
};
+
+interface VotePowerToNetwork {
+ power: BigNumber;
+ isDeclared: boolean;
+}
+
+function votePowerToNetwork(
+ power?:
+ | {
+ L1: VotePowerToNetwork | undefined;
+ Optimism: VotePowerToNetwork | undefined;
+ }
+ | undefined
+) {
+ if (!power) return [];
+ const networks = [];
+ networks.push({ icon: EthereumIcon, chainId: 1, power: power.L1?.power });
+ networks.push({ icon: OPIcon, chainId: 10, power: power.Optimism?.power });
+ return networks;
+}
diff --git a/governance/ui/src/components/UserProfileCard/VotePower.tsx b/governance/ui/src/components/UserProfileCard/VotePower.tsx
new file mode 100644
index 000000000..c7770816a
--- /dev/null
+++ b/governance/ui/src/components/UserProfileCard/VotePower.tsx
@@ -0,0 +1,79 @@
+import { CloseIcon } from '@chakra-ui/icons';
+import { Button, Flex, Heading, IconButton, Text } from '@chakra-ui/react';
+import { ReactNode } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { useNetwork } from '../../queries';
+import { BigNumber } from 'ethers';
+import { formatNumber } from '@snx-v3/formatters';
+
+export default function VotePower({
+ activeCouncil,
+ networks,
+}: {
+ networks: { icon: ReactNode; chainId: number; power?: BigNumber }[];
+ activeCouncil: string;
+}) {
+ const navigate = useNavigate();
+ const { setNetwork } = useNetwork();
+ return (
+
+ navigate(`/councils/${activeCouncil}`)}
+ size="sm"
+ aria-label="close button"
+ icon={}
+ variant="ghost"
+ colorScheme="whiteAlpha"
+ color="white"
+ position="absolute"
+ top="16px"
+ right="0px"
+ _hover={{}}
+ />
+
+ Voting not available on Snaxchain
+
+
+ Voting is not available on Snaxchain. Switch to Ethereum or Optimism.{' '}
+
+
+ {networks.map((network) => (
+
+
+ {network.icon}
+
+
+ Total Voting Power
+
+
+ {formatNumber(network.power?.toString() || '0') || '0'}
+
+
+
+ setNetwork(network.chainId)}
+ >
+ Switch {network.chainId == 1 ? 'Ethereum' : 'Optimism'}
+
+
+ ))}
+
+
+ );
+}
diff --git a/governance/ui/src/components/UserProfileForm/UserProfileEditPreview.tsx b/governance/ui/src/components/UserProfileForm/UserProfileEditPreview.tsx
index a099a9be3..3d9e7a447 100644
--- a/governance/ui/src/components/UserProfileForm/UserProfileEditPreview.tsx
+++ b/governance/ui/src/components/UserProfileForm/UserProfileEditPreview.tsx
@@ -143,6 +143,7 @@ export default function UserProfileEditPreview({
fontSize="14px"
data-cy="governance-pitch-preview"
lineHeight="20px"
+ whiteSpace="pre-wrap"
overflowY="scroll"
h="330px"
mb="auto"
diff --git a/governance/ui/src/components/UserTableView/UserTableView.tsx b/governance/ui/src/components/UserTableView/UserTableView.tsx
index d41025f93..05ec18d8f 100644
--- a/governance/ui/src/components/UserTableView/UserTableView.tsx
+++ b/governance/ui/src/components/UserTableView/UserTableView.tsx
@@ -30,7 +30,8 @@ export default function UserTableView({
const { data: ballot } = useGetUserBallot(activeCouncil);
const { data: councilPeriod } = useGetCurrentPeriod(activeCouncil);
const isSelected = searchParams.get('view') === user.address;
- const councilIsInAdminOrVoting = councilPeriod === '2' || councilPeriod === '0';
+ const councilIsInAdminOrVotinOrEval =
+ councilPeriod === '2' || councilPeriod === '0' || councilPeriod === '3';
const totalVotingPowerPercentage =
totalVotingPower && user.voteResult
? formatNumber(user.voteResult?.votePower.mul(100).div(totalVotingPower).toString())
@@ -43,7 +44,7 @@ export default function UserTableView({
onClick={() => navigate(`/councils/${activeCouncil}?view=${user.address}`)}
_hover={{ background: 'rgba(255,255,255,0.12)' }}
>
- {councilIsInAdminOrVoting && (
+ {councilIsInAdminOrVotinOrEval && (
|
- {councilIsInAdminOrVoting && (
+ {councilIsInAdminOrVotinOrEval && (
)}
- {councilIsInAdminOrVoting && (
+ {councilIsInAdminOrVotinOrEval && (
|
);
- const { mutateAsync, isPending } = useCastVotes(councilToCastVote, councilsToAddress);
+ const { mutateAsync, isPending, isSuccess } = useCastVotes(councilToCastVote, councilsToAddress);
const navigate = useNavigate();
const formattedVotePower = formatNumber(
votingPowerSpartan?.power && votingPowerAmbassador?.power && votingPowerTreassury?.power
@@ -198,59 +201,100 @@ export default function MyVotes() {
flexDir="column"
h="fit-content"
>
-
- {period === '2' ? 'Cast Your Votes' : 'Voting Power'}
-
-
- Your voting power is determined by your staked SNX on Synthetix V2x and represents the
- influence you hold in the governance process.
-
-
-
- Total Voting Power
-
-
- {formattedVotePower === '0.00'
- ? formatNumber(
- votingPowerSpartan?.power
- .add(votingPowerAmbassador?.power || 0)
- .add(votingPowerTreassury?.power || 0)
- .toString() || 0
- )
- : formattedVotePower}
-
-
- {
- if (!network?.id) {
- connect();
- } else {
- if (councilToCastVote.length !== 3) {
- setShowConfirmation(true);
- } else {
- await mutateAsync();
+ {isSuccess ? (
+ <>
+ Your votes has been casted!
+
+ Your voting power is determined by your staked SNX on Synthetix V2x and represents
+ the influence you hold in the governance process.
+
+
+
+ It can take up to 45 minutes for Snaxchain to receive your vote. Refresh the page
+ to see when your vote has been received
+
+ navigate('/councils/spartan')}
+ >
+ Back to Homepage
+
+ >
+ ) : (
+ <>
+
+ {period === '2' ? 'Cast Your Votes' : 'Voting Power'}
+
+
+ Your voting power is determined by your staked SNX on Synthetix V2x and represents
+ the influence you hold in the governance process.
+
+
+
+ Total Voting Power
+
+
+ {formattedVotePower === '0.00'
+ ? formatNumber(
+ votingPowerSpartan?.power
+ .add(votingPowerAmbassador?.power || 0)
+ .add(votingPowerTreassury?.power || 0)
+ .toString() || 0
+ )
+ : formattedVotePower}
+
+
+
- {!network?.id ? 'Connect Wallet' : 'Cast Votes'}
-
+ onClick={async () => {
+ if (!network?.id) {
+ connect();
+ } else {
+ if (councilToCastVote.length !== 3) {
+ setShowConfirmation(true);
+ } else {
+ await mutateAsync();
+ }
+ }
+ }}
+ >
+ {!network?.id ? 'Connect Wallet' : 'Cast Votes'}
+
+ >
+ )}
+
{
@@ -294,7 +338,7 @@ export default function MyVotes() {
{council.title}
- {councilsToAddress[council.slug] ? (
+ {utils.isAddress(councilsToAddress[council.slug]) ? (
) : (
diff --git a/governance/ui/src/queries/useGetCurrentPeriod.ts b/governance/ui/src/queries/useGetCurrentPeriod.ts
index 1fb9a4786..12e6d4c1f 100644
--- a/governance/ui/src/queries/useGetCurrentPeriod.ts
+++ b/governance/ui/src/queries/useGetCurrentPeriod.ts
@@ -3,12 +3,17 @@ import { CouncilSlugs } from '../utils/councils';
import { motherShipProvider } from '../utils/providers';
import { getCouncilContract } from '../utils/contracts';
import { useNetwork } from './useWallet';
+import { useGetEpochSchedule } from './useGetEpochSchedule';
export function useGetCurrentPeriod(council?: CouncilSlugs) {
const { network } = useNetwork();
+ const { data: schedule } = useGetEpochSchedule(council);
return useQuery({
- queryKey: ['period', council, network?.id],
+ queryKey: ['period', council, network?.id, schedule?.endDate],
queryFn: async () => {
+ if (schedule?.endDate && schedule.endDate < 0) {
+ return '3';
+ }
return (
await getCouncilContract(council!)
.connect(motherShipProvider(network?.id || 2192))
diff --git a/governance/ui/src/queries/useGetEpochSchedule.ts b/governance/ui/src/queries/useGetEpochSchedule.ts
index 636e7c539..b910695c8 100644
--- a/governance/ui/src/queries/useGetEpochSchedule.ts
+++ b/governance/ui/src/queries/useGetEpochSchedule.ts
@@ -17,7 +17,8 @@ export function useGetEpochSchedule(council?: CouncilSlugs) {
startDate: Number(schedule.startDate.toString()),
nominationPeriodStartDate: Number(schedule.nominationPeriodStartDate.toString()),
votingPeriodStartDate: Number(schedule.votingPeriodStartDate.toString()),
- endDate: Number(schedule.endDate.toString()),
+ // Todo @dev remove after "bug" is resolved
+ endDate: Number((schedule.endDate - 3600).toString()),
} as
| {
startDate: number;
diff --git a/governance/ui/src/queries/useGetUserVotingPower.ts b/governance/ui/src/queries/useGetUserVotingPower.ts
index cccd5251d..5417de6ed 100644
--- a/governance/ui/src/queries/useGetUserVotingPower.ts
+++ b/governance/ui/src/queries/useGetUserVotingPower.ts
@@ -3,7 +3,7 @@ import { useNetwork } from '@snx-v3/useBlockchain';
import { CouncilSlugs } from '../utils/councils';
import { SnapshotRecordContract, getCouncilContract, isMotherchain } from '../utils/contracts';
import { useProvider, useWallet } from './';
-import { BigNumber, ethers } from 'ethers';
+import { BigNumber, ethers, providers } from 'ethers';
import { motherShipProvider } from '../utils/providers';
export function useGetUserVotingPower(council: CouncilSlugs) {
@@ -13,56 +13,84 @@ export function useGetUserVotingPower(council: CouncilSlugs) {
return useQuery({
queryFn: async () => {
- if (!activeWallet || !provider || !network?.id) return;
- const isCI = process.env.CI === 'true';
- try {
- const electionModule = getCouncilContract(council).connect(motherShipProvider(network.id));
- const isMC = isMotherchain(network.id);
-
- const snapshotContract = SnapshotRecordContract(network.id, council);
- const electionId = await electionModule.getEpochIndex();
- const ballot: { votingPower: BigNumber } | undefined = await electionModule.getBallot(
- activeWallet.address,
- network.id,
- electionId
- );
-
- const periodId = await snapshotContract?.connect(provider).currentPeriodId();
-
- const votingPowerFromMotherchain: BigNumber = isCI
- ? BigNumber.from(0)
- : await electionModule
- .connect(provider)
- .getVotingPowerForUser(snapshotContract?.address, activeWallet.address, periodId);
-
- if (ballot?.votingPower?.gt(0)) {
- return {
- power: isCI ? (ballot.votingPower as BigNumber) : votingPowerFromMotherchain,
- isDeclared: true,
- };
- }
- const votingPower: BigNumber = isCI
- ? isMC
- ? await electionModule
- .connect(provider)
- .callStatic.prepareBallotWithSnapshot(
- snapshotContract?.address,
- activeWallet?.address
- )
- : BigNumber.from(0)
- : votingPowerFromMotherchain;
-
- return {
- power: votingPower,
- isDeclared: false,
- };
- } catch (error) {
- console.error('ERROR IS', { error });
- return { power: ethers.BigNumber.from(0), isDeclared: false };
- }
+ return await fetchVotingPower(council, activeWallet?.address, provider, network?.id);
},
enabled: !!provider && !!activeWallet && !!network?.id,
queryKey: ['votingPower', council.toString(), activeWallet?.address, network?.id],
staleTime: 900000,
});
}
+
+export function useGetUserVotingPowerForAllChains(council: CouncilSlugs) {
+ const L1Provider = new providers.JsonRpcProvider(
+ 'https://mainnet.infura.io/v3/23087ce9f88c44d1b1c54fd7c07c65fb'
+ );
+ const OptimismProvider = new providers.JsonRpcProvider(
+ 'https://optimism-mainnet.infura.io/v3/23087ce9f88c44d1b1c54fd7c07c65fb'
+ );
+ const { activeWallet } = useWallet();
+ return useQuery({
+ queryFn: async () => {
+ return {
+ L1: await fetchVotingPower(council, activeWallet?.address, L1Provider, 1),
+ Optimism: await fetchVotingPower(council, activeWallet?.address, OptimismProvider, 10),
+ };
+ },
+ enabled: !!activeWallet,
+ queryKey: ['votingPowerForAllChains', council.toString(), activeWallet?.address],
+ staleTime: 900000,
+ });
+}
+
+async function fetchVotingPower(
+ council: CouncilSlugs,
+ activeWallet?: string,
+ provider?: providers.JsonRpcProvider,
+ networkId?: number
+) {
+ if (!activeWallet || !provider || !networkId) return;
+ const isCI = process.env.CI === 'true';
+ try {
+ const electionModule = getCouncilContract(council).connect(motherShipProvider(networkId));
+ const isMC = isMotherchain(networkId);
+
+ const snapshotContract = SnapshotRecordContract(networkId, council);
+ const electionId = await electionModule.getEpochIndex();
+ const ballot: { votingPower: BigNumber } | undefined = await electionModule.getBallot(
+ activeWallet,
+ networkId,
+ electionId
+ );
+
+ const periodId = await snapshotContract?.connect(provider).currentPeriodId();
+
+ const votingPowerFromMotherchain: BigNumber = isCI
+ ? BigNumber.from(0)
+ : await electionModule
+ .connect(provider)
+ .getVotingPowerForUser(snapshotContract?.address, activeWallet, periodId);
+
+ if (ballot?.votingPower?.gt(0)) {
+ return {
+ power: isCI ? (ballot.votingPower as BigNumber) : votingPowerFromMotherchain,
+ isDeclared: true,
+ };
+ }
+
+ const votingPower: BigNumber = isCI
+ ? isMC
+ ? await electionModule
+ .connect(provider)
+ .callStatic.prepareBallotWithSnapshot(snapshotContract?.address, activeWallet)
+ : BigNumber.from(0)
+ : votingPowerFromMotherchain;
+
+ return {
+ power: votingPower,
+ isDeclared: false,
+ };
+ } catch (error) {
+ console.error('ERROR IS', { error });
+ return { power: ethers.BigNumber.from(0), isDeclared: false };
+ }
+}
|