From f55328afd09f6d817ce57996e140e655444204de Mon Sep 17 00:00:00 2001 From: Atatakai Date: Tue, 30 Jul 2024 20:37:02 +0400 Subject: [PATCH 1/2] (govern) fix: changes for next week start, veOlas -> veOLAS --- apps/govern/common-util/functions/requests.ts | 4 +- apps/govern/common-util/functions/time.ts | 32 ++++++++------- .../Contracts/ContractsList.spec.tsx | 4 +- .../components/Contracts/ContractsList.tsx | 23 +++++------ .../Contracts/EditVotes/validations.tsx | 6 +-- .../MyVotingWeight/MyVotingWeight.tsx | 2 +- .../Contracts/MyVotingWeight/Votes.tsx | 13 +++--- apps/govern/components/NextWeekTooltip.tsx | 36 +++++++++++++++++ .../hooks/useFetchStakingContractsList.ts | 40 ++++++++++++++----- apps/govern/hooks/useFetchUserVotes.ts | 9 +++-- libs/common-middleware/src/lib/cspHeader.ts | 3 ++ 11 files changed, 117 insertions(+), 55 deletions(-) create mode 100644 apps/govern/components/NextWeekTooltip.tsx 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..a74d93f0 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 nextThursday = new Date(now); + nextThursday.setDate(now.getDate() + daysUntilNextThursday); + nextThursday.setHours(0, 0, 0, 0); - return nextWeekStartDate.getTime() / 1000; + return nextThursday.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 thursday = new Date(now); - monday.setDate(now.getDate() - daysToMonday); - monday.setHours(0, 0, 0, 0); + thursday.setDate(now.getDate() - daysSinceThursday); + thursday.setHours(0, 0, 0, 0); - return monday.getTime() / 1000; + return thursday.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..326d7cd1 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/EditVotes/validations.tsx b/apps/govern/components/Contracts/EditVotes/validations.tsx index 1770e45b..fb8c1821 100644 --- a/apps/govern/components/Contracts/EditVotes/validations.tsx +++ b/apps/govern/components/Contracts/EditVotes/validations.tsx @@ -25,7 +25,7 @@ const getRemovedNomineesError = (removedNominees: Address[], allocations: Alloca ); -const NO_VEOLAS_ERROR = `You don't have enough veOLAS to vote`; +const NO_veOLAS_ERROR = `You don't have enough veOLAS to vote`; // Checks if any of the nominees were removed from voting export const checkNoRemovedNominees = async (allocations: Allocation[]) => { @@ -59,7 +59,7 @@ export const checkNoDisabledContracts = async (allocations: Allocation[]) => { export const checkNotNegativeSlope = async (account: Address) => { const isNegativeSlope = await checkNegativeSlope(account); if (isNegativeSlope) { - notification.error({ message: NO_VEOLAS_ERROR }); + notification.error({ message: NO_veOLAS_ERROR }); return false; } @@ -70,7 +70,7 @@ export const checkNotNegativeSlope = async (account: Address) => { export const checkLockNotExpired = async (account: Address) => { const isLockExpired = await checkLockExpired(account); if (isLockExpired) { - notification.error({ message: NO_VEOLAS_ERROR }); + notification.error({ message: NO_veOLAS_ERROR }); return false; } 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..aa4ecab4 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,8 @@ import { useNominees } from './useNominees'; import { useNomineesMetadata } from './useNomineesMetadata'; import { useNomineesWeights } from './useNomineesWeights'; +const WEEK = 604_800; + export const useFetchStakingContractsList = () => { const dispatch = useAppDispatch(); const { stakingContracts } = useAppSelector((state) => state.govern); @@ -20,17 +25,32 @@ 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 + 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 || [], + timeSum + ? // If timeSum is in the future, subtract a week from it + timeSum * 1000 > Date.now() + ? timeSum - WEEK + : timeSum + : null, + ); - // 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/', From 40ffd8257399913e5532529e744a487e263a7b5a Mon Sep 17 00:00:00 2001 From: Atatakai Date: Tue, 30 Jul 2024 21:23:57 +0400 Subject: [PATCH 2/2] Review fixes --- apps/govern/common-util/functions/time.ts | 16 ++++++++-------- .../components/Contracts/ContractsList.tsx | 2 +- .../Contracts/EditVotes/validations.tsx | 6 +++--- .../hooks/useFetchStakingContractsList.ts | 17 ++++++++++------- 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/apps/govern/common-util/functions/time.ts b/apps/govern/common-util/functions/time.ts index a74d93f0..b2629f4b 100644 --- a/apps/govern/common-util/functions/time.ts +++ b/apps/govern/common-util/functions/time.ts @@ -5,11 +5,11 @@ export const getUnixNextWeekStartTimestamp = () => { const dayOfWeek = now.getDay(); const daysUntilNextThursday = (4 - dayOfWeek + 7) % 7; - const nextThursday = new Date(now); - nextThursday.setDate(now.getDate() + daysUntilNextThursday); - nextThursday.setHours(0, 0, 0, 0); + const result = new Date(now); + result.setDate(now.getDate() + daysUntilNextThursday); + result.setHours(0, 0, 0, 0); - return nextThursday.getTime() / 1000; + return result.getTime() / 1000; }; // Returns the closest Thursday in the past @@ -18,10 +18,10 @@ export const getUnixWeekStartTimestamp = () => { const now = new Date(); const dayOfWeek = now.getDay(); const daysSinceThursday = ((dayOfWeek + 2) % 7) + 1; - const thursday = new Date(now); + const result = new Date(now); - thursday.setDate(now.getDate() - daysSinceThursday); - thursday.setHours(0, 0, 0, 0); + result.setDate(now.getDate() - daysSinceThursday); + result.setHours(0, 0, 0, 0); - return thursday.getTime() / 1000; + return result.getTime() / 1000; }; diff --git a/apps/govern/components/Contracts/ContractsList.tsx b/apps/govern/components/Contracts/ContractsList.tsx index 326d7cd1..5e7870b2 100644 --- a/apps/govern/components/Contracts/ContractsList.tsx +++ b/apps/govern/components/Contracts/ContractsList.tsx @@ -67,7 +67,7 @@ const getColumns = ({ { title: ( - {`Next week's weight`}{' '} + Next week's weight ), diff --git a/apps/govern/components/Contracts/EditVotes/validations.tsx b/apps/govern/components/Contracts/EditVotes/validations.tsx index fb8c1821..1770e45b 100644 --- a/apps/govern/components/Contracts/EditVotes/validations.tsx +++ b/apps/govern/components/Contracts/EditVotes/validations.tsx @@ -25,7 +25,7 @@ const getRemovedNomineesError = (removedNominees: Address[], allocations: Alloca ); -const NO_veOLAS_ERROR = `You don't have enough veOLAS to vote`; +const NO_VEOLAS_ERROR = `You don't have enough veOLAS to vote`; // Checks if any of the nominees were removed from voting export const checkNoRemovedNominees = async (allocations: Allocation[]) => { @@ -59,7 +59,7 @@ export const checkNoDisabledContracts = async (allocations: Allocation[]) => { export const checkNotNegativeSlope = async (account: Address) => { const isNegativeSlope = await checkNegativeSlope(account); if (isNegativeSlope) { - notification.error({ message: NO_veOLAS_ERROR }); + notification.error({ message: NO_VEOLAS_ERROR }); return false; } @@ -70,7 +70,7 @@ export const checkNotNegativeSlope = async (account: Address) => { export const checkLockNotExpired = async (account: Address) => { const isLockExpired = await checkLockExpired(account); if (isLockExpired) { - notification.error({ message: NO_veOLAS_ERROR }); + notification.error({ message: NO_VEOLAS_ERROR }); return false; } diff --git a/apps/govern/hooks/useFetchStakingContractsList.ts b/apps/govern/hooks/useFetchStakingContractsList.ts index aa4ecab4..df8d80da 100644 --- a/apps/govern/hooks/useFetchStakingContractsList.ts +++ b/apps/govern/hooks/useFetchStakingContractsList.ts @@ -16,7 +16,14 @@ import { useNominees } from './useNominees'; import { useNomineesMetadata } from './useNomineesMetadata'; import { useNomineesWeights } from './useNomineesWeights'; -const WEEK = 604_800; +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(); @@ -26,6 +33,7 @@ export const useFetchStakingContractsList = () => { const { data: nominees } = useNominees(); // 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, @@ -39,12 +47,7 @@ export const useFetchStakingContractsList = () => { // Get contracts current week weights const { data: currentWeight } = useNomineesWeights( nominees || [], - timeSum - ? // If timeSum is in the future, subtract a week from it - timeSum * 1000 > Date.now() - ? timeSum - WEEK - : timeSum - : null, + getCurrentWeightTimestamp(timeSum), ); // Get contracts next week weights