diff --git a/apps/govern/common-util/functions/requests.ts b/apps/govern/common-util/functions/requests.ts index a9c27cc7..228b6cc5 100644 --- a/apps/govern/common-util/functions/requests.ts +++ b/apps/govern/common-util/functions/requests.ts @@ -12,7 +12,7 @@ import { SUPPORTED_CHAINS, wagmiConfig } from 'common-util/config/wagmi'; import { RPC_URLS } from 'common-util/constants/rpcs'; import { getAddressFromBytes32 } from './addresses'; -import { getStartOfNextWeekTimestamp } from './time'; +import { getUnixNextWeekStartTimestamp } from './time'; import { getVoteWeightingContract } from './web3'; type VoteForNomineeWeightsParams = { @@ -109,7 +109,7 @@ export const checkLockExpired = async (account: Address) => { args: [account], }); - const nextWeek = getStartOfNextWeekTimestamp(); + const nextWeek = getUnixNextWeekStartTimestamp(); return result ? nextWeek >= (result as number) : false; }; diff --git a/apps/govern/common-util/functions/time.ts b/apps/govern/common-util/functions/time.ts index 4838358f..b2629f4b 100644 --- a/apps/govern/common-util/functions/time.ts +++ b/apps/govern/common-util/functions/time.ts @@ -1,23 +1,27 @@ -export const getStartOfNextWeekTimestamp = () => { - const date = new Date(); - const dayOfWeek = date.getDay(); - const daysUntilNextWeek = dayOfWeek === 1 ? 7 : (8 - dayOfWeek) % 7; +// Returns the closest Thursday in the future +// which is the start of the next week by Unix time +export const getUnixNextWeekStartTimestamp = () => { + const now = new Date(); + const dayOfWeek = now.getDay(); + const daysUntilNextThursday = (4 - dayOfWeek + 7) % 7; - const nextWeekStartDate = new Date(date); - nextWeekStartDate.setDate(date.getDate() + daysUntilNextWeek); - nextWeekStartDate.setHours(0, 0, 0, 0); + const result = new Date(now); + result.setDate(now.getDate() + daysUntilNextThursday); + result.setHours(0, 0, 0, 0); - return nextWeekStartDate.getTime() / 1000; + return result.getTime() / 1000; }; -export const getThisWeekMondayTimestamp = () => { +// Returns the closest Thursday in the past +// which is the start of the current week by Unix time +export const getUnixWeekStartTimestamp = () => { const now = new Date(); const dayOfWeek = now.getDay(); - const daysToMonday = (dayOfWeek + 6) % 7; - const monday = new Date(now); + const daysSinceThursday = ((dayOfWeek + 2) % 7) + 1; + const result = new Date(now); - monday.setDate(now.getDate() - daysToMonday); - monday.setHours(0, 0, 0, 0); + result.setDate(now.getDate() - daysSinceThursday); + result.setHours(0, 0, 0, 0); - return monday.getTime() / 1000; + return result.getTime() / 1000; }; diff --git a/apps/govern/components/Contracts/ContractsList.spec.tsx b/apps/govern/components/Contracts/ContractsList.spec.tsx index 13fab168..69a0766c 100644 --- a/apps/govern/components/Contracts/ContractsList.spec.tsx +++ b/apps/govern/components/Contracts/ContractsList.spec.tsx @@ -89,11 +89,11 @@ describe('', () => { // current weight column expect(screen.getByText(/10.12%/)).toBeInTheDocument(); - expect(screen.getByText(/298.8k veOlas/)).toBeInTheDocument(); + expect(screen.getByText(/298.8k veOLAS/)).toBeInTheDocument(); // next weight column expect(screen.getByText(/25.56%/)).toBeInTheDocument(); - expect(screen.getByText(/297.4k veOlas/)).toBeInTheDocument(); + expect(screen.getByText(/297.4k veOLAS/)).toBeInTheDocument(); }); describe('Already voted', () => { diff --git a/apps/govern/components/Contracts/ContractsList.tsx b/apps/govern/components/Contracts/ContractsList.tsx index e4082b66..5e7870b2 100644 --- a/apps/govern/components/Contracts/ContractsList.tsx +++ b/apps/govern/components/Contracts/ContractsList.tsx @@ -1,5 +1,5 @@ import { CheckOutlined, InfoCircleOutlined, PlusOutlined } from '@ant-design/icons'; -import { Button, Card as CardAntd, Space, Table, Tooltip, Typography } from 'antd'; +import { Button, Card as CardAntd, Space, Table, Typography } from 'antd'; import { ColumnsType } from 'antd/es/table'; import styled from 'styled-components'; import { Allocation, StakingContract } from 'types'; @@ -9,20 +9,12 @@ import { COLOR } from 'libs/ui-theme/src'; import { CHAIN_NAMES } from 'libs/util-constants/src'; import { formatWeiBalance } from 'common-util/functions/balance'; +import { NextWeekTooltip } from 'components/NextWeekTooltip'; import { useVotingPower } from 'hooks/useVotingPower'; import { useAppSelector } from 'store/index'; const { Title, Paragraph, Text } = Typography; -const NextWeekTitle = () => ( - Updated voting weights will take effect at the start of next week} - > - {"Next week's weight"} - -); - const Card = styled(CardAntd)` flex: auto; `; @@ -68,18 +60,23 @@ const getColumns = ({ render: (currentWeight) => ( {`${currentWeight?.percentage.toFixed(2)}%`} - {`${formatWeiBalance(currentWeight?.value)} veOlas`} + {`${formatWeiBalance(currentWeight?.value)} veOLAS`} ), }, { - title: , + title: ( + + Next week's weight + + + ), key: 'nextWeight', dataIndex: 'nextWeight', render: (nextWeight) => ( {`${nextWeight?.percentage.toFixed(2)}%`} - {`${formatWeiBalance(nextWeight?.value)} veOlas`} + {`${formatWeiBalance(nextWeight?.value)} veOLAS`} ), }, diff --git a/apps/govern/components/Contracts/MyVotingWeight/MyVotingWeight.tsx b/apps/govern/components/Contracts/MyVotingWeight/MyVotingWeight.tsx index 87ea385d..ebcb9b5d 100644 --- a/apps/govern/components/Contracts/MyVotingWeight/MyVotingWeight.tsx +++ b/apps/govern/components/Contracts/MyVotingWeight/MyVotingWeight.tsx @@ -109,7 +109,7 @@ export const MyVotingWeight = ({ return ; } - // If the user doesn't have voting power, suggest to get veOlas + // If the user doesn't have voting power, suggest to get veOLAS if (Number(votingPower) === 0) { return ; } diff --git a/apps/govern/components/Contracts/MyVotingWeight/Votes.tsx b/apps/govern/components/Contracts/MyVotingWeight/Votes.tsx index e85d968f..a0389197 100644 --- a/apps/govern/components/Contracts/MyVotingWeight/Votes.tsx +++ b/apps/govern/components/Contracts/MyVotingWeight/Votes.tsx @@ -10,6 +10,7 @@ import { CHAIN_NAMES } from 'libs/util-constants/src'; import { RETAINER_ADDRESS } from 'common-util/constants/addresses'; import { getBytes32FromAddress } from 'common-util/functions/addresses'; +import { NextWeekTooltip } from 'components/NextWeekTooltip'; import { useAppSelector } from 'store/index'; const ONE_DAY_IN_MS = 24 * 60 * 60 * 1000; @@ -77,12 +78,9 @@ const columns: ColumnsType = [ }, { title: ( - Updated voting weights will take effect at the start of next week} - > + My updated weight - + ), key: 'nextWeight', dataIndex: 'nextWeight', @@ -156,7 +154,10 @@ export const Votes = ({ setIsUpdating, setAllocations }: VotesProps) => { isRetainer: true, }); } - return res; + return res.sort((item) => + // put Rollover address at the end + item.address === getBytes32FromAddress(RETAINER_ADDRESS) ? 1 : -1, + ); }, []); }, [userVotes, stakingContracts]); diff --git a/apps/govern/components/NextWeekTooltip.tsx b/apps/govern/components/NextWeekTooltip.tsx new file mode 100644 index 00000000..60f5cc55 --- /dev/null +++ b/apps/govern/components/NextWeekTooltip.tsx @@ -0,0 +1,36 @@ +import { Space, Tooltip, Typography } from 'antd'; +import { ReactNode } from 'react'; +import { mainnet } from 'viem/chains'; + +import { COLOR } from 'libs/ui-theme/src'; +import { UNICODE_SYMBOLS } from 'libs/util-constants/src'; +import { VOTE_WEIGHTING } from 'libs/util-contracts/src/lib/abiAndAddresses'; + +const { Text } = Typography; + +const TOOLTIP_STYLE = { maxWidth: '350px' }; + +export const NextWeekTooltip = ({ children }: { children: ReactNode }) => { + return ( + + Updated voting weights will take effect at the start of next week Unix time. + + {`View timestamp when the last votes apply ${UNICODE_SYMBOLS.EXTERNAL_LINK}`} + + + } + > + {children} + + ); +}; diff --git a/apps/govern/hooks/useFetchStakingContractsList.ts b/apps/govern/hooks/useFetchStakingContractsList.ts index 42307b42..df8d80da 100644 --- a/apps/govern/hooks/useFetchStakingContractsList.ts +++ b/apps/govern/hooks/useFetchStakingContractsList.ts @@ -1,11 +1,14 @@ import { useEffect } from 'react'; import { StakingContract } from 'types'; -import { useBlock } from 'wagmi'; +import { Address } from 'viem'; +import { mainnet } from 'viem/chains'; +import { useReadContract } from 'wagmi'; + +import { VOTE_WEIGHTING } from 'libs/util-contracts/src/lib/abiAndAddresses'; import { RETAINER_ADDRESS } from 'common-util/constants/addresses'; -import { LATEST_BLOCK_KEY, NEXT_RELATIVE_WEIGHTS_KEY } from 'common-util/constants/scopeKeys'; +import { NEXT_RELATIVE_WEIGHTS_KEY } from 'common-util/constants/scopeKeys'; import { getBytes32FromAddress } from 'common-util/functions'; -import { getStartOfNextWeekTimestamp } from 'common-util/functions/time'; import { setStakingContracts } from 'store/govern'; import { useAppDispatch, useAppSelector } from 'store/index'; @@ -13,6 +16,15 @@ import { useNominees } from './useNominees'; import { useNomineesMetadata } from './useNomineesMetadata'; import { useNomineesWeights } from './useNomineesWeights'; +const WEEK_IN_SECONDS = 604_800; + +const getCurrentWeightTimestamp = (timeSum: number | undefined) => { + if (!timeSum) return null; + // If timeSum is in the future, subtract a week from it + if (timeSum * 1000 > Date.now()) return timeSum - WEEK_IN_SECONDS; + return timeSum; +}; + export const useFetchStakingContractsList = () => { const dispatch = useAppDispatch(); const { stakingContracts } = useAppSelector((state) => state.govern); @@ -20,17 +32,28 @@ export const useFetchStakingContractsList = () => { // Get nominees list const { data: nominees } = useNominees(); - const { data: block } = useBlock({ blockTag: 'latest', scopeKey: LATEST_BLOCK_KEY }); - const now = block ? Number(block.timestamp) : null; - const nextWeek = block ? getStartOfNextWeekTimestamp() : null; + // Get last scheduled time (next week) from the contract + // Has the timestamp when the last votes should be applied + const { data: timeSum } = useReadContract({ + address: (VOTE_WEIGHTING.addresses as Record)[mainnet.id], + abi: VOTE_WEIGHTING.abi, + chainId: mainnet.id, + functionName: 'timeSum', + query: { + select: (data) => Number(data), + }, + }); - // Get contracts current weight - const { data: currentWeight } = useNomineesWeights(nominees || [], now); + // Get contracts current week weights + const { data: currentWeight } = useNomineesWeights( + nominees || [], + getCurrentWeightTimestamp(timeSum), + ); - // Get contracts next weight + // Get contracts next week weights const { data: nextWeight } = useNomineesWeights( nominees || [], - nextWeek, + timeSum || null, NEXT_RELATIVE_WEIGHTS_KEY, ); diff --git a/apps/govern/hooks/useFetchUserVotes.ts b/apps/govern/hooks/useFetchUserVotes.ts index 584cd247..39a5901d 100644 --- a/apps/govern/hooks/useFetchUserVotes.ts +++ b/apps/govern/hooks/useFetchUserVotes.ts @@ -3,7 +3,7 @@ import { UserVotes } from 'types'; import { useAccount, useBlock } from 'wagmi'; import { LATEST_BLOCK_KEY, NEXT_USERS_SLOPES_KEY } from 'common-util/constants/scopeKeys'; -import { getThisWeekMondayTimestamp } from 'common-util/functions/time'; +import { getUnixWeekStartTimestamp } from 'common-util/functions/time'; import { setLastUserVote, setUserVotes } from 'store/govern'; import { useAppDispatch, useAppSelector } from 'store/index'; @@ -16,12 +16,13 @@ import { useVoteUserSlopes } from './useVoteUserSlopes'; const SECONDS_PER_BLOCK = 12; const CONTRACT_DEPLOY_BLOCK = 20312875; -// Current votes are those that have been applied at the start of the week +// Current votes are those that have been applied at the start of the week (unix time) const getCurrentVotesBlock = (blockNumber: bigint, blockTimestamp: bigint) => { const mondayBlock = Number(blockNumber) - - // Approx number of blocks between current timestamp and this Monday - Math.round((Number(blockTimestamp) - getThisWeekMondayTimestamp()) / SECONDS_PER_BLOCK); + // Approximate number of blocks between the current timestamp and + // the start of the current week by Unix time + Math.round((Number(blockTimestamp) - getUnixWeekStartTimestamp()) / SECONDS_PER_BLOCK); return BigInt(Math.max(CONTRACT_DEPLOY_BLOCK, mondayBlock)); }; diff --git a/libs/common-middleware/src/lib/cspHeader.ts b/libs/common-middleware/src/lib/cspHeader.ts index 44b2a302..0b349f1f 100644 --- a/libs/common-middleware/src/lib/cspHeader.ts +++ b/libs/common-middleware/src/lib/cspHeader.ts @@ -64,6 +64,9 @@ const ALLOWED_ORIGINS = [ // tenderly 'https://virtual.mainnet.rpc.tenderly.co/', + 'https://virtual.gnosis.rpc.tenderly.co/', + 'https://virtual.polygon.rpc.tenderly.co/', + 'https://rpc.tenderly.co/fork/', // others 'https://api.thegraph.com/',