From 8dd062cb4824b22a7c910ec4353248d06cc24725 Mon Sep 17 00:00:00 2001 From: James Russell Orola Date: Fri, 9 Jul 2021 21:14:10 +0800 Subject: [PATCH 1/6] Isolate pendingRewardToken calls from Updater() to UI still works even if 1 call fails Add underline to earned HALO if the underlying call fails --- src/components/Farm/FarmPoolCard.tsx | 13 ++++++-- src/constants/pools.ts | 2 ++ src/halo-hooks/useFarmSummary.ts | 4 ++- src/halo-hooks/useHaloHalo.ts | 1 - src/halo-hooks/useRewards.ts | 45 +++++++++++++++++----------- 5 files changed, 44 insertions(+), 21 deletions(-) diff --git a/src/components/Farm/FarmPoolCard.tsx b/src/components/Farm/FarmPoolCard.tsx index f56fdc41..6d4a8605 100644 --- a/src/components/Farm/FarmPoolCard.tsx +++ b/src/components/Farm/FarmPoolCard.tsx @@ -50,6 +50,7 @@ import { useDispatch } from 'react-redux' import { AppDispatch } from 'state' import useHaloHalo from 'halo-hooks/useHaloHalo' import { tokenSymbolForPool } from 'utils/poolInfo' +import { PENDING_REWARD_FAILED } from 'constants/pools' const StyledFixedHeightRowCustom = styled(FixedHeightRow)` padding: 1rem; @@ -452,7 +453,9 @@ export default function FarmPoolCard({ poolInfo, tokenPrice }: FarmPoolCardProps // Get user earned HALO const unclaimedRewards = useUnclaimedRewardsPerPool([poolInfo.pid]) - const unclaimedPoolRewards = unclaimedRewards[poolInfo.pid] ?? 0 + let unclaimedPoolRewards = unclaimedRewards[poolInfo.pid] ?? 0 + const hasPendingRewardTokenError = unclaimedPoolRewards === PENDING_REWARD_FAILED + unclaimedPoolRewards = hasPendingRewardTokenError ? 0 : unclaimedPoolRewards const unclaimedHALO = unclaimedPoolRewards * rewardsToHALOPrice // Make use of `useApproveCallback` for checking & setting allowance @@ -654,7 +657,13 @@ export default function FarmPoolCard({ poolInfo, tokenPrice }: FarmPoolCardProps {t('earned')}: - {formatNumber(unclaimedHALO)} RNBW + + {hasPendingRewardTokenError ? ( + {formatNumber(unclaimedHALO)} RNBW + ) : ( + <>{formatNumber(unclaimedHALO)} RNBW + )} + {account && ( diff --git a/src/constants/pools.ts b/src/constants/pools.ts index f60fac2e..895c5261 100644 --- a/src/constants/pools.ts +++ b/src/constants/pools.ts @@ -51,3 +51,5 @@ rawLpTokenPoolMapList.forEach(rawLpTokenPool => { }) export const BALANCER_LPTOKEN_POOL_MAP = lpTokenPoolMap + +export const PENDING_REWARD_FAILED = -99999 diff --git a/src/halo-hooks/useFarmSummary.ts b/src/halo-hooks/useFarmSummary.ts index be56ae24..64d7a040 100644 --- a/src/halo-hooks/useFarmSummary.ts +++ b/src/halo-hooks/useFarmSummary.ts @@ -8,6 +8,7 @@ import { useUnclaimedRewardsPerPool, useStakedBPTPerPool } from './useRewards' import useHaloHalo from './useHaloHalo' import { getPoolLiquidity } from 'utils/poolInfo' import { TokenPrice } from './useTokenPrice' +import { PENDING_REWARD_FAILED } from 'constants/pools' const useFarmSummary = (poolsInfo: PoolInfo[], tokenPrice: TokenPrice) => { // Get user balance for each pool @@ -47,7 +48,8 @@ const useFarmSummary = (poolsInfo: PoolInfo[], tokenPrice: TokenPrice) => { for (const poolInfo of poolsInfo) { // Add unclaimed HALO per pool to totalHALOEarned - const unclaimedPoolRewards = unclaimedRewards[poolInfo.pid] ?? 0 + let unclaimedPoolRewards = unclaimedRewards[poolInfo.pid] ?? 0 + unclaimedPoolRewards = unclaimedPoolRewards === PENDING_REWARD_FAILED ? 0 : unclaimedPoolRewards totalHALOEarned += unclaimedPoolRewards * rewardsToHALOPrice // Calculate LPToken price per pool diff --git a/src/halo-hooks/useHaloHalo.ts b/src/halo-hooks/useHaloHalo.ts index 42228b8d..a4fcfc87 100644 --- a/src/halo-hooks/useHaloHalo.ts +++ b/src/halo-hooks/useHaloHalo.ts @@ -37,7 +37,6 @@ const useHaloHalo = () => { // one year in seconds / 31536000 const timePriceChangedRatio = 31536000 / (currentTimestamp - genesisTimestamp) - console.log(`Genesis timestamp is ${genesisTimestamp}, current block timestamp is ${currentTimestamp}`) // 1 comes from the 1:1 ratio before adding rewards const priceChange = Number(formatEther(currentHaloHaloPrice)) - 1 diff --git a/src/halo-hooks/useRewards.ts b/src/halo-hooks/useRewards.ts index faa45372..96eb02a9 100644 --- a/src/halo-hooks/useRewards.ts +++ b/src/halo-hooks/useRewards.ts @@ -1,5 +1,5 @@ import { useHALORewardsContract } from 'hooks/useContract' -import { useCallback, useMemo } from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' import { useSingleCallResult, useSingleContractMultipleData } from 'state/multicall/hooks' import { formatEther } from 'ethers/lib/utils' import { useTransactionAdder } from 'state/transactions/hooks' @@ -7,6 +7,7 @@ import { useActiveWeb3React } from 'hooks' import { tokenSymbolForPool } from 'utils/poolInfo' import { ZERO_ADDRESS } from '../constants' import { BigNumber } from 'ethers' +import { PENDING_REWARD_FAILED } from 'constants/pools' /** * Internal Methods @@ -61,24 +62,34 @@ export const useUnclaimedRewardsPerPool = (poolIds: number[]): { [poolId: number const { account } = useActiveWeb3React() const rewardsContract = useHALORewardsContract() - const args = useMemo(() => poolIds.map(poolId => [`${poolId}`, account ?? ZERO_ADDRESS]), [poolIds, account]) - const results = useSingleContractMultipleData(rewardsContract, 'pendingRewardToken', args) + const [unclaimedRewards, setUnclaimedRewards] = useState(() => { + const rewards: { [poolId: number]: number } = {} + for (const pid of poolIds) { + rewards[pid] = 0 + } + return rewards + }) + + const fetchPendingRewards = useCallback(async () => { + const newUnclaimedRewards = unclaimedRewards + for (const pid of poolIds) { + try { + const result = await rewardsContract?.pendingRewardToken(`${pid}`, account) + newUnclaimedRewards[pid] = parseFloat(formatEther(result.toString())) + } catch (err) { + console.error(`Error fetching pending rewards for pid[${pid}]: `, err) + newUnclaimedRewards[pid] = PENDING_REWARD_FAILED + } + } + setUnclaimedRewards(newUnclaimedRewards) + }, [poolIds, account, rewardsContract, unclaimedRewards]) - return useMemo( - () => - poolIds.length > 0 - ? poolIds.reduce<{ [poolId: number]: number }>((memo, poolId, i) => { - if (!results[i]) return memo + useEffect(() => { + if (!account) return + fetchPendingRewards() + }, [poolIds, account, fetchPendingRewards]) - const reward = results[i].result - if (reward) { - memo[poolId] = parseFloat(formatEther(reward.toString())) - } - return memo - }, {}) - : {}, - [poolIds, results] - ) + return unclaimedRewards } export const useStakedBPTPerPool = (poolIds: number[]): { [poolId: number]: number } => { From cc289e2db97563a0e93d69b85175f8685aa06c7d Mon Sep 17 00:00:00 2001 From: James Russell Orola Date: Sat, 10 Jul 2021 11:06:18 +0800 Subject: [PATCH 2/6] Feat: inactive pools --- .env.example | 1 + public/locales/en.json | 6 ++-- src/components/Farm/FarmPoolCard.tsx | 49 ++++++++++++++++----------- src/components/Farm/FarmPoolTable.tsx | 36 ++++++++++++++++++-- src/constants/pools.ts | 7 ++++ src/utils/poolInfo.ts | 24 ++++++++++++- 6 files changed, 98 insertions(+), 25 deletions(-) diff --git a/.env.example b/.env.example index 85238e19..15bdaa46 100644 --- a/.env.example +++ b/.env.example @@ -10,6 +10,7 @@ REACT_APP_BALANCER_POOLS_ADDRESSES=0x0,0x0 REACT_APP_UNI_POOLS_ADDRESSES=0x0,0x0 REACT_APP_SUSHI_POOLS_ADDRESSES=0x0,0x0 REACT_APP_BALANCER_LPTOKEN_POOL_MAP=0x0:0x0,0x0:0x0 +REACT_APP_DISABLED_POOLS=0x0,0x0 REACT_APP_HALO_TOKEN_ADDRESS_MAINNET= REACT_APP_HALO_REWARDS_ADDRESS_MAINNET= diff --git a/public/locales/en.json b/public/locales/en.json index dc3a7327..4ff4e83d 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -120,5 +120,7 @@ "emptyStateTitleInVest": "Let's get vesting!", "emptyStateSubTitleInFarm": "You’re one step away from earning. Connect your wallet to join the farm.", "emptyStateSubTitleInVest": "You’re one step away from earning. Connect your wallet to start to vest.", - "add": "Add" -} + "add": "Add", + "staking disabled": "Staking Disabled", + "inactive pools": "Inactive Pools" +} \ No newline at end of file diff --git a/src/components/Farm/FarmPoolCard.tsx b/src/components/Farm/FarmPoolCard.tsx index 826c4d00..bbade228 100644 --- a/src/components/Farm/FarmPoolCard.tsx +++ b/src/components/Farm/FarmPoolCard.tsx @@ -415,9 +415,10 @@ const ClaimButton = styled(ButtonOutlined)` interface FarmPoolCardProps { poolInfo: PoolInfo tokenPrice: TokenPrice + isActivePool: boolean } -export default function FarmPoolCard({ poolInfo, tokenPrice }: FarmPoolCardProps) { +export default function FarmPoolCard({ poolInfo, tokenPrice, isActivePool }: FarmPoolCardProps) { const { chainId, account } = useActiveWeb3React() const { t } = useTranslation() const dispatch = useDispatch() @@ -664,7 +665,9 @@ export default function FarmPoolCard({ poolInfo, tokenPrice }: FarmPoolCardProps setShowMore(!showMore)}>{t('closeTxt')} ) : ( - setShowMore(!showMore)}>{t('add')} + setShowMore(!showMore)}> + {isActivePool ? t('add') : t('manage')} + )} )} @@ -697,11 +700,13 @@ export default function FarmPoolCard({ poolInfo, tokenPrice }: FarmPoolCardProps value={stakeAmount} onUserInput={amount => setStakeAmount(amount)} id="stake-input" + disabled={!isActivePool} /> { setStakeAmount(`${toFixed(bptBalance, 8)}`) }} + disabled={!isActivePool} > {t('max')} @@ -709,11 +714,11 @@ export default function FarmPoolCard({ poolInfo, tokenPrice }: FarmPoolCardProps { if (stakeButtonState === ButtonHaloStates.Approved) { stakeLpToken() @@ -722,19 +727,25 @@ export default function FarmPoolCard({ poolInfo, tokenPrice }: FarmPoolCardProps } }} > - {(stakeButtonState === ButtonHaloStates.Disabled || - stakeButtonState === ButtonHaloStates.Approved) && <>{t('stake')}} - {stakeButtonState === ButtonHaloStates.NotApproved && <>{t('approve')}} - {stakeButtonState === ButtonHaloStates.Approving && ( + {!isActivePool ? ( + <>{t('staking disabled')} + ) : ( <> - {HALO_REWARDS_MESSAGE.approving}  - {' '} - - )} - {stakeButtonState === ButtonHaloStates.TxInProgress && ( - <> - {HALO_REWARDS_MESSAGE.staking}  - {' '} + {(stakeButtonState === ButtonHaloStates.Disabled || + stakeButtonState === ButtonHaloStates.Approved) && <>{t('stake')}} + {stakeButtonState === ButtonHaloStates.NotApproved && <>{t('approve')}} + {stakeButtonState === ButtonHaloStates.Approving && ( + <> + {HALO_REWARDS_MESSAGE.approving}  + {' '} + + )} + {stakeButtonState === ButtonHaloStates.TxInProgress && ( + <> + {HALO_REWARDS_MESSAGE.staking}  + {' '} + + )} )} diff --git a/src/components/Farm/FarmPoolTable.tsx b/src/components/Farm/FarmPoolTable.tsx index 2c805216..772a180a 100644 --- a/src/components/Farm/FarmPoolTable.tsx +++ b/src/components/Farm/FarmPoolTable.tsx @@ -8,6 +8,16 @@ import FarmPoolCard from 'components/Farm/FarmPoolCard' import Card from 'components/Card' import { PoolInfo } from 'halo-hooks/usePoolInfo' import { TokenPrice } from 'halo-hooks/useTokenPrice' +import { groupPoolsInfo } from 'utils/poolInfo' +import styled from 'styled-components' + +const InactivePools = styled.div` + margin-top: 1rem; + + .tbHeader { + margin-bottom: 0.5rem; + } +` interface FarmPoolTableProps { poolsInfo: PoolInfo[] @@ -16,8 +26,9 @@ interface FarmPoolTableProps { const FarmPoolTable = ({ poolsInfo, tokenPrice }: FarmPoolTableProps) => { const { t } = useTranslation() - const { account } = useWeb3React() + const { inactivePools, activePools } = groupPoolsInfo(poolsInfo) + if (account) { return ( <> @@ -58,9 +69,28 @@ const FarmPoolTable = ({ poolsInfo, tokenPrice }: FarmPoolTableProps) => { - {poolsInfo.map(poolInfo => { - return + + {activePools.map(poolInfo => { + return ( + + ) })} + + {inactivePools.length > 0 && ( + + {t('inactive pools')} + {inactivePools.map(poolInfo => { + return ( + + ) + })} + + )} ) diff --git a/src/constants/pools.ts b/src/constants/pools.ts index f60fac2e..7facd08e 100644 --- a/src/constants/pools.ts +++ b/src/constants/pools.ts @@ -51,3 +51,10 @@ rawLpTokenPoolMapList.forEach(rawLpTokenPool => { }) export const BALANCER_LPTOKEN_POOL_MAP = lpTokenPoolMap + +/** + * Inactive pools + */ + +const inactivePoolsRaw = process.env.REACT_APP_INACTIVE_POOLS || '' +export const INACTIVE_POOLS = inactivePoolsRaw.split(',').map(a => a.toLowerCase()) diff --git a/src/utils/poolInfo.ts b/src/utils/poolInfo.ts index 40da6a13..eed24ada 100644 --- a/src/utils/poolInfo.ts +++ b/src/utils/poolInfo.ts @@ -1,4 +1,4 @@ -import { BALANCER_POOLS_ADDRESSES, SUSHI_POOLS_ADDRESSES, UNI_POOLS_ADDRESSES } from 'constants/pools' +import { BALANCER_POOLS_ADDRESSES, INACTIVE_POOLS, SUSHI_POOLS_ADDRESSES, UNI_POOLS_ADDRESSES } from 'constants/pools' import { PoolInfo } from 'halo-hooks/usePoolInfo' import { TokenPrice } from 'halo-hooks/useTokenPrice' import { getAddress } from 'ethers/lib/utils' @@ -20,6 +20,10 @@ const isSushiPool = (address: string) => { return SUSHI_POOLS_ADDRESSES.includes(address.toLocaleLowerCase()) } +const isInactivePool = (address: string) => { + return INACTIVE_POOLS.includes(address.toLocaleLowerCase()) +} + export const groupByPoolProvider = (addresses: string[]) => { const balancerPools: PoolIdLpTokenMap[] = [] const uniPools: PoolIdLpTokenMap[] = [] @@ -77,3 +81,21 @@ export const getPoolLiquidity = (poolInfo: PoolInfo, tokenPrice: TokenPrice) => return poolInfo.liquidity } } + +export const groupPoolsInfo = (poolInfos: PoolInfo[]) => { + const activePools: PoolInfo[] = [] + const inactivePools: PoolInfo[] = [] + + for (const poolInfo of poolInfos) { + if (isInactivePool(poolInfo.asToken.address)) { + inactivePools.push(poolInfo) + } else { + activePools.push(poolInfo) + } + } + + return { + activePools, + inactivePools + } +} From 5a1637e4da1588f778692f6da05d4b2eb8f8fb8a Mon Sep 17 00:00:00 2001 From: Lint Action Date: Sat, 10 Jul 2021 03:12:48 +0000 Subject: [PATCH 3/6] Fix code style issues with ESLint --- public/locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/locales/en.json b/public/locales/en.json index 4ff4e83d..d6fb7870 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -123,4 +123,4 @@ "add": "Add", "staking disabled": "Staking Disabled", "inactive pools": "Inactive Pools" -} \ No newline at end of file +} From eef8cef709a5565e2a137b74b3c69bfd7429a0ce Mon Sep 17 00:00:00 2001 From: James Russell Orola Date: Sat, 10 Jul 2021 11:36:20 +0800 Subject: [PATCH 4/6] Show "Inactive" instead of "New" APY for inactive pools --- public/locales/en.json | 5 +++-- src/components/Farm/FarmPoolCard.tsx | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/public/locales/en.json b/public/locales/en.json index d6fb7870..fd60c343 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -122,5 +122,6 @@ "emptyStateSubTitleInVest": "You’re one step away from earning. Connect your wallet to start to vest.", "add": "Add", "staking disabled": "Staking Disabled", - "inactive pools": "Inactive Pools" -} + "inactive pools": "Inactive Pools", + "inactive": "Inactive" +} \ No newline at end of file diff --git a/src/components/Farm/FarmPoolCard.tsx b/src/components/Farm/FarmPoolCard.tsx index bbade228..5c2d949e 100644 --- a/src/components/Farm/FarmPoolCard.tsx +++ b/src/components/Farm/FarmPoolCard.tsx @@ -637,7 +637,7 @@ export default function FarmPoolCard({ poolInfo, tokenPrice, isActivePool }: Far {t('apy')}: - {poolAPY} + {isActivePool ? poolAPY : t('inactive')} {t('totalPoolValue')}: From d707cb4bf932bda8c2457615f47b2f2bfc734d77 Mon Sep 17 00:00:00 2001 From: Lint Action Date: Sat, 10 Jul 2021 03:37:44 +0000 Subject: [PATCH 5/6] Fix code style issues with ESLint --- public/locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/locales/en.json b/public/locales/en.json index fd60c343..729b710d 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -124,4 +124,4 @@ "staking disabled": "Staking Disabled", "inactive pools": "Inactive Pools", "inactive": "Inactive" -} \ No newline at end of file +} From 0128e3a1129ec24fbd22c59d5559dbff71c90670 Mon Sep 17 00:00:00 2001 From: James Russell Orola Date: Sat, 10 Jul 2021 14:37:34 +0800 Subject: [PATCH 6/6] Show RNBW earned in 2 decimal places for inactive pools --- src/components/Farm/FarmPoolCard.tsx | 8 +++++--- src/utils/formatNumber.ts | 8 +++++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/components/Farm/FarmPoolCard.tsx b/src/components/Farm/FarmPoolCard.tsx index f054ef0a..cc751406 100644 --- a/src/components/Farm/FarmPoolCard.tsx +++ b/src/components/Farm/FarmPoolCard.tsx @@ -660,9 +660,9 @@ export default function FarmPoolCard({ poolInfo, tokenPrice, isActivePool }: Far {t('earned')}: {hasPendingRewardTokenError ? ( - {formatNumber(unclaimedHALO)} RNBW + {formatNumber(unclaimedHALO, isActivePool ? undefined : NumberFormat.short)} RNBW ) : ( - <>{formatNumber(unclaimedHALO)} RNBW + <>{formatNumber(unclaimedHALO, isActivePool ? undefined : NumberFormat.short)} RNBW )} @@ -828,7 +828,9 @@ export default function FarmPoolCard({ poolInfo, tokenPrice, isActivePool }: Far {poolInfo.pair} Rewards: - {formatNumber(unclaimedHALO)} RNBW + + {formatNumber(unclaimedHALO, isActivePool ? undefined : NumberFormat.short)} RNBW + 1000) format = '0.[0]a' + } + if (key === NumberFormat.usd) { format = '$(0.[00])' if (number > 1000) format = '$(0.[0]a)'