diff --git a/.circleci/config.yml b/.circleci/config.yml index b58c8421b..64584af03 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -120,56 +120,56 @@ jobs: - run: git diff --name-only --exit-code - run: yarn typecheck - # governance-e2e: - # working_directory: /tmp/app - # docker: - # - image: cypress/included:<< pipeline.parameters.cypress-version >> - # resource_class: large - # environment: - # NODE_ENV: test - # TESTNET: 1 - # steps: - # - checkout - # - install-foundry - # - yarn-install - - # - run: - # name: Run server localhost:3000 - # working_directory: /tmp/app/governance/ui - # command: CI=true yarn start - # background: true - - # - run: - # name: Run anvil localhost:8545 - # command: 'anvil --fork-url https://testnet.snaxchain.io --fork-block-number 296528' - # background: true - - # - run: - # name: Wait for server localhost:3000 - # command: wget --retry-connrefused --waitretry=20 --read-timeout=20 --timeout=15 -t 10 http://localhost:3000 - - # - run: - # name: Wait for anvil localhost:8545 - # command: wget -q -O - --retry-connrefused --waitretry=20 --read-timeout=20 --timeout=15 -t 10 --post-data='{"method":"eth_chainId","params":[],"id":1,"jsonrpc":"2.0"}' --header='Content-Type:application/json' http://localhost:8545 - - # - run: - # working_directory: /tmp/app/governance/cypress - # command: cypress run --e2e --browser chrome - - # - store_test_results: - # path: 'governance/cypress/cypress/reports' - - # - store_artifacts: - # path: 'governance/cypress/.nyc_output' - # destination: 'coverage' - - # - store_artifacts: - # path: 'governance/cypress/cypress/screenshots' - # destination: 'screenshots' - - # - store_artifacts: - # path: 'governance/cypress/cypress/videos' - # destination: 'videos' + governance-e2e: + working_directory: /tmp/app + docker: + - image: cypress/included:<< pipeline.parameters.cypress-version >> + resource_class: large + environment: + NODE_ENV: test + TESTNET: 1 + steps: + - checkout + - install-foundry + - yarn-install + + - run: + name: Run server localhost:3000 + working_directory: /tmp/app/governance/ui + command: CI=true yarn start + background: true + + - run: + name: Run anvil localhost:8545 + command: 'anvil --fork-url https://testnet.snaxchain.io --fork-block-number 296528' + background: true + + - run: + name: Wait for server localhost:3000 + command: wget --retry-connrefused --waitretry=20 --read-timeout=20 --timeout=15 -t 10 http://localhost:3000 + + - run: + name: Wait for anvil localhost:8545 + command: wget -q -O - --retry-connrefused --waitretry=20 --read-timeout=20 --timeout=15 -t 10 --post-data='{"method":"eth_chainId","params":[],"id":1,"jsonrpc":"2.0"}' --header='Content-Type:application/json' http://localhost:8545 + + - run: + working_directory: /tmp/app/governance/cypress + command: cypress run --e2e --browser chrome + + - store_test_results: + path: 'governance/cypress/cypress/reports' + + - store_artifacts: + path: 'governance/cypress/.nyc_output' + destination: 'coverage' + + - store_artifacts: + path: 'governance/cypress/cypress/screenshots' + destination: 'screenshots' + + - store_artifacts: + path: 'governance/cypress/cypress/videos' + destination: 'videos' liquidity-e2e: parameters: @@ -307,8 +307,8 @@ workflows: - typecheck - tests - liquidity-cy - # - governance-e2e: - # name: governance-e2e-snax-testnet + - governance-e2e: + name: governance-e2e-snax-testnet - liquidity-e2e: name: liquidity-e2e-base-mainnet chainId: 8453 @@ -323,7 +323,7 @@ workflows: requires: [ tests, liquidity-cy, - # governance-e2e-snax-testnet, + governance-e2e-snax-testnet, liquidity-e2e-base-mainnet, #liquidity-e2e-sepolia, ] diff --git a/governance/cypress/cypress/e2e/Councils - Administration.e2e.js b/governance/cypress/cypress/e2e/Councils - Administration.e2e.js index e4bd48442..b07073bc7 100644 --- a/governance/cypress/cypress/e2e/Councils - Administration.e2e.js +++ b/governance/cypress/cypress/e2e/Councils - Administration.e2e.js @@ -39,10 +39,10 @@ it('Councils - Administration', () => { cy.get('[data-cy="sort-arrow-up"]').should('exist'); cy.get('[data-cy="sort-arrow-up"]').click(); cy.get('[data-cy="sort-arrow-down"]').should('exist'); - cy.viewport(600, 500); + cy.viewport(400, 800); cy.visit('#/councils'); cy.get('[data-cy="my-votes-summary-text"]').should('have.css', 'font-size', '14px'); - cy.get('[data-cy="council-select-mobile"]').should('have.css', 'font-size', '14px'); + cy.get('[data-cy="council-select-mobile"]').should('have.css', 'font-size', '12px'); cy.get('[data-cy="menu-button-flex-council-select"]').should('have.css', 'height', '48px'); cy.get('[data-cy="my-votes-button"]').should('have.css', 'height', '48px'); }); diff --git a/governance/cypress/cypress/e2e/Councils - Voting.e2e.js b/governance/cypress/cypress/e2e/Councils - Voting.e2e.js index 7c0b6ebaf..ceb9f874f 100644 --- a/governance/cypress/cypress/e2e/Councils - Voting.e2e.js +++ b/governance/cypress/cypress/e2e/Councils - Voting.e2e.js @@ -38,6 +38,7 @@ it('Councils - Administration', () => { cy.get( '[data-cy="user-blockies-council-tabs-0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"]' ).should('exist'); + cy.get('[data-cy="selected-badge-my-row"]').should('exist'); cy.wait(3000); cy.get('[data-cy="my-votes-voting-power"]').contains('30.00'); cy.get('[data-cy="cast-my-vote-button"]').click(); @@ -50,10 +51,19 @@ it('Councils - Administration', () => { cy.get( '[data-cy="user-blockies-council-tabs-0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"]' ).should('exist'); + cy.visit('#/councils/spartan'); + cy.get('[data-cy="your-vote-badge-table"]').should('exist'); + cy.visit('#/my-votes'); cy.wait(3000); cy.get('[data-cy="remove-vote-button-spartan"]').click(); + cy.get('[data-cy="selected-badge-my-row"]').should('exist'); cy.get('[data-cy="cast-my-vote-button"]').click(); cy.get('[data-cy="cast-vote-anyway-button"]').click(); cy.wait(3000); cy.get('[data-cy="my-votes-total-votes"]').contains('0/3'); + cy.reload({ cache: false }); + cy.get( + '[data-cy="user-blockies-council-tabs-0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"]' + ).should('not.exist'); + cy.get('[data-cy="my-votes-total-votes"]').contains('0/3'); }); diff --git a/governance/cypress/cypress/tasks/changePeriod.js b/governance/cypress/cypress/tasks/changePeriod.js index c6fd3b708..ad95e98d1 100644 --- a/governance/cypress/cypress/tasks/changePeriod.js +++ b/governance/cypress/cypress/tasks/changePeriod.js @@ -13,11 +13,13 @@ export async function changePeriod({ council, period }) { '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80', provider ); + const electionId = await proxy.spartan.connect(signer).getEpochIndex(); + if (period === 'admin') { await proxy[council] .connect(signer) .Epoch_setEpochDates( - 0, + electionId, block.timestamp, block.timestamp + 10000, block.timestamp + 20000, @@ -27,7 +29,7 @@ export async function changePeriod({ council, period }) { await proxy[council] .connect(signer) .Epoch_setEpochDates( - 0, + electionId, block.timestamp - 10, block.timestamp, block.timestamp + 10000, @@ -37,7 +39,7 @@ export async function changePeriod({ council, period }) { await proxy[council] .connect(signer) .Epoch_setEpochDates( - 0, + electionId, block.timestamp - 200, block.timestamp - 100, block.timestamp, @@ -47,7 +49,7 @@ export async function changePeriod({ council, period }) { await proxy[council] .connect(signer) .Epoch_setEpochDates( - 0, + electionId, block.timestamp - 10, block.timestamp - 5, block.timestamp - 1, diff --git a/governance/cypress/cypress/tasks/prepareVotingPower.js b/governance/cypress/cypress/tasks/prepareVotingPower.js index 2061463d6..f2d324f49 100644 --- a/governance/cypress/cypress/tasks/prepareVotingPower.js +++ b/governance/cypress/cypress/tasks/prepareVotingPower.js @@ -1,32 +1,37 @@ -import { ethers, Wallet } from 'ethers'; -import { getCouncilContract, SnapshotRecordContract } from '../../../ui/src/utils/contracts'; +import { Contract, ethers, Wallet } from 'ethers'; +import { SnapshotRecordContract } from '../../../ui/src/utils/contracts'; +import { electionModuleABITest } from '../../../ui/src/utils/abi'; export async function prepareVotingPower({ council }) { const provider = new ethers.providers.JsonRpcProvider('http://127.0.0.1:8545'); - const proxy = getCouncilContract(council); + const proxy = { + spartan: new Contract('0xBC85F11300A8EF619592fD678418Ec4eF26FBdFD', electionModuleABITest), + ambassador: new Contract('0xCdbEf5753cE3CEbF361e143117e345ADd7498F80', electionModuleABITest), + treasury: new Contract('0xe3aB2C6F1C9E46Fb53eD6b297c6fff68e935B161', electionModuleABITest), + }; + const block = await provider.getBlock('latest'); const signer = new Wallet( '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80', provider ); - const block = await provider.getBlock('latest'); - await proxy + const electionId = await proxy.spartan.connect(signer).getEpochIndex(); + await proxy[council] .connect(signer) .Epoch_setEpochDates( - 0, + electionId, block.timestamp - 200, block.timestamp - 100, block.timestamp, block.timestamp + 10000 ); - // let id = ''; - // try { - // id = await proxy - // .connect(signer) - // .takeVotePowerSnapshot(SnapshotRecordContract(2192, council)?.address); - // } catch (error) { - // console.info('takeVotePowerSnapshot failed'); - // } + try { + await proxy[council] + .connect(signer) + .takeVotePowerSnapshot(SnapshotRecordContract(13001, council)?.address); + } catch (error) { + console.info('takeVotePowerSnapshot failed'); + } await SnapshotRecordContract(13001, council) .connect(signer) .setBalanceOfOnPeriod(await signer.getAddress(), 100, 1); diff --git a/governance/ui/.env.example b/governance/ui/.env.example index 0c7ceb20f..6e1b1ed99 100644 --- a/governance/ui/.env.example +++ b/governance/ui/.env.example @@ -1,6 +1,3 @@ WC_PROJECT_ID=5075a2da602e17eec34aa77b40b321be BOARDROOM_KEY=d9abe7a1ab45ace58e6bd91bb9771586 -DEV=false -DEV_RPC_MOTHERSHIP=http://127.0.0.1:19000 CI=false -CI_RPC_MOTHERSHIP=http://127.0.0.1:8545 diff --git a/governance/ui/src/components/CouncilInformation/CouncilInformation.tsx b/governance/ui/src/components/CouncilInformation/CouncilInformation.tsx index bb2756db2..c13eb61ef 100644 --- a/governance/ui/src/components/CouncilInformation/CouncilInformation.tsx +++ b/governance/ui/src/components/CouncilInformation/CouncilInformation.tsx @@ -1,4 +1,4 @@ -import { Flex, Heading, Icon, Text } from '@chakra-ui/react'; +import { Button, Flex, Heading, Icon, Text } from '@chakra-ui/react'; import councils, { CouncilSlugs } from '../../utils/councils'; import { CouncilImage } from '../CouncilImage'; import { Link } from 'react-router-dom'; @@ -57,16 +57,7 @@ export default function CouncilInformation({ activeCouncil }: { activeCouncil: C Stipends: {council?.stipends}/month - + diff --git a/governance/ui/src/components/CouncilMembers/CouncilMembers.tsx b/governance/ui/src/components/CouncilMembers/CouncilMembers.tsx index 661792f2b..d8fa06fb3 100644 --- a/governance/ui/src/components/CouncilMembers/CouncilMembers.tsx +++ b/governance/ui/src/components/CouncilMembers/CouncilMembers.tsx @@ -2,12 +2,10 @@ import { Divider, Flex, Heading, - Skeleton, Table, TableContainer, Tag, Tbody, - Td, Text, Th, Thead, @@ -21,6 +19,7 @@ import { useMemo, useState } from 'react'; import { ArrowUpDownIcon } from '@chakra-ui/icons'; import SortArrows from '../SortArrows/SortArrows'; import { useGetCouncilMembers, useGetUserDetailsQuery } from '../../queries'; +import TableLoading from '../TableLoading/TableLoading'; export default function CouncilMembers({ activeCouncil }: { activeCouncil: CouncilSlugs }) { const [sortConfig, setSortConfig] = useState<[boolean, string]>([false, 'start']); @@ -163,36 +162,7 @@ export default function CouncilMembers({ activeCouncil }: { activeCouncil: Counc {userDetailsLoading || !councilMemberDetails ? ( - <> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + ) : ( !!sortedNominees?.length && sortedNominees.map((councilNominee, index) => { diff --git a/governance/ui/src/components/CouncilNominees/CouncilNominees.tsx b/governance/ui/src/components/CouncilNominees/CouncilNominees.tsx index baea49ae9..c0bb23ef2 100644 --- a/governance/ui/src/components/CouncilNominees/CouncilNominees.tsx +++ b/governance/ui/src/components/CouncilNominees/CouncilNominees.tsx @@ -4,6 +4,8 @@ import { Flex, Heading, Input, + InputGroup, + InputRightElement, Table, TableContainer, Tbody, @@ -23,48 +25,83 @@ import { useGetCurrentPeriod } from '../../queries/useGetCurrentPeriod'; import { useMemo, useState } from 'react'; import { utils } from 'ethers'; import SortArrows from '../SortArrows/SortArrows'; -import { useWallet } from '../../queries/useWallet'; +import { useNetwork, useWallet } from '../../queries/useWallet'; import { CouncilImage } from '../CouncilImage'; +import TableLoading from '../TableLoading/TableLoading'; +import { CloseIcon } from '@chakra-ui/icons'; +import { useVoteContext } from '../../context/VoteContext'; +import { useGetEpochIndex, useGetHistoricalVotes } from '../../queries'; +import { getVoteSelectionState } from '../../utils/localstorage'; export default function CouncilNominees({ activeCouncil }: { activeCouncil: CouncilSlugs }) { const [search, setSearch] = useState(''); - const [sortConfig, setSortConfig] = useState<[boolean, string]>([false, 'start']); + const [sortConfig, setSortConfig] = useState<[boolean, string]>([false, 'name']); + const { network } = useNetwork(); + const { data: epochId } = useGetEpochIndex(activeCouncil); const { activeWallet, connect } = useWallet(); - const { data: councilNomineesDetails } = useGetNomineesDetails(activeCouncil); + const { data: councilNomineesDetails, isLoading } = useGetNomineesDetails(activeCouncil); + const { data: votes } = useGetHistoricalVotes(); const { data: councilSchedule } = useGetEpochSchedule(activeCouncil); const { data: nextEpochDuration } = useGetNextElectionSettings(activeCouncil); const { data: councilPeriod } = useGetCurrentPeriod(activeCouncil); - + const { state } = useVoteContext(); + const currentSelectedUser = getVoteSelectionState( + state, + epochId, + network?.id.toString(), + activeCouncil + ); const council = councils.find((council) => council.slug === activeCouncil); - const epoch = calculateNextEpoch(councilSchedule, nextEpochDuration); + const votesForCouncil = votes && votes[activeCouncil]; - let sortedNominees = useMemo(() => { - return !!councilNomineesDetails?.length - ? councilNomineesDetails - .filter((nominee) => { - if (councilPeriod !== '2') { - nominee?.address.toLowerCase() !== activeWallet?.address.toLowerCase(); - } - return true; - }) - .filter((nominee) => { - if (utils.isAddress(search)) { + const sortedNominees = useMemo(() => { + if (councilNomineesDetails?.length && votesForCouncil) { + return councilNomineesDetails + .map((nominee) => { + const vote = votesForCouncil.find( + (vote) => + epochId === vote.id && vote.voter.toLowerCase() === nominee.address.toLowerCase() + ); + if (vote) return (nominee = { ...nominee, vote }); + return nominee; + }) + .filter((nominee) => { + if (utils.isAddress(search)) { + return nominee.address.toLowerCase().includes(search); + } + if (search) { + if (nominee.username) { + return nominee.username.toLowerCase().includes(search); + } else { return nominee.address.toLowerCase().includes(search); } - if (search) { - if (nominee.username) { - return nominee.username.toLowerCase().includes(search); - } else { - return nominee.address.toLowerCase().includes(search); - } + } + return true; + }) + .sort((a, b) => { + if (sortConfig[1] === 'name') { + if (a.username && b.username) { + return sortConfig[0] + ? a.username.localeCompare(b.username) + : a.username.localeCompare(b.username) * -1; + } + if (a.username && !b.username) { + return -1; + } else if (b.username && !a.username) { + return 1; } - return true; - }) - : []; - }, [search, councilNomineesDetails, activeWallet?.address, councilPeriod]); + return sortConfig[0] + ? a.address.localeCompare(b.address) + : a.address.localeCompare(b.address) * -1; + } + return 0; + }); + } + return []; + }, [search, councilNomineesDetails, sortConfig, votesForCouncil, epochId]); return ( ) : ( - + + - Nominate Yourself for the {activeCouncil} Council ); diff --git a/governance/ui/src/components/MyVoteRow/MyVoteRow.tsx b/governance/ui/src/components/MyVoteRow/MyVoteRow.tsx index bf96d3b7d..94e998e20 100644 --- a/governance/ui/src/components/MyVoteRow/MyVoteRow.tsx +++ b/governance/ui/src/components/MyVoteRow/MyVoteRow.tsx @@ -4,8 +4,9 @@ import { AddIcon, ArrowForwardIcon, CloseIcon } from '@chakra-ui/icons'; import { useNavigate } from 'react-router-dom'; import CouncilUser from '../CouncilUser/CouncilUser'; import { useVoteContext } from '../../context/VoteContext'; -import { useGetCurrentPeriod, useGetUserBallot, useNetwork } from '../../queries'; +import { useGetEpochIndex, useGetUserBallot, useNetwork } from '../../queries'; import { getVoteSelectionState } from '../../utils/localstorage'; +import { Badge } from '../Badge'; export default function MyVoteRow({ councilSlug, @@ -18,16 +19,19 @@ export default function MyVoteRow({ }) { const navigate = useNavigate(); const { data: ballot } = useGetUserBallot(councilSlug); - const { data: epochId } = useGetCurrentPeriod(councilSlug); + const { data: epochId } = useGetEpochIndex(councilSlug); const { dispatch, state } = useVoteContext(); const { network } = useNetwork(); + const networkForState = getVoteSelectionState( state, epochId, network?.id.toString(), councilSlug ); + const voteAddressState = typeof networkForState === 'string' ? networkForState : ''; + return ( - + - + )} + {ballot?.votedCandidates.includes(voteAddressState) ? ( + Your Vote + ) : voteAddressState ? ( + + Selected + + ) : null} {!networkForState && !ballot?.votedCandidates[0] ? ( (); const [isLongpress, setIsLongpress] = useState(false); diff --git a/governance/ui/src/components/NominateSelf/NominateSelf.tsx b/governance/ui/src/components/NominateSelf/NominateSelf.tsx index 82026d1fa..870678d56 100644 --- a/governance/ui/src/components/NominateSelf/NominateSelf.tsx +++ b/governance/ui/src/components/NominateSelf/NominateSelf.tsx @@ -55,6 +55,8 @@ export default function NominateSelf({ activeCouncil, ...props }: NominateSelfPr useEffect(() => { if (network?.id && !isMotherchain(network.id)) { setShowSnaxChainBanner(true); + } else { + setShowSnaxChainBanner(false); } }, [network?.id]); diff --git a/governance/ui/src/components/TableLoading/TableLoading.tsx b/governance/ui/src/components/TableLoading/TableLoading.tsx new file mode 100644 index 000000000..feb7296e3 --- /dev/null +++ b/governance/ui/src/components/TableLoading/TableLoading.tsx @@ -0,0 +1,78 @@ +import { Skeleton, Td, Tr } from '@chakra-ui/react'; + +export default function TableLoading() { + return ( + <> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/governance/ui/src/components/TableLoading/index.ts b/governance/ui/src/components/TableLoading/index.ts new file mode 100644 index 000000000..747474240 --- /dev/null +++ b/governance/ui/src/components/TableLoading/index.ts @@ -0,0 +1 @@ +export * from './TableLoading'; diff --git a/governance/ui/src/components/UserListItem/UserListItem.tsx b/governance/ui/src/components/UserListItem/UserListItem.tsx index ba5084f46..72e3f3ef0 100644 --- a/governance/ui/src/components/UserListItem/UserListItem.tsx +++ b/governance/ui/src/components/UserListItem/UserListItem.tsx @@ -70,14 +70,8 @@ export default function UserListItem({ size="xs" variant="outline" colorScheme="gray" - onClick={(e) => { - e.stopPropagation(); - if (!nominationInformation?.isNominated) { - navigate(`/councils/${activeCouncil}?nominate=true`); - } else { - navigate(`/councils/${activeCouncil}?editNomination=true`); - } - }} + _hover={{}} + _active={{}} data-cy="user-list-item-button-nomination" > {nominationInformation?.isNominated && isOwn ? ( @@ -93,14 +87,18 @@ export default function UserListItem({ variant="outline" colorScheme="gray" data-cy="nominate-self-button-user-profile-details-voting-period" + _hover={{}} onClick={(e) => { e.stopPropagation(); if (nominationInformation?.isNominated) { - navigate(`/councils/${activeCouncil}?view=${address}`); + navigate({ + pathname: `/councils/${activeCouncil}`, + search: `view=${address}`, + }); } else { navigate({ pathname: `/councils/${activeCouncil}`, - search: 'nominate=true', + search: `view=${address}&nominate=true`, }); } }} @@ -115,9 +113,17 @@ export default function UserListItem({ size="xs" variant="outline" colorScheme="gray" + _hover={{}} onClick={(e) => { e.stopPropagation(); - navigate(`/councils/${activeCouncil}?view=${address}`); + if (nominationInformation?.isNominated && isNominatedFetched) { + navigate({ pathname: `/councils/${activeCouncil}`, search: `view=${address}` }); + } else { + navigate({ + pathname: `/councils/${activeCouncil}`, + search: 'nominate=true', + }); + } }} color="white" > diff --git a/governance/ui/src/components/UserProfileCard/UserProfileDetails.tsx b/governance/ui/src/components/UserProfileCard/UserProfileDetails.tsx index 428402d90..a2e56636b 100644 --- a/governance/ui/src/components/UserProfileCard/UserProfileDetails.tsx +++ b/governance/ui/src/components/UserProfileCard/UserProfileDetails.tsx @@ -8,7 +8,7 @@ import { useNavigate } from 'react-router-dom'; import { useVoteContext } from '../../context/VoteContext'; import { ProfilePicture } from './ProfilePicture'; import { EditIcon, ShareIcon } from '../Icons'; -import { useGetCurrentPeriod, useGetUserBallot, useNetwork } from '../../queries'; +import { useGetEpochIndex, useGetUserBallot, useNetwork } from '../../queries'; import { useEffect, useRef, useState } from 'react'; import { useRecoilState } from 'recoil'; import { voteCardState } from '../../state/vote-card'; @@ -37,7 +37,7 @@ export const UserProfileDetails = ({ const [isTooltipOpen, setIsTooltipOpen] = useState(false); const [isWalletTooltipOpen, setWalletIsTooltipOpen] = useState(false); const { network } = useNetwork(); - const { data: epochId } = useGetCurrentPeriod(activeCouncil); + const { data: epochId } = useGetEpochIndex(activeCouncil); const { dispatch, state } = useVoteContext(); const navigate = useNavigate(); const { data: ballot } = useGetUserBallot(activeCouncil); diff --git a/governance/ui/src/components/UserProfileForm/UserProfileEditPreview.tsx b/governance/ui/src/components/UserProfileForm/UserProfileEditPreview.tsx index 30773dc91..a099a9be3 100644 --- a/governance/ui/src/components/UserProfileForm/UserProfileEditPreview.tsx +++ b/governance/ui/src/components/UserProfileForm/UserProfileEditPreview.tsx @@ -11,11 +11,13 @@ export default function UserProfileEditPreview({ activeWallet, isPending, isDirty, + hasError, }: { isPending: boolean; activeWallet?: string; isDirty: boolean; userData: GetUserDetails; + hasError: boolean; }) { const elementRef = useRef(null); const [isOverflowing, setIsOverflowing] = useState(false); @@ -166,7 +168,7 @@ export default function UserProfileEditPreview({ w="100%" type="submit" data-cy="save-profile-changes-button" - isDisabled={!isDirty} + isDisabled={!isDirty || hasError} > Save Changes diff --git a/governance/ui/src/components/UserProfileForm/UserProfileForm.tsx b/governance/ui/src/components/UserProfileForm/UserProfileForm.tsx index d8ba77480..ba7b4ffee 100644 --- a/governance/ui/src/components/UserProfileForm/UserProfileForm.tsx +++ b/governance/ui/src/components/UserProfileForm/UserProfileForm.tsx @@ -24,6 +24,10 @@ import { unstable_useBlocker as useBlocker } from 'react-router-dom'; import { useWallet } from '../../queries/useWallet'; import UserProfileEditPreview from './UserProfileEditPreview'; +const urlPattern = new RegExp('https?:\\/\\/(?:www\\.)?[^s/$.?#].[^s]*', 'i'); + +type OnChangeEvent = Record>; + export function UserProfileForm() { const { isOpen, onClose, onOpen } = useDisclosure(); const { activeWallet } = useWallet(); @@ -34,7 +38,17 @@ export function UserProfileForm() { onClose: onBlockerClose, onOpen: onBlockerOpen, } = useDisclosure({ id: 'blocker' }); - const { register, getValues, setValue, watch, formState, reset, handleSubmit } = useForm({ + const { + register, + getValues, + setValue, + watch, + formState, + reset, + handleSubmit, + setError, + clearErrors, + } = useForm({ defaultValues: { address: user?.about, username: user?.username, @@ -60,7 +74,6 @@ export function UserProfileForm() { setValue('delegationPitch', user.delegationPitch); } }, [user, setValue]); - const handleOnFormSave = async () => { reset({ about: getValues('about')!, @@ -128,25 +141,6 @@ export function UserProfileForm() { mt="9" mb="24" > - {/* - - - - - - Avatar - - - */} - Username @@ -173,19 +167,64 @@ export function UserProfileForm() { Discord - + { + if (urlPattern.test(event.target.value)) { + setError('discord', { message: 'Only usernames. No links' }); + } else { + clearErrors('discord'); + } + }, + })} + placeholder="eg: username" + data-cy="discord-input" + /> + + {formState.errors?.discord?.message} + Twitter - + { + if (urlPattern.test(event.target.value)) { + setError('twitter', { message: 'Only usernames. No links' }); + } else { + clearErrors('twitter'); + } + }, + })} + placeholder="eg: username" + data-cy="twitter-input" + /> + + {formState.errors?.twitter?.message} + Github - + { + if (urlPattern.test(event.target.value)) { + setError('github', { message: 'Only usernames. No links' }); + } else { + clearErrors('github'); + } + }, + })} + placeholder="eg: username" + data-cy="github-input" + /> + + {formState.errors?.github?.message} + @@ -287,6 +326,7 @@ export function UserProfileForm() { isPending={isPending} userData={userData} isDirty={formState.isDirty} + hasError={!!Object.keys(formState.errors).length} /> @@ -301,6 +341,7 @@ export function UserProfileForm() { isPending={isPending} userData={userData} isDirty={formState.isDirty} + hasError={!!Object.keys(formState.errors).length} /> diff --git a/governance/ui/src/components/UserTableView/UserTableView.tsx b/governance/ui/src/components/UserTableView/UserTableView.tsx index ca49ccd91..eef5cef5c 100644 --- a/governance/ui/src/components/UserTableView/UserTableView.tsx +++ b/governance/ui/src/components/UserTableView/UserTableView.tsx @@ -6,9 +6,12 @@ import { useGetCurrentPeriod } from '../../queries/useGetCurrentPeriod'; import { CouncilSlugs } from '../../utils/councils'; import { ProfilePicture } from '../UserProfileCard/ProfilePicture'; import { prettyString } from '@snx-v3/format'; +import { useGetEpochIndex, useGetUserBallot, useNetwork } from '../../queries'; +import { getVoteSelectionState } from '../../utils/localstorage'; +import { useVoteContext } from '../../context/VoteContext'; function renderCorrectBorder( - column: 'place' | 'name' | 'votes' | 'power', + column: 'place' | 'name' | 'votes' | 'power' | 'badge', position: 'left' | 'right' | 'bottom', period: string | undefined, isSelected: boolean @@ -35,9 +38,9 @@ function renderCorrectBorder( if (position === 'bottom') return isSelected ? '1px solid' : ''; } else if (column === 'power') { if (position === 'bottom') return isSelected ? '1px solid' : ''; - if (position === 'right') { - if (period === '2') return isSelected ? '1px solid' : ''; - } + } else if (column === 'badge') { + if (position === 'bottom') return isSelected ? '1px solid' : ''; + if (position === 'right') return isSelected ? '1px solid' : ''; } } @@ -45,7 +48,9 @@ export default function UserTableView({ user, activeCouncil, place, + isSelectedForVoting, }: { + isSelectedForVoting?: boolean; place: number; user: GetUserDetails; activeCouncil: CouncilSlugs; @@ -53,10 +58,20 @@ export default function UserTableView({ }) { const navigate = useNavigate(); const [searchParams] = useSearchParams(); + const { data: ballot } = useGetUserBallot(activeCouncil); const { data: councilPeriod } = useGetCurrentPeriod(activeCouncil); - const isSelected = searchParams.get('view') === user.address; + const { network } = useNetwork(); + const { data: epochId } = useGetEpochIndex(activeCouncil); + const { state } = useVoteContext(); const councilIsInAdminOrVoting = councilPeriod === '2' || councilPeriod === '0'; + const networkForState = getVoteSelectionState( + state, + epochId, + network?.id.toString(), + activeCouncil + ); + const voteAddressState = typeof networkForState === 'string' ? networkForState : ''; return ( )} + {councilPeriod === '2' && ( + + {ballot?.votedCandidates.includes(voteAddressState) ? ( + + Your Vote + + ) : isSelectedForVoting ? ( + + Selected + + ) : null} + + )} {councilPeriod !== '2' && councilPeriod !== '0' && ( { switch (action.type) { case 'SPARTAN': { - if (action.payload.action) { + if (action.payload.action && action.payload.action !== 'remove') { setCandidate( action.payload.action, 'spartan', @@ -75,7 +75,7 @@ const voteReducer = (state: VoteState, action: Action): VoteState => { }; } case 'AMBASSADOR': { - if (action.payload.action) { + if (action.payload.action && action.payload.action !== 'remove') { setCandidate( action.payload.action, 'ambassador', @@ -97,7 +97,7 @@ const voteReducer = (state: VoteState, action: Action): VoteState => { }; } case 'TREASURY': { - if (action.payload.action) { + if (action.payload.action && action.payload.action !== 'remove') { setCandidate( action.payload.action, 'treasury', @@ -125,7 +125,7 @@ const voteReducer = (state: VoteState, action: Action): VoteState => { const VoteProvider: React.FC<{ children: ReactNode }> = ({ children }) => { const { network } = useNetwork(); - const { data: epochId } = useGetCurrentPeriod('spartan'); + const { data: epochId } = useGetEpochIndex('spartan'); const [init, setInit] = useState(false); const [state, dispatch] = useReducer( voteReducer, diff --git a/governance/ui/src/mutations/index.ts b/governance/ui/src/mutations/index.ts index 72fba119a..a6100960a 100644 --- a/governance/ui/src/mutations/index.ts +++ b/governance/ui/src/mutations/index.ts @@ -1,5 +1,4 @@ export * from './useCastVotes'; -export * from './useDeclareVotingPower'; export * from './useEditNomination'; export * from './useNominateSelf'; export * from './useUpdateUserDetailsMutation'; diff --git a/governance/ui/src/mutations/useCastVotes.ts b/governance/ui/src/mutations/useCastVotes.ts index a8ba0d532..fb25febe0 100644 --- a/governance/ui/src/mutations/useCastVotes.ts +++ b/governance/ui/src/mutations/useCastVotes.ts @@ -1,6 +1,6 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { - useGetCurrentPeriod, + useGetEpochIndex, useGetUserVotingPower, useNetwork, useSigner, @@ -8,10 +8,11 @@ import { } from '../queries'; import { CouncilSlugs } from '../utils/councils'; import { getCouncilContract, isMotherchain, SnapshotRecordContract } from '../utils/contracts'; -import { BigNumber } from 'ethers'; +import { BigNumber, utils } from 'ethers'; import { useVoteContext } from '../context/VoteContext'; import { useMulticall } from '../hooks/useMulticall'; import { useToast } from '@chakra-ui/react'; +import { motherShipProvider } from '../utils/providers'; export function useCastVotes( councils: CouncilSlugs[], @@ -23,7 +24,7 @@ export function useCastVotes( const { network } = useNetwork(); const { activeWallet } = useWallet(); const { dispatch } = useVoteContext(); - const { data: epochId } = useGetCurrentPeriod('spartan'); + const { data: epochId } = useGetEpochIndex('spartan'); const multicall = useMulticall(); const { data: spartanVotingPower } = useGetUserVotingPower('spartan'); const { data: ambassadorVotingPower } = useGetUserVotingPower('ambassador'); @@ -50,6 +51,20 @@ export function useCastVotes( const electionModules = councils.map((council) => getCouncilContract(council).connect(signer) ); + const nomineesCheck = await Promise.all( + electionModules.map(async (council, index) => { + if (utils.isAddress(candidates[councils[index]] || '')) { + return council + .connect(motherShipProvider(network.id)) + .isNominated(candidates[councils[index]]); + } + return true; + }) + ); + + if (nomineesCheck.some((val) => val === false)) { + throw new Error('Some of the candidates were not nominees'); + } const prepareBallotData = councils .map((council, index) => { if (!getVotingPowerByCouncil(council)?.isDeclared) { @@ -114,21 +129,25 @@ export function useCastVotes( [isMC ? 'aggregate' : 'aggregate3Value']([...prepareBallotData, ...castData], { value: isMC ? 0 : quote.add(quote.mul(25).div(100)), }); - } catch (error) { + } catch (error: any) { console.error(error); toast({ - description: 'Could not cast votes.', + title: 'Could not cast votes.', + description: error && 'message' in error ? error.message : '', status: 'error', + isClosable: true, }); } } else { console.error('signer not connected'); } }, - onError: () => { + onError: (error) => { toast({ - description: 'Could not cast votes.', + title: 'Could not cast votes.', + description: error.message, status: 'error', + isClosable: true, }); }, onSuccess: async () => { diff --git a/governance/ui/src/mutations/useDeclareVotingPower.ts b/governance/ui/src/mutations/useDeclareVotingPower.ts deleted file mode 100644 index e504140d3..000000000 --- a/governance/ui/src/mutations/useDeclareVotingPower.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { useMutation } from '@tanstack/react-query'; -import { CouncilSlugs } from '../utils/councils'; -import { getCouncilContract, SnapshotRecordContract } from '../utils/contracts'; -import { useNetwork, useSigner } from '../queries/useWallet'; - -export default function useDeclareVotingPower(council: CouncilSlugs) { - const signer = useSigner(); - const { network } = useNetwork(); - - return useMutation({ - mutationFn: async () => { - if (signer && network) { - try { - const electionModule = getCouncilContract(council).connect(signer); - const voter = await signer.getAddress(); - await electionModule.prepareBallotWithSnapshot( - SnapshotRecordContract(network.id, council)?.address, - voter - ); - } catch (error) { - console.error(error); - } - } - }, - mutationKey: ['userVotingPower', council], - }); -} diff --git a/governance/ui/src/mutations/useEditNomination.ts b/governance/ui/src/mutations/useEditNomination.ts index 85bf0e52e..d401e5cc1 100644 --- a/governance/ui/src/mutations/useEditNomination.ts +++ b/governance/ui/src/mutations/useEditNomination.ts @@ -67,6 +67,7 @@ export default function useEditNomination({ toast({ description: 'Nomination successfully edited.', status: 'success', + isClosable: true, }); }, }); diff --git a/governance/ui/src/mutations/useNominateSelf.ts b/governance/ui/src/mutations/useNominateSelf.ts index 05e338cb0..2459e61f4 100644 --- a/governance/ui/src/mutations/useNominateSelf.ts +++ b/governance/ui/src/mutations/useNominateSelf.ts @@ -26,6 +26,7 @@ export default function useNominateSelf(council: CouncilSlugs, address?: string) toast({ description: err?.message ? err.message : 'Something went wrong.', status: 'error', + isClosable: true, }); throw new Error(err); } @@ -51,6 +52,7 @@ export default function useNominateSelf(council: CouncilSlugs, address?: string) toast({ description: 'Successfully nominated yourself.', status: 'success', + isClosable: true, }); }, }); diff --git a/governance/ui/src/mutations/useUpdateUserDetailsMutation.ts b/governance/ui/src/mutations/useUpdateUserDetailsMutation.ts index ec4640cb1..30df00bfd 100644 --- a/governance/ui/src/mutations/useUpdateUserDetailsMutation.ts +++ b/governance/ui/src/mutations/useUpdateUserDetailsMutation.ts @@ -178,6 +178,7 @@ function useUpdateUserDetailsMutation() { toast({ description: 'Your profile has been updated.', status: 'success', + isClosable: true, }); }, }); diff --git a/governance/ui/src/pages/MyProfile.tsx b/governance/ui/src/pages/MyProfile.tsx index fe2402088..a63b8a2e8 100644 --- a/governance/ui/src/pages/MyProfile.tsx +++ b/governance/ui/src/pages/MyProfile.tsx @@ -11,6 +11,7 @@ export default function MyProfile() { Please connect your wallet ); + return ( <> diff --git a/governance/ui/src/pages/MyVotes.tsx b/governance/ui/src/pages/MyVotes.tsx index f89c842df..031b948e6 100644 --- a/governance/ui/src/pages/MyVotes.tsx +++ b/governance/ui/src/pages/MyVotes.tsx @@ -17,7 +17,7 @@ import { useGetCurrentPeriod } from '../queries/useGetCurrentPeriod'; import { useGetEpochSchedule } from '../queries/useGetEpochSchedule'; import { Timer } from '../components/Timer'; import CouncilTabs from '../components/CouncilTabs/CouncilTabs'; -import { useGetUserVotingPower, useNetwork, useWallet } from '../queries/'; +import { useGetEpochIndex, useGetUserVotingPower, useNetwork, useWallet } from '../queries/'; import { useCastVotes } from '../mutations'; import { formatNumber } from '@snx-v3/formatters'; import MyVoteRow from '../components/MyVoteRow/MyVoteRow'; @@ -32,7 +32,7 @@ export default function MyVotes() { const { onClose } = useDisclosure(); const { data: period } = useGetCurrentPeriod('spartan'); const { data: schedule } = useGetEpochSchedule('spartan'); - const { data: epochId } = useGetCurrentPeriod('spartan'); + const { data: epochId } = useGetEpochIndex('spartan'); const { network } = useNetwork(); const { connect } = useWallet(); const { data: votingPowerSpartan } = useGetUserVotingPower('spartan'); @@ -40,7 +40,6 @@ export default function MyVotes() { const { data: votingPowerTreassury } = useGetUserVotingPower('treasury'); const { state } = useVoteContext(); const networkForState = getVoteSelectionState(state, epochId, network?.id.toString()); - const voteAddressState = typeof networkForState === 'string' ? networkForState : ''; const stateFromCouncils = ( typeof networkForState !== 'string' ? networkForState : {} ) as VoteStateForNetwork; @@ -49,7 +48,6 @@ export default function MyVotes() { .map(([council]) => council) as CouncilSlugs[]; const { mutateAsync, isPending } = useCastVotes(councilToCastVote, stateFromCouncils); const navigate = useNavigate(); - return ( <> @@ -163,12 +161,8 @@ export default function MyVotes() { {period === '2' ? 'Cast Your Votes' : 'Voting Power'} - Your total voting powered is aggregated from all chains and used to vote on Optimism. - It can take{' '} - - up to 15 mins - {' '} - to transfer. + Your voting power is determined by your staked SNX on Synthetix V2x and represents the + influence you hold in the governance process. - {voteAddressState ? ( - + {stateFromCouncils[council.slug] ? ( + ) : ( )} diff --git a/governance/ui/src/pages/index.css b/governance/ui/src/pages/index.css index 74de8a53c..dfc9b9bcc 100644 --- a/governance/ui/src/pages/index.css +++ b/governance/ui/src/pages/index.css @@ -2,6 +2,10 @@ --onboard-modal-z-index: 1; } +body { + height: 100%; +} + .blockies-rounded { border-radius: 50%; } diff --git a/governance/ui/src/queries/index.ts b/governance/ui/src/queries/index.ts index 9ddfe506d..44183aecc 100644 --- a/governance/ui/src/queries/index.ts +++ b/governance/ui/src/queries/index.ts @@ -2,7 +2,7 @@ export * from './useGetCouncilNominees'; export * from './useGetUserBallot'; export * from './useGetCouncilMembers'; export * from './useGetCurrentPeriod'; -export * from './useGetElectionSettings'; +export * from './useGetEpochIndex'; export * from './useGetEpochSchedule'; export * from './useGetIsNominated'; export * from './useGetIsUUIDValidQuery'; diff --git a/governance/ui/src/queries/useGetElectionSettings.ts b/governance/ui/src/queries/useGetElectionSettings.ts deleted file mode 100644 index bcd325c4d..000000000 --- a/governance/ui/src/queries/useGetElectionSettings.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; -import { CouncilSlugs } from '../utils/councils'; -import { getCouncilContract } from '../utils/contracts'; -import { motherShipProvider } from '../utils/providers'; -import { useNetwork } from './useWallet'; - -export function useGetElectionSettings(council: CouncilSlugs) { - const { network } = useNetwork(); - return useQuery({ - queryKey: ['useGetElectionSettings', council], - queryFn: async () => { - return (await getCouncilContract(council) - .connect(motherShipProvider(network?.id || 13001)) - .getElectionSettings()) as Promise<{ - epochSeatCount: number; - minimumActiveMembers: number; - epochDuration: number; - nominationPeriodDuration: number; - votingPeriodDuration: number; - maxDateAdjustmentTolerance: number; - }>; - }, - staleTime: 900000, - }); -} diff --git a/governance/ui/src/queries/useGetEpochIndex.ts b/governance/ui/src/queries/useGetEpochIndex.ts new file mode 100644 index 000000000..cd7e06010 --- /dev/null +++ b/governance/ui/src/queries/useGetEpochIndex.ts @@ -0,0 +1,19 @@ +import { useQuery } from '@tanstack/react-query'; +import { CouncilSlugs } from '../utils/councils'; +import { getCouncilContract } from '../utils/contracts'; +import { motherShipProvider } from '../utils/providers'; +import { useNetwork } from './useWallet'; + +export function useGetEpochIndex(council: CouncilSlugs) { + const { network } = useNetwork(); + + return useQuery({ + queryKey: ['epochId', council], + queryFn: async () => { + return await getCouncilContract(council) + .connect(motherShipProvider(network?.id || 13001)) + .getEpochIndex(); + }, + staleTime: 900000, + }); +} diff --git a/governance/ui/src/queries/useGetHistoricalVotes.ts b/governance/ui/src/queries/useGetHistoricalVotes.ts index 126218be7..846a59223 100644 --- a/governance/ui/src/queries/useGetHistoricalVotes.ts +++ b/governance/ui/src/queries/useGetHistoricalVotes.ts @@ -1,17 +1,46 @@ import { useQuery } from '@tanstack/react-query'; -import { CouncilSlugs } from '../utils/councils'; import { useNetwork } from './useWallet'; +import councils, { CouncilSlugs } from '../utils/councils'; const testnetURL = 'https://api.synthetix.io/v3/snax-testnet/votes'; +const mainnetURL = 'https://api.synthetix.io/v3/snax/votes'; -export function useGetHistoricalVotes(council: CouncilSlugs) { +export interface Vote { + eventName: string; + chainId: number; + epochId: number; + voter: string; + blockTimestamp: number; + id: string; + transactionHash: string; + votingPower: string; + blockNumber: number; + candidates: string[]; + contract: string; +} + +export function useGetHistoricalVotes() { const { network } = useNetwork(); return useQuery({ - queryKey: ['historical-votes', council, network?.id], + queryKey: ['historical-votes', network?.id], queryFn: async () => { - const res = await fetch(network?.id === 2192 ? testnetURL : testnetURL); - const votes = await res.json(); - return votes; + const res = await fetch(network?.id !== 2192 ? mainnetURL : testnetURL); + let votesRaw: Record = await res.json(); + if (!('spartan' in votesRaw)) { + votesRaw = { spartan: votesRaw[0], ambassador: [], treasury: [] }; + } + return councils.reduce( + (cur, next) => { + cur[next.slug] = votesRaw[next.slug].sort((a, b) => { + if (a.epochId === b.epochId) { + return a.blockTimestamp - b.blockTimestamp; + } + return a.epochId - b.epochId; + }); + return cur; + }, + {} as Record + ); }, staleTime: 900000, }); diff --git a/governance/ui/src/queries/useGetUserDetailsQuery.ts b/governance/ui/src/queries/useGetUserDetailsQuery.ts index 9cd94e457..226bb353a 100644 --- a/governance/ui/src/queries/useGetUserDetailsQuery.ts +++ b/governance/ui/src/queries/useGetUserDetailsQuery.ts @@ -3,6 +3,7 @@ import { useQuery } from '@tanstack/react-query'; import { motherShipProvider } from '../utils/providers'; import { profileContract } from '../utils/contracts'; import { utils } from 'ethers'; +import { Vote } from './useGetHistoricalVotes'; export type GetUserDetails = { address: string; @@ -25,6 +26,7 @@ export type GetUserDetails = { delegationPitch: string; github: string; council?: string; + vote?: Vote; }; type UserPitch = { @@ -37,9 +39,10 @@ export function useGetUserDetailsQuery(walletAddres return useQuery({ queryKey: ['userDetails', walletAddress], queryFn: async () => { - return await getUserDetails(walletAddress!); + const users = await getUserDetails(walletAddress!); + return users; }, - enabled: !!walletAddress, + enabled: Array.isArray(walletAddress) ? !!walletAddress.length : !!walletAddress, staleTime: 900000, }); } @@ -152,6 +155,8 @@ export async function getUserDetails( } const profileIsEmpty = (profile: GetUserDetails) => { - const values = Object.values(profile).filter((val) => !utils.isAddress(val) && !!val.trim()); + const values = Object.values(profile).filter((val) => + typeof val === 'string' ? !utils.isAddress(val) && !!val.trim() : !Object.keys(val).length + ); return values.every((val) => !!val); }; diff --git a/governance/ui/src/queries/useGetUserVotingPower.ts b/governance/ui/src/queries/useGetUserVotingPower.ts index 14360b105..76699a2ed 100644 --- a/governance/ui/src/queries/useGetUserVotingPower.ts +++ b/governance/ui/src/queries/useGetUserVotingPower.ts @@ -5,7 +5,6 @@ import { SnapshotRecordContract, getCouncilContract, isMotherchain } from '../ut import { useProvider, useWallet } from './'; import { BigNumber, ethers } from 'ethers'; import { motherShipProvider } from '../utils/providers'; -import { sqrt } from '../utils/math'; export function useGetUserVotingPower(council: CouncilSlugs) { const { network } = useNetwork(); @@ -15,7 +14,7 @@ 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); @@ -24,27 +23,39 @@ export function useGetUserVotingPower(council: CouncilSlugs) { const ballot = isMC ? await electionModule.getBallot(activeWallet.address, network.id, electionId) : await electionModule.connect(provider).getPreparedBallot(activeWallet.address); + const votingPowerFromMotherchain: BigNumber = isMC + ? await electionModule + .connect(motherShipProvider(network.id)) + .getVotePower(activeWallet.address, network.id, electionId) + : BigNumber.from(0); if (ballot) { if (ballot?.votingPower?.gt(0)) { - return { power: ballot.votingPower as BigNumber, isDeclared: true }; + return { + power: isCI ? (ballot.votingPower as BigNumber) : votingPowerFromMotherchain, + isDeclared: true, + }; } else if ('gt' in ballot && ballot.gt(0)) { - return { power: ballot as BigNumber, isDeclared: true }; + return { + power: isCI ? (ballot as BigNumber) : votingPowerFromMotherchain, + isDeclared: true, + }; } } - const votingPower: BigNumber = isMC - ? await electionModule - .connect(provider) - .callStatic.prepareBallotWithSnapshot( - SnapshotRecordContract(network.id, council)?.address, - activeWallet?.address - ) - : await SnapshotRecordContract(network.id, council) - ?.connect(provider) - .balanceOfOnPeriod(activeWallet.address, 1); - // TODO @dev check when prod is live + const votingPower: BigNumber = isCI + ? isMC + ? await electionModule + .connect(provider) + .callStatic.prepareBallotWithSnapshot( + SnapshotRecordContract(network.id, council)?.address, + activeWallet?.address + ) + : await SnapshotRecordContract(network.id, council) + ?.connect(provider) + .balanceOfOnPeriod(activeWallet.address, 1) + : BigNumber.from(0); return { - power: process.env.CI === 'true' ? votingPower : sqrt(votingPower), + power: process.env.CI === 'true' ? votingPower : votingPowerFromMotherchain, isDeclared: false, }; } catch (error) { diff --git a/governance/ui/src/utils/abi.ts b/governance/ui/src/utils/abi.ts index 729525104..6bac7d7e6 100644 --- a/governance/ui/src/utils/abi.ts +++ b/governance/ui/src/utils/abi.ts @@ -911,7 +911,7 @@ export const multicallABI = [ }, ]; -export const electionModuleABITest = [ +export const electionModuleABI = [ { inputs: [ { @@ -3336,3 +3336,3528 @@ export const electionModuleABITest = [ type: 'function', }, ]; +export const electionModuleABITest = [ + { + inputs: [ + { + internalType: 'bytes32', + name: 'expected', + type: 'bytes32', + }, + { + internalType: 'bytes32', + name: 'actual', + type: 'bytes32', + }, + ], + name: 'MismatchAssociatedSystemKind', + type: 'error', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'id', + type: 'bytes32', + }, + ], + name: 'MissingAssociatedSystem', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'addr', + type: 'address', + }, + ], + name: 'Unauthorized', + type: 'error', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'bytes32', + name: 'kind', + type: 'bytes32', + }, + { + indexed: true, + internalType: 'bytes32', + name: 'id', + type: 'bytes32', + }, + { + indexed: false, + internalType: 'address', + name: 'proxy', + type: 'address', + }, + { + indexed: false, + internalType: 'address', + name: 'impl', + type: 'address', + }, + ], + name: 'AssociatedSystemSet', + type: 'event', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'id', + type: 'bytes32', + }, + ], + name: 'getAssociatedSystem', + outputs: [ + { + internalType: 'address', + name: 'addr', + type: 'address', + }, + { + internalType: 'bytes32', + name: 'kind', + type: 'bytes32', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'id', + type: 'bytes32', + }, + { + internalType: 'string', + name: 'name', + type: 'string', + }, + { + internalType: 'string', + name: 'symbol', + type: 'string', + }, + { + internalType: 'string', + name: 'uri', + type: 'string', + }, + { + internalType: 'address', + name: 'impl', + type: 'address', + }, + ], + name: 'initOrUpgradeNft', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'id', + type: 'bytes32', + }, + { + internalType: 'string', + name: 'name', + type: 'string', + }, + { + internalType: 'string', + name: 'symbol', + type: 'string', + }, + { + internalType: 'uint8', + name: 'decimals', + type: 'uint8', + }, + { + internalType: 'address', + name: 'impl', + type: 'address', + }, + ], + name: 'initOrUpgradeToken', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'id', + type: 'bytes32', + }, + { + internalType: 'address', + name: 'endpoint', + type: 'address', + }, + ], + name: 'registerUnmanagedSystem', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'candidate', + type: 'address', + }, + { + internalType: 'uint256', + name: 'epochIndex', + type: 'uint256', + }, + ], + name: 'getCandidateVotesInEpoch', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'epochIndex', + type: 'uint256', + }, + ], + name: 'getElectionWinnersInEpoch', + outputs: [ + { + internalType: 'address[]', + name: '', + type: 'address[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'epochIndex', + type: 'uint256', + }, + ], + name: 'getEpochEndDateForIndex', + outputs: [ + { + internalType: 'uint64', + name: '', + type: 'uint64', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'epochIndex', + type: 'uint256', + }, + ], + name: 'getEpochStartDateForIndex', + outputs: [ + { + internalType: 'uint64', + name: '', + type: 'uint64', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'epochIndex', + type: 'uint256', + }, + ], + name: 'getNominationPeriodStartDateForIndex', + outputs: [ + { + internalType: 'uint64', + name: '', + type: 'uint64', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'epochIndex', + type: 'uint256', + }, + ], + name: 'getNomineesAtEpoch', + outputs: [ + { + internalType: 'address[]', + name: '', + type: 'address[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'epochIndex', + type: 'uint256', + }, + ], + name: 'getVotingPeriodStartDateForIndex', + outputs: [ + { + internalType: 'uint64', + name: '', + type: 'uint64', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'user', + type: 'address', + }, + { + internalType: 'uint256', + name: 'chainId', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'epochIndex', + type: 'uint256', + }, + ], + name: 'hasVotedInEpoch', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'candidate', + type: 'address', + }, + { + internalType: 'uint256', + name: 'epochIndex', + type: 'uint256', + }, + ], + name: 'wasNominated', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'AlreadyACouncilMember', + type: 'error', + }, + { + inputs: [], + name: 'AlreadyNominated', + type: 'error', + }, + { + inputs: [], + name: 'ChangesCurrentPeriod', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'duplicatedCandidate', + type: 'address', + }, + ], + name: 'DuplicateCandidates', + type: 'error', + }, + { + inputs: [], + name: 'ElectionAlreadyEvaluated', + type: 'error', + }, + { + inputs: [], + name: 'ElectionNotEvaluated', + type: 'error', + }, + { + inputs: [], + name: 'EmptyArray', + type: 'error', + }, + { + inputs: [], + name: 'InsufficientValue', + type: 'error', + }, + { + inputs: [], + name: 'InvalidBallot', + type: 'error', + }, + { + inputs: [], + name: 'InvalidElectionSettings', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'code', + type: 'uint256', + }, + { + internalType: 'uint64', + name: 'v1', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'v2', + type: 'uint64', + }, + ], + name: 'InvalidEpochConfiguration', + type: 'error', + }, + { + inputs: [ + { + internalType: 'string', + name: 'parameter', + type: 'string', + }, + { + internalType: 'string', + name: 'reason', + type: 'string', + }, + ], + name: 'InvalidParameter', + type: 'error', + }, + { + inputs: [ + { + internalType: 'string', + name: 'reason', + type: 'string', + }, + ], + name: 'InvalidVM', + type: 'error', + }, + { + inputs: [], + name: 'MessageAlreadyProcessed', + type: 'error', + }, + { + inputs: [], + name: 'NoCandidates', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'sender', + type: 'address', + }, + { + internalType: 'uint256', + name: 'currentEpoch', + type: 'uint256', + }, + ], + name: 'NoVotingPower', + type: 'error', + }, + { + inputs: [], + name: 'NotACouncilMember', + type: 'error', + }, + { + inputs: [], + name: 'NotCallableInCurrentPeriod', + type: 'error', + }, + { + inputs: [], + name: 'NotImplemented', + type: 'error', + }, + { + inputs: [], + name: 'NotNominated', + type: 'error', + }, + { + inputs: [], + name: 'OnlyRelayer', + type: 'error', + }, + { + inputs: [], + name: 'OverflowUint256ToUint16', + type: 'error', + }, + { + inputs: [], + name: 'OverflowUint256ToUint64', + type: 'error', + }, + { + inputs: [], + name: 'PositionOutOfBounds', + type: 'error', + }, + { + inputs: [], + name: 'TooManyMembers', + type: 'error', + }, + { + inputs: [], + name: 'UnregisteredEmitter', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint64', + name: '', + type: 'uint64', + }, + ], + name: 'UnsupportedNetwork', + type: 'error', + }, + { + inputs: [], + name: 'ValueAlreadyInSet', + type: 'error', + }, + { + inputs: [], + name: 'ValueNotInSet', + type: 'error', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'candidate', + type: 'address', + }, + { + indexed: true, + internalType: 'uint256', + name: 'epochId', + type: 'uint256', + }, + ], + name: 'CandidateNominated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'member', + type: 'address', + }, + { + indexed: true, + internalType: 'uint256', + name: 'epochIndex', + type: 'uint256', + }, + ], + name: 'CouncilMemberAdded', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'member', + type: 'address', + }, + { + indexed: true, + internalType: 'uint256', + name: 'epochIndex', + type: 'uint256', + }, + ], + name: 'CouncilMemberRemoved', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address[]', + name: 'dismissedMembers', + type: 'address[]', + }, + { + indexed: false, + internalType: 'uint256', + name: 'epochId', + type: 'uint256', + }, + ], + name: 'CouncilMembersDismissed', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'uint256', + name: 'epochId', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'numEvaluatedBallots', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'totalBallots', + type: 'uint256', + }, + ], + name: 'ElectionBatchEvaluated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'uint256', + name: 'epochId', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'ballotCount', + type: 'uint256', + }, + ], + name: 'ElectionEvaluated', + type: 'event', + }, + { + anonymous: false, + inputs: [], + name: 'ElectionModuleInitialized', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'uint256', + name: 'epochId', + type: 'uint256', + }, + ], + name: 'EmergencyElectionStarted', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint256', + name: 'epochIndex', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint64', + name: 'nominationPeriodStartDate', + type: 'uint64', + }, + { + indexed: false, + internalType: 'uint64', + name: 'votingPeriodStartDate', + type: 'uint64', + }, + { + indexed: false, + internalType: 'uint64', + name: 'epochEndDate', + type: 'uint64', + }, + ], + name: 'EpochScheduleTweaked', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'uint64', + name: 'epochId', + type: 'uint64', + }, + { + indexed: false, + internalType: 'uint64', + name: 'startDate', + type: 'uint64', + }, + { + indexed: false, + internalType: 'uint64', + name: 'endDate', + type: 'uint64', + }, + ], + name: 'EpochScheduleUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint256', + name: 'epochIndex', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint64', + name: 'epochStartDate', + type: 'uint64', + }, + { + indexed: false, + internalType: 'uint64', + name: 'nominationPeriodStartDate', + type: 'uint64', + }, + { + indexed: false, + internalType: 'uint64', + name: 'votingPeriodStartDate', + type: 'uint64', + }, + { + indexed: false, + internalType: 'uint64', + name: 'epochEndDate', + type: 'uint64', + }, + ], + name: 'EpochSetup', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'uint256', + name: 'epochId', + type: 'uint256', + }, + ], + name: 'EpochStarted', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint256', + name: 'gasLimit', + type: 'uint256', + }, + ], + name: 'GasLimitSet', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint256', + name: 'epochIndex', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint64', + name: 'epochStartDate', + type: 'uint64', + }, + { + indexed: false, + internalType: 'uint64', + name: 'nominationPeriodStartDate', + type: 'uint64', + }, + { + indexed: false, + internalType: 'uint64', + name: 'votingPeriodStartDate', + type: 'uint64', + }, + { + indexed: false, + internalType: 'uint64', + name: 'epochEndDate', + type: 'uint64', + }, + { + indexed: false, + internalType: 'address[]', + name: 'councilMembers', + type: 'address[]', + }, + ], + name: 'InitializedSatellite', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'string', + name: 'message', + type: 'string', + }, + ], + name: 'MessageReceived', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'candidate', + type: 'address', + }, + { + indexed: true, + internalType: 'uint256', + name: 'epochId', + type: 'uint256', + }, + ], + name: 'NominationWithdrawn', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'sender', + type: 'address', + }, + { + indexed: false, + internalType: 'address[]', + name: 'candidates', + type: 'address[]', + }, + { + indexed: false, + internalType: 'uint256[]', + name: 'amounts', + type: 'uint256[]', + }, + ], + name: 'VoteCastSent', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'voter', + type: 'address', + }, + { + indexed: true, + internalType: 'uint256', + name: 'chainId', + type: 'uint256', + }, + { + indexed: true, + internalType: 'uint256', + name: 'epochId', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'votingPower', + type: 'uint256', + }, + { + indexed: false, + internalType: 'address[]', + name: 'candidates', + type: 'address[]', + }, + ], + name: 'VoteRecorded', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'voter', + type: 'address', + }, + { + indexed: true, + internalType: 'uint256', + name: 'chainId', + type: 'uint256', + }, + { + indexed: true, + internalType: 'uint256', + name: 'epochId', + type: 'uint256', + }, + ], + name: 'VoteWithdrawn', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'sender', + type: 'address', + }, + ], + name: 'VoteWithdrawnSent', + type: 'event', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'epochIndex', + type: 'uint256', + }, + { + internalType: 'address', + name: 'voter', + type: 'address', + }, + { + internalType: 'uint256', + name: 'votingPower', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'chainId', + type: 'uint256', + }, + { + internalType: 'address[]', + name: 'candidates', + type: 'address[]', + }, + { + internalType: 'uint256[]', + name: 'amounts', + type: 'uint256[]', + }, + ], + name: '_recvCast', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address[]', + name: 'membersToDismiss', + type: 'address[]', + }, + { + internalType: 'uint256', + name: 'epochIndex', + type: 'uint256', + }, + ], + name: '_recvDismissMembers', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'epochIndex', + type: 'uint256', + }, + { + internalType: 'uint64', + name: 'epochStartDate', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'nominationPeriodStartDate', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'votingPeriodStartDate', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'epochEndDate', + type: 'uint64', + }, + { + internalType: 'address[]', + name: 'councilMembers', + type: 'address[]', + }, + ], + name: '_recvResolve', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'epochIndex', + type: 'uint256', + }, + { + internalType: 'uint64', + name: 'nominationPeriodStartDate', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'votingPeriodStartDate', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'epochEndDate', + type: 'uint64', + }, + ], + name: '_recvTweakEpochSchedule', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'epochIndex', + type: 'uint256', + }, + { + internalType: 'address', + name: 'voter', + type: 'address', + }, + { + internalType: 'uint256', + name: 'chainId', + type: 'uint256', + }, + ], + name: '_recvWithdrawVote', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address[]', + name: 'candidates', + type: 'address[]', + }, + { + internalType: 'uint256[]', + name: 'amounts', + type: 'uint256[]', + }, + ], + name: 'cast', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address[]', + name: 'membersToDismiss', + type: 'address[]', + }, + ], + name: 'dismissMembers', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'numBallots', + type: 'uint256', + }, + ], + name: 'evaluate', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'voter', + type: 'address', + }, + { + internalType: 'uint256', + name: 'chainId', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'electionId', + type: 'uint256', + }, + ], + name: 'getBallot', + outputs: [ + { + components: [ + { + internalType: 'uint256', + name: 'votingPower', + type: 'uint256', + }, + { + internalType: 'address[]', + name: 'votedCandidates', + type: 'address[]', + }, + { + internalType: 'uint256[]', + name: 'amounts', + type: 'uint256[]', + }, + ], + internalType: 'struct Ballot.Data', + name: '', + type: 'tuple', + }, + ], + stateMutability: 'pure', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'voter', + type: 'address', + }, + { + internalType: 'uint256', + name: 'chainId', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'electionId', + type: 'uint256', + }, + ], + name: 'getBallotCandidates', + outputs: [ + { + internalType: 'address[]', + name: '', + type: 'address[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'candidate', + type: 'address', + }, + ], + name: 'getCandidateVotes', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getCouncilMembers', + outputs: [ + { + internalType: 'address[]', + name: '', + type: 'address[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getCouncilToken', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getCurrentPeriod', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getElectionSettings', + outputs: [ + { + components: [ + { + internalType: 'uint8', + name: 'epochSeatCount', + type: 'uint8', + }, + { + internalType: 'uint8', + name: 'minimumActiveMembers', + type: 'uint8', + }, + { + internalType: 'uint64', + name: 'epochDuration', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'nominationPeriodDuration', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'votingPeriodDuration', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'maxDateAdjustmentTolerance', + type: 'uint64', + }, + ], + internalType: 'struct ElectionSettings.Data', + name: 'settings', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getElectionWinners', + outputs: [ + { + internalType: 'address[]', + name: '', + type: 'address[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getEpochIndex', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getEpochSchedule', + outputs: [ + { + components: [ + { + internalType: 'uint64', + name: 'startDate', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'nominationPeriodStartDate', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'votingPeriodStartDate', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'endDate', + type: 'uint64', + }, + ], + internalType: 'struct Epoch.Data', + name: 'epoch', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getGasLimit', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getNextElectionSettings', + outputs: [ + { + components: [ + { + internalType: 'uint8', + name: 'epochSeatCount', + type: 'uint8', + }, + { + internalType: 'uint8', + name: 'minimumActiveMembers', + type: 'uint8', + }, + { + internalType: 'uint64', + name: 'epochDuration', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'nominationPeriodDuration', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'votingPeriodDuration', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'maxDateAdjustmentTolerance', + type: 'uint64', + }, + ], + internalType: 'struct ElectionSettings.Data', + name: 'settings', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getNominees', + outputs: [ + { + internalType: 'address[]', + name: '', + type: 'address[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getRegisteredEmitters', + outputs: [ + { + internalType: 'bytes32[]', + name: '', + type: 'bytes32[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getSupportedNetworks', + outputs: [ + { + internalType: 'uint16[]', + name: '', + type: 'uint16[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'user', + type: 'address', + }, + { + internalType: 'uint256', + name: 'chainId', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'electionId', + type: 'uint256', + }, + ], + name: 'getVotePower', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getWormholeCore', + outputs: [ + { + internalType: 'contract IWormhole', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getWormholeRelayer', + outputs: [ + { + internalType: 'contract IWormholeRelayer', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'deliveryHash', + type: 'bytes32', + }, + ], + name: 'hasProcessedMsg', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'user', + type: 'address', + }, + { + internalType: 'uint256', + name: 'chainId', + type: 'uint256', + }, + ], + name: 'hasVoted', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'epochIndex', + type: 'uint256', + }, + { + internalType: 'uint64', + name: 'epochStartDate', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'nominationPeriodStartDate', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'votingPeriodStartDate', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'epochEndDate', + type: 'uint64', + }, + { + internalType: 'contract IWormhole', + name: 'wormholeCore', + type: 'address', + }, + { + internalType: 'contract IWormholeRelayer', + name: 'wormholeRelayer', + type: 'address', + }, + { + internalType: 'address[]', + name: 'councilMembers', + type: 'address[]', + }, + ], + name: 'initElectionModuleSatellite', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'initElectionModuleSatellite', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address[]', + name: 'initialCouncil', + type: 'address[]', + }, + { + internalType: 'contract IWormhole', + name: 'wormholeCore', + type: 'address', + }, + { + internalType: 'contract IWormholeRelayer', + name: 'wormholeRelayer', + type: 'address', + }, + { + internalType: 'uint8', + name: 'minimumActiveMembers', + type: 'uint8', + }, + { + internalType: 'uint64', + name: 'initialNominationPeriodStartDate', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'administrationPeriodDuration', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'nominationPeriodDuration', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'votingPeriodDuration', + type: 'uint64', + }, + ], + name: 'initOrUpdateElectionSettings', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'isElectionEvaluated', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'isElectionModuleInitialized', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'candidate', + type: 'address', + }, + ], + name: 'isNominated', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'nominate', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint16', + name: 'targetChain', + type: 'uint16', + }, + { + internalType: 'uint256', + name: 'receiverValue', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'gasLimit', + type: 'uint256', + }, + ], + name: 'quoteCrossChainDeliveryPrice', + outputs: [ + { + internalType: 'uint256', + name: 'cost', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes', + name: 'payload', + type: 'bytes', + }, + { + internalType: 'bytes[]', + name: '', + type: 'bytes[]', + }, + { + internalType: 'bytes32', + name: 'sourceAddress', + type: 'bytes32', + }, + { + internalType: 'uint16', + name: 'sourceChain', + type: 'uint16', + }, + { + internalType: 'bytes32', + name: 'deliveryHash', + type: 'bytes32', + }, + ], + name: 'receiveWormholeMessages', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint16[]', + name: 'chainIds', + type: 'uint16[]', + }, + ], + name: 'removeRegisteredEmitters', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'resolve', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'gasLimit', + type: 'uint256', + }, + ], + name: 'setGasLimit', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint8', + name: 'epochSeatCount', + type: 'uint8', + }, + { + internalType: 'uint8', + name: 'minimumActiveMembers', + type: 'uint8', + }, + { + internalType: 'uint64', + name: 'epochDuration', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'nominationPeriodDuration', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'votingPeriodDuration', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'maxDateAdjustmentTolerance', + type: 'uint64', + }, + ], + name: 'setNextElectionSettings', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint16[]', + name: 'chainIds', + type: 'uint16[]', + }, + { + internalType: 'address[]', + name: 'emitters', + type: 'address[]', + }, + ], + name: 'setRegisteredEmitters', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint64', + name: 'newNominationPeriodStartDate', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'newVotingPeriodStartDate', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'newEpochEndDate', + type: 'uint64', + }, + ], + name: 'tweakEpochSchedule', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [], + name: 'withdrawNomination', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'withdrawVote', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'voter', + type: 'address', + }, + { + internalType: 'uint256', + name: 'electionId', + type: 'uint256', + }, + ], + name: 'BallotAlreadyPrepared', + type: 'error', + }, + { + inputs: [], + name: 'InvalidSnapshotContract', + type: 'error', + }, + { + inputs: [], + name: 'InvalidWeightType', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + name: 'NoPower', + type: 'error', + }, + { + inputs: [], + name: 'OverflowUint256ToUint128', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint128', + name: 'snapshotId', + type: 'uint128', + }, + ], + name: 'SnapshotAlreadyTaken', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'snapshotContract', + type: 'address', + }, + { + internalType: 'uint128', + name: 'electionId', + type: 'uint128', + }, + ], + name: 'SnapshotNotTaken', + type: 'error', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'snapshotContract', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'scale', + type: 'uint256', + }, + ], + name: 'ScaleSet', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'snapshotContract', + type: 'address', + }, + { + indexed: true, + internalType: 'bool', + name: 'enabled', + type: 'bool', + }, + { + indexed: false, + internalType: 'enum SnapshotVotePower.WeightType', + name: 'weight', + type: 'uint8', + }, + ], + name: 'SnapshotContractSet', + type: 'event', + }, + { + inputs: [ + { + internalType: 'address', + name: 'voter', + type: 'address', + }, + ], + name: 'getPreparedBallot', + outputs: [ + { + internalType: 'uint256', + name: 'power', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'snapshotContract', + type: 'address', + }, + { + internalType: 'uint128', + name: 'electionId', + type: 'uint128', + }, + ], + name: 'getVotePowerSnapshotId', + outputs: [ + { + internalType: 'uint128', + name: '', + type: 'uint128', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'snapshotContract', + type: 'address', + }, + { + internalType: 'address', + name: 'voter', + type: 'address', + }, + ], + name: 'prepareBallotWithSnapshot', + outputs: [ + { + internalType: 'uint256', + name: 'votingPower', + type: 'uint256', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'snapshotContract', + type: 'address', + }, + { + internalType: 'uint256', + name: 'scale', + type: 'uint256', + }, + ], + name: 'setScale', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'snapshotContract', + type: 'address', + }, + { + internalType: 'enum SnapshotVotePower.WeightType', + name: 'weight', + type: 'uint8', + }, + { + internalType: 'bool', + name: 'enabled', + type: 'bool', + }, + ], + name: 'setSnapshotContract', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'snapshotContract', + type: 'address', + }, + ], + name: 'takeVotePowerSnapshot', + outputs: [ + { + internalType: 'uint128', + name: 'snapshotId', + type: 'uint128', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'implementation', + type: 'address', + }, + ], + name: 'ImplementationIsSterile', + type: 'error', + }, + { + inputs: [], + name: 'NoChange', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'contr', + type: 'address', + }, + ], + name: 'NotAContract', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'addr', + type: 'address', + }, + ], + name: 'NotNominated', + type: 'error', + }, + { + inputs: [], + name: 'UpgradeSimulationFailed', + type: 'error', + }, + { + inputs: [], + name: 'ZeroAddress', + type: 'error', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'oldOwner', + type: 'address', + }, + { + indexed: false, + internalType: 'address', + name: 'newOwner', + type: 'address', + }, + ], + name: 'OwnerChanged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'newOwner', + type: 'address', + }, + ], + name: 'OwnerNominated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'self', + type: 'address', + }, + { + indexed: false, + internalType: 'address', + name: 'implementation', + type: 'address', + }, + ], + name: 'Upgraded', + type: 'event', + }, + { + inputs: [], + name: 'acceptOwnership', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'getImplementation', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'newNominatedOwner', + type: 'address', + }, + ], + name: 'nominateNewOwner', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'nominatedOwner', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'owner', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'renounceNomination', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'newImplementation', + type: 'address', + }, + ], + name: 'simulateUpgradeTo', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'newImplementation', + type: 'address', + }, + ], + name: 'upgradeTo', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'Council_get_currentElectionId', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'Council_get_initialized', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'Council_jumpToNominationPeriod', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'Council_newElection', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'enum Epoch.ElectionPeriod', + name: 'period', + type: 'uint8', + }, + ], + name: 'Council_onlyInPeriod', + outputs: [], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'enum Epoch.ElectionPeriod', + name: 'period1', + type: 'uint8', + }, + { + internalType: 'enum Epoch.ElectionPeriod', + name: 'period2', + type: 'uint8', + }, + ], + name: 'Council_onlyInPeriods', + outputs: [], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'val', + type: 'uint256', + }, + ], + name: 'Council_set_currentElectionId', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bool', + name: 'val', + type: 'bool', + }, + ], + name: 'Council_set_initialized', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint64', + name: 'epochStartDate', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'nominationPeriodStartDate', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'votingPeriodStartDate', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'epochEndDate', + type: 'uint64', + }, + ], + name: 'Council_validateEpochSchedule', + outputs: [], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_electionId', + type: 'uint256', + }, + { + internalType: 'address', + name: '_load_voter', + type: 'address', + }, + { + internalType: 'uint256', + name: '_load_chainId', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'idx', + type: 'uint256', + }, + ], + name: 'Ballot_get_amounts', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_electionId', + type: 'uint256', + }, + { + internalType: 'address', + name: '_load_voter', + type: 'address', + }, + { + internalType: 'uint256', + name: '_load_chainId', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'idx', + type: 'uint256', + }, + ], + name: 'Ballot_get_votedCandidates', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_electionId', + type: 'uint256', + }, + { + internalType: 'address', + name: '_load_voter', + type: 'address', + }, + { + internalType: 'uint256', + name: '_load_chainId', + type: 'uint256', + }, + ], + name: 'Ballot_get_votingPower', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_electionId', + type: 'uint256', + }, + { + internalType: 'address', + name: '_load_voter', + type: 'address', + }, + { + internalType: 'uint256', + name: '_load_chainId', + type: 'uint256', + }, + ], + name: 'Ballot_hasVoted', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_electionId', + type: 'uint256', + }, + { + internalType: 'address', + name: '_load_voter', + type: 'address', + }, + { + internalType: 'uint256', + name: '_load_chainId', + type: 'uint256', + }, + ], + name: 'Ballot_isValid', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_electionId', + type: 'uint256', + }, + { + internalType: 'address', + name: '_load_voter', + type: 'address', + }, + { + internalType: 'uint256', + name: '_load_chainId', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'idx', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'val', + type: 'uint256', + }, + ], + name: 'Ballot_set_amounts', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_electionId', + type: 'uint256', + }, + { + internalType: 'address', + name: '_load_voter', + type: 'address', + }, + { + internalType: 'uint256', + name: '_load_chainId', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'idx', + type: 'uint256', + }, + { + internalType: 'address', + name: 'val', + type: 'address', + }, + ], + name: 'Ballot_set_votedCandidates', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_electionId', + type: 'uint256', + }, + { + internalType: 'address', + name: '_load_voter', + type: 'address', + }, + { + internalType: 'uint256', + name: '_load_chainId', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'val', + type: 'uint256', + }, + ], + name: 'Ballot_set_votingPower', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_electionId', + type: 'uint256', + }, + { + internalType: 'address', + name: '_load_voter', + type: 'address', + }, + { + internalType: 'uint256', + name: '_load_chainId', + type: 'uint256', + }, + ], + name: 'Ballot_validate', + outputs: [], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_load_snapshotContract', + type: 'address', + }, + { + internalType: 'uint256', + name: 'ballotBalance', + type: 'uint256', + }, + ], + name: 'SnapshotVotePower_calculateVotingPower', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_load_snapshotContract', + type: 'address', + }, + ], + name: 'SnapshotVotePower_get_enabled', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_load_snapshotContract', + type: 'address', + }, + ], + name: 'SnapshotVotePower_get_scale', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_load_snapshotContract', + type: 'address', + }, + { + internalType: 'bool', + name: 'val', + type: 'bool', + }, + ], + name: 'SnapshotVotePower_set_enabled', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_load_snapshotContract', + type: 'address', + }, + { + internalType: 'uint256', + name: 'val', + type: 'uint256', + }, + ], + name: 'SnapshotVotePower_set_scale', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + ], + name: 'ElectionSettings_get_epochDuration', + outputs: [ + { + internalType: 'uint64', + name: '', + type: 'uint64', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + ], + name: 'ElectionSettings_get_epochSeatCount', + outputs: [ + { + internalType: 'uint8', + name: '', + type: 'uint8', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + ], + name: 'ElectionSettings_get_maxDateAdjustmentTolerance', + outputs: [ + { + internalType: 'uint64', + name: '', + type: 'uint64', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + ], + name: 'ElectionSettings_get_minimumActiveMembers', + outputs: [ + { + internalType: 'uint8', + name: '', + type: 'uint8', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + ], + name: 'ElectionSettings_get_nominationPeriodDuration', + outputs: [ + { + internalType: 'uint64', + name: '', + type: 'uint64', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + ], + name: 'ElectionSettings_get_votingPeriodDuration', + outputs: [ + { + internalType: 'uint64', + name: '', + type: 'uint64', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + ], + name: 'ElectionSettings_minimumElectionPeriodDuration', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + { + internalType: 'uint8', + name: 'epochSeatCount', + type: 'uint8', + }, + { + internalType: 'uint8', + name: 'minimumActiveMembers', + type: 'uint8', + }, + { + internalType: 'uint64', + name: 'epochDuration', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'nominationPeriodDuration', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'votingPeriodDuration', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'maxDateAdjustmentTolerance', + type: 'uint64', + }, + ], + name: 'ElectionSettings_setElectionSettings', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + { + internalType: 'uint64', + name: 'val', + type: 'uint64', + }, + ], + name: 'ElectionSettings_set_epochDuration', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + { + internalType: 'uint8', + name: 'val', + type: 'uint8', + }, + ], + name: 'ElectionSettings_set_epochSeatCount', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + { + internalType: 'uint64', + name: 'val', + type: 'uint64', + }, + ], + name: 'ElectionSettings_set_maxDateAdjustmentTolerance', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + { + internalType: 'uint8', + name: 'val', + type: 'uint8', + }, + ], + name: 'ElectionSettings_set_minimumActiveMembers', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + { + internalType: 'uint64', + name: 'val', + type: 'uint64', + }, + ], + name: 'ElectionSettings_set_nominationPeriodDuration', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + { + internalType: 'uint64', + name: 'val', + type: 'uint64', + }, + ], + name: 'ElectionSettings_set_votingPeriodDuration', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + ], + name: 'ElectionSettings_validate', + outputs: [], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + { + internalType: 'address', + name: 'idx', + type: 'address', + }, + ], + name: 'Election_get_candidateVoteTotals', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + ], + name: 'Election_get_evaluated', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + ], + name: 'Election_get_numEvaluatedBallots', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + { + internalType: 'address', + name: 'idx', + type: 'address', + }, + { + internalType: 'uint256', + name: 'val', + type: 'uint256', + }, + ], + name: 'Election_set_candidateVoteTotals', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + { + internalType: 'bool', + name: 'val', + type: 'bool', + }, + ], + name: 'Election_set_evaluated', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'val', + type: 'uint256', + }, + ], + name: 'Election_set_numEvaluatedBallots', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + ], + name: 'Epoch_getCurrentPeriod', + outputs: [ + { + internalType: 'enum Epoch.ElectionPeriod', + name: '', + type: 'uint8', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + ], + name: 'Epoch_get_endDate', + outputs: [ + { + internalType: 'uint64', + name: '', + type: 'uint64', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + ], + name: 'Epoch_get_nominationPeriodStartDate', + outputs: [ + { + internalType: 'uint64', + name: '', + type: 'uint64', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + ], + name: 'Epoch_get_startDate', + outputs: [ + { + internalType: 'uint64', + name: '', + type: 'uint64', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + ], + name: 'Epoch_get_votingPeriodStartDate', + outputs: [ + { + internalType: 'uint64', + name: '', + type: 'uint64', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + { + internalType: 'uint64', + name: 'startDate', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'nominationPeriodStartDate', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'votingPeriodStartDate', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'endDate', + type: 'uint64', + }, + ], + name: 'Epoch_setEpochDates', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + { + internalType: 'uint64', + name: 'val', + type: 'uint64', + }, + ], + name: 'Epoch_set_endDate', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + { + internalType: 'uint64', + name: 'val', + type: 'uint64', + }, + ], + name: 'Epoch_set_nominationPeriodStartDate', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + { + internalType: 'uint64', + name: 'val', + type: 'uint64', + }, + ], + name: 'Epoch_set_startDate', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_load_epochIndex', + type: 'uint256', + }, + { + internalType: 'uint64', + name: 'val', + type: 'uint64', + }, + ], + name: 'Epoch_set_votingPeriodStartDate', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +]; diff --git a/governance/ui/src/utils/contracts.ts b/governance/ui/src/utils/contracts.ts index 23a3e9b4b..62dea00a7 100644 --- a/governance/ui/src/utils/contracts.ts +++ b/governance/ui/src/utils/contracts.ts @@ -1,5 +1,5 @@ import { Contract } from 'ethers'; -import { electionModuleABITest, profileAbi } from './abi'; +import { electionModuleABI, electionModuleABITest, profileAbi } from './abi'; import { CouncilSlugs } from './councils'; export const isMotherchain = (chainId?: string | number) => { @@ -12,26 +12,36 @@ export const isMotherchain = (chainId?: string | number) => { const SpartanCouncilContract = new Contract( '0x2082d5A6f6F17F5e421FD6508b867D794472A42a', - electionModuleABITest + electionModuleABI ); const AmbassadorCouncilContract = new Contract( '0x93D3A11B8403C2140D0d1f1c0460601e4FBB52DE', - electionModuleABITest + electionModuleABI ); const TreasuryCouncilContract = new Contract( '0xECfA1d4B17AaCD691173b6194C3ade361ef38367', - electionModuleABITest + electionModuleABI ); export function getCouncilContract(council: CouncilSlugs) { switch (council) { - case 'spartan': + case 'spartan': { + if (process.env.CI === 'true') { + return new Contract('0xBC85F11300A8EF619592fD678418Ec4eF26FBdFD', electionModuleABITest); + } return SpartanCouncilContract; + } case 'ambassador': + if (process.env.CI === 'true') { + return new Contract('0xCdbEf5753cE3CEbF361e143117e345ADd7498F80', electionModuleABITest); + } return AmbassadorCouncilContract; case 'treasury': + if (process.env.CI === 'true') { + return new Contract('0xe3aB2C6F1C9E46Fb53eD6b297c6fff68e935B161', electionModuleABITest); + } return TreasuryCouncilContract; default: throw new Error('could not find contract'); @@ -40,14 +50,30 @@ export function getCouncilContract(council: CouncilSlugs) { export const SnapshotRecordContract = (chainId: number, council: CouncilSlugs) => { switch (chainId) { + case 1: { + switch (council) { + case 'spartan': + case 'ambassador': + case 'treasury': + return new Contract('0x89FCb32F29e509cc42d0C8b6f058C993013A843F', abiForSnapshotMock); + } + } case 10: { switch (council) { case 'spartan': - return new Contract('0x7EBF54FD78Ced402c200A6C3c1A10506B83Fb419', abiForSnapshotMock); case 'ambassador': - return new Contract('0x7EBF54FD78Ced402c200A6C3c1A10506B83Fb419', abiForSnapshotMock); case 'treasury': - return new Contract('0x7EBF54FD78Ced402c200A6C3c1A10506B83Fb419', abiForSnapshotMock); + return new Contract('0x45c55BF488D3Cb8640f12F63CbeDC027E8261E79', abiForSnapshotMock); + } + } + case 13001: { + switch (council) { + case 'spartan': + return new Contract('0x552E469B7C88cd501C08e7759d35dC58f08C9648', abiForSnapshotMock); + case 'ambassador': + return new Contract('0x3a0186E03137B9b971EC911350A0F2D88D24FDF2', abiForSnapshotMock); + case 'treasury': + return new Contract('0xC0bFA9aC792cF691734F7b2BD252d1c2B9fBa343', abiForSnapshotMock); } } } diff --git a/governance/ui/src/utils/localstorage.ts b/governance/ui/src/utils/localstorage.ts index fe1f34920..039fb5158 100644 --- a/governance/ui/src/utils/localstorage.ts +++ b/governance/ui/src/utils/localstorage.ts @@ -33,10 +33,8 @@ export const removeCandidate = (council?: CouncilSlugs, network?: string, epochI if (!council || !network || !epochId) return; try { - const key = 'yourStorageKey'; // Replace with your actual key - const parsedSelection = JSON.parse(localStorage.getItem(key) || '{}'); + const parsedSelection = JSON.parse(localStorage.getItem(localStorageKey) || '{}'); - // Ensure the nested structure exists before attempting to delete if ( parsedSelection[epochId] && parsedSelection[epochId][network] && @@ -44,7 +42,6 @@ export const removeCandidate = (council?: CouncilSlugs, network?: string, epochI ) { delete parsedSelection[epochId][network][council]; - // If the object becomes empty after deletion, consider removing the empty objects if (Object.keys(parsedSelection[epochId][network]).length === 0) { delete parsedSelection[epochId][network]; } @@ -52,7 +49,7 @@ export const removeCandidate = (council?: CouncilSlugs, network?: string, epochI delete parsedSelection[epochId]; } - localStorage.setItem(key, JSON.stringify(parsedSelection)); + localStorage.setItem(localStorageKey, JSON.stringify(parsedSelection)); } } catch (err) { console.error( diff --git a/governance/ui/src/utils/math.ts b/governance/ui/src/utils/math.ts deleted file mode 100644 index 498650638..000000000 --- a/governance/ui/src/utils/math.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { BigNumber } from 'ethers'; - -export function sqrt(value?: BigNumber): BigNumber { - if (!value) return BigNumber.from(0); - if (value.isZero()) { - return BigNumber.from(0); - } - - let z: BigNumber = value; - let x: BigNumber = value.add(BigNumber.from(1)).div(BigNumber.from(2)); - - while (x.lt(z)) { - z = x; - x = value.div(x).add(x).div(BigNumber.from(2)); - } - - return z; -} diff --git a/governance/ui/src/utils/providers.ts b/governance/ui/src/utils/providers.ts index a01b65a0f..a03846086 100644 --- a/governance/ui/src/utils/providers.ts +++ b/governance/ui/src/utils/providers.ts @@ -2,6 +2,10 @@ import { providers } from 'ethers'; export const motherShipProvider = (networkId?: number) => { return new providers.JsonRpcProvider( - networkId === 13001 ? 'https://testnet.snaxchain.io/' : 'https://mainnet.snaxchain.io/' + process.env.CI === 'true' + ? 'http://127.0.0.1:8545' + : networkId === 13001 + ? 'https://testnet.snaxchain.io/' + : 'https://mainnet.snaxchain.io/' ); }; diff --git a/governance/ui/webpack.config.js b/governance/ui/webpack.config.js index fb0d85f76..9e7265de9 100644 --- a/governance/ui/webpack.config.js +++ b/governance/ui/webpack.config.js @@ -174,11 +174,12 @@ module.exports = { .concat( new webpack.DefinePlugin({ 'process.env.WC_PROJECT_ID': JSON.stringify( - process.env.WC_PROJECT_ID || '824270fbfdf10d95099e9702d3cb3741' + process.env.WC_PROJECT_ID ?? '824270fbfdf10d95099e9702d3cb3741' ), 'process.env.BOARDROOM_KEY': JSON.stringify( - process.env.BOARDROOM_KEY || 'd9abe7a1ab45ace58e6bd91bb9771586' + process.env.BOARDROOM_KEY ?? 'd9abe7a1ab45ace58e6bd91bb9771586' ), + 'process.env.CI': JSON.stringify(process.env.CI ?? false), }) ), resolve: {