diff --git a/src/app/pages/RewardPage/components/FeesEarnedTab/index.tsx b/src/app/pages/RewardPage/components/FeesEarnedTab/index.tsx index ea660c819f..7259656da3 100644 --- a/src/app/pages/RewardPage/components/FeesEarnedTab/index.tsx +++ b/src/app/pages/RewardPage/components/FeesEarnedTab/index.tsx @@ -90,7 +90,7 @@ export const FeesEarnedTab: React.FC = ({ = ({ = ({ activeTab }) => { const getActiveTabType = useCallback(item => { switch (item) { case RewardTabType.FEES_EARNED: - return RewardsEarnedAction.UserFeeWithdrawn; + return [RewardsEarnedAction.UserFeeWithdrawn]; case RewardTabType.LIQUID_SOV: - return RewardsEarnedAction.StakingRewardWithdrawn; + return [RewardsEarnedAction.StakingRewardWithdrawn]; case RewardTabType.REWARD_SOV: - return RewardsEarnedAction.EarnReward; + return [ + RewardsEarnedAction.EarnReward, + RewardsEarnedAction.RewardClaimed, + ]; default: - return RewardsEarnedAction.EarnReward; + return [ + RewardsEarnedAction.EarnReward, + RewardsEarnedAction.RewardClaimed, + ]; } }, []); diff --git a/src/app/pages/RewardPage/components/LiquidTab/index.tsx b/src/app/pages/RewardPage/components/LiquidTab/index.tsx index 78362418cc..cc2be439a4 100644 --- a/src/app/pages/RewardPage/components/LiquidTab/index.tsx +++ b/src/app/pages/RewardPage/components/LiquidTab/index.tsx @@ -71,7 +71,7 @@ export const LiquidTab: React.FC = ({ = ({ diff --git a/src/app/pages/RewardPage/components/RewardTab/hooks/useGetAvailableLiquidityRewards.ts b/src/app/pages/RewardPage/components/RewardTab/hooks/useGetAvailableLiquidityRewards.ts index f3a1476651..2db3c480a7 100644 --- a/src/app/pages/RewardPage/components/RewardTab/hooks/useGetAvailableLiquidityRewards.ts +++ b/src/app/pages/RewardPage/components/RewardTab/hooks/useGetAvailableLiquidityRewards.ts @@ -1,14 +1,20 @@ import { useAccount } from 'app/hooks/useAccount'; -import { bridgeNetwork } from 'app/pages/BridgeDepositPage/utils/bridge-network'; import { bignumber } from 'mathjs'; -import { useEffect, useState } from 'react'; -import { Chain } from 'types'; -import { getContract } from 'utils/blockchain/contract-helpers'; -import { ethGenesisAddress } from 'utils/classifiers'; -import { LiquidityPoolDictionary } from 'utils/dictionaries/liquidity-pool-dictionary'; +import { useEffect, useMemo, useState } from 'react'; import { useCacheCallWithValue } from 'app/hooks/useCacheCallWithValue'; -export const useGetAvailableLiquidityRewards = (): string => { +type UserInfo = { + amount: string; + rewardDebt: string; + accumulatedReward: string; +}; + +type LiquidityRewardsData = { + availableLiquidityRewardsVested: string; + availableLiquidityRewardsLiquid: string; +}; + +export const useGetAvailableLiquidityRewards = (): LiquidityRewardsData => { const [liquidityRewards, setLiquidityRewards] = useState({ accumulatedRewards: '0', userRewards: '0', @@ -20,6 +26,27 @@ export const useGetAvailableLiquidityRewards = (): string => { loading: lockedBalanceLoading, } = useCacheCallWithValue('lockedSov', 'getLockedBalance', '', address); + const { + value: accumulatedRewardsVested, + loading: accumulatedRewardsVestedLoading, + } = useCacheCallWithValue( + 'liquidityMiningProxy', + 'getUserAccumulatedRewardToBeVested', + '', + address, + ); + + const { value: accumulatedRewardsLiquid } = useCacheCallWithValue( + 'liquidityMiningProxy', + 'getUserAccumulatedRewardToBePaidLiquid', + '', + address, + ); + + const { value: infoList, loading: infoListLoading } = useCacheCallWithValue< + UserInfo[] + >('liquidityMiningProxy', 'getUserInfoList', '', address); + useEffect(() => { if (!lockedBalanceLoading) { setLiquidityRewards(value => ({ @@ -30,79 +57,53 @@ export const useGetAvailableLiquidityRewards = (): string => { }, [lockedBalance, lockedBalanceLoading]); useEffect(() => { - const ammPools = LiquidityPoolDictionary.list().filter( - item => item.hasSovRewards, - ); - if (address !== '' && address !== ethGenesisAddress) { - const pools = ammPools.flatMap(item => - item.converterVersion === 1 - ? [item.poolTokenA] - : [item.poolTokenA, item.poolTokenB], - ); - bridgeNetwork - .multiCall( - Chain.RSK, - pools.flatMap((item, index) => { - return [ - { - address: getContract('liquidityMiningProxy').address, - abi: getContract('liquidityMiningProxy').abi, - fnName: 'getUserAccumulatedReward', - args: [item, address], - key: `getUserAccumulatedReward_${item}`, - parser: value => value[0].toString(), - }, - ]; - }), - ) - .then(result => { - const total = Object.values(result.returnData).reduce( - (previousValue, currentValue) => previousValue.add(currentValue), - bignumber(0), - ); - setLiquidityRewards(value => ({ - ...value, - accumulatedRewards: total.toString(), - })); - }) - .catch(error => { - console.error('e', error); - }); + if (!accumulatedRewardsVestedLoading && accumulatedRewardsVested) { + setLiquidityRewards(value => ({ + ...value, + accumulatedRewards: accumulatedRewardsVested.toString() || '0', + })); + } + }, [accumulatedRewardsVested, accumulatedRewardsVestedLoading]); - bridgeNetwork - .multiCall( - Chain.RSK, - pools.flatMap(item => { - return [ - { - address: getContract('liquidityMiningProxy').address, - abi: getContract('liquidityMiningProxy').abi, - fnName: 'getUserInfo', - args: [item, address], - key: `getUserInfo_${item}`, - parser: value => value[0].accumulatedReward.toString(), - }, - ]; - }), - ) - .then(result => { - const total = Object.values(result.returnData).reduce( - (prevValue, currValue) => prevValue.add(currValue), - bignumber(0), - ); - setLiquidityRewards(value => ({ - ...value, - userRewards: total.toString(), - })); - }) - .catch(error => { - console.error('e', error); - }); + useEffect(() => { + if (!infoListLoading) { + const userRewards = + infoList && infoList.length > 0 + ? infoList + .map(item => item?.accumulatedReward) + .reduce((a, b) => bignumber(a).add(b).toString(), '0') + : '0'; + + setLiquidityRewards(value => ({ + ...value, + userRewards, + })); } - }, [address]); + }, [infoList, infoListLoading, lockedBalance]); + + const availableLiquidityRewardsVested = useMemo( + () => + bignumber(liquidityRewards.accumulatedRewards) + .add(liquidityRewards.userRewards) + .add(liquidityRewards.lockedRewards) + .toString(), + [ + liquidityRewards.accumulatedRewards, + liquidityRewards.lockedRewards, + liquidityRewards.userRewards, + ], + ); + + const availableLiquidityRewardsLiquid = useMemo( + () => + !accumulatedRewardsVestedLoading && accumulatedRewardsLiquid + ? accumulatedRewardsLiquid.toString() + : '0', + [accumulatedRewardsLiquid, accumulatedRewardsVestedLoading], + ); - return bignumber(liquidityRewards.accumulatedRewards) - .add(liquidityRewards.userRewards) - .add(liquidityRewards.lockedRewards) - .toString(); + return { + availableLiquidityRewardsVested, + availableLiquidityRewardsLiquid, + }; }; diff --git a/src/app/pages/RewardPage/components/RewardTab/index.tsx b/src/app/pages/RewardPage/components/RewardTab/index.tsx index 2c2a9c7779..ba6b19faea 100644 --- a/src/app/pages/RewardPage/components/RewardTab/index.tsx +++ b/src/app/pages/RewardPage/components/RewardTab/index.tsx @@ -15,14 +15,16 @@ import { weiTo18 } from 'utils/blockchain/math-helpers'; interface IRewardTabProps { availableTradingRewards: string; - availableLiquidityRewards: string; + availableLiquidityRewardsVested: string; + availableLiquidityRewardsLiquid: string; availableLendingRewards: string; amountToClaim: string; } export const RewardTab: React.FC = ({ availableTradingRewards, - availableLiquidityRewards, + availableLiquidityRewardsVested, + availableLiquidityRewardsLiquid, availableLendingRewards, amountToClaim, }) => { @@ -54,11 +56,14 @@ export const RewardTab: React.FC = ({ calculatePercentageDistribution( availableLendingRewards, availableTradingRewards, - availableLiquidityRewards, + bignumber(availableLiquidityRewardsVested) + .add(availableLiquidityRewardsLiquid) + .toString(), ), [ availableLendingRewards, - availableLiquidityRewards, + availableLiquidityRewardsLiquid, + availableLiquidityRewardsVested, availableTradingRewards, ], ); @@ -107,28 +112,33 @@ export const RewardTab: React.FC = ({ diff --git a/src/app/pages/RewardPage/components/RewardsDetail/index.tsx b/src/app/pages/RewardPage/components/RewardsDetail/index.tsx index 68cbd63ddd..f6c623e6ed 100644 --- a/src/app/pages/RewardPage/components/RewardsDetail/index.tsx +++ b/src/app/pages/RewardPage/components/RewardsDetail/index.tsx @@ -18,13 +18,15 @@ export enum RewardsDetailColor { interface IRewardsDetailProps { color: RewardsDetailColor; title: string; - availableAmount: number | string; + availableAmountVested: number | string; + availableAmountLiquid?: number | string; totalEarnedAmount: number | string; isInMainSection?: boolean; isComingSoon?: boolean; asset?: Asset; loading?: boolean; showApproximateSign?: boolean; + isLiquidityMining?: boolean; // TODO: Temporary fix for liquidity mining rewards, introduced in https://sovryn.atlassian.net/browse/SOV-2001 } const getDetailColor = (color: RewardsDetailColor): string => { @@ -43,13 +45,15 @@ const getDetailColor = (color: RewardsDetailColor): string => { export const RewardsDetail: React.FC = ({ color, title, - availableAmount, + availableAmountVested, + availableAmountLiquid = '0', totalEarnedAmount, isInMainSection, isComingSoon, asset = Asset.SOV, loading = false, showApproximateSign = false, + isLiquidityMining = false, }) => { const { t } = useTranslation(); @@ -79,12 +83,16 @@ export const RewardsDetail: React.FC = ({ {t(translations.rewardPage.availableRewards)} -
+
= ({ } loading={loading} /> + {isLiquidityMining && ( + + ({t(translations.rewardPage.vested)}) + + )}
+ + {isLiquidityMining && ( +
+ + } + loading={loading} + /> + + ({t(translations.rewardPage.liquid)}) + +
+ )}
diff --git a/src/app/pages/RewardPage/hooks/useGetRewardSovClaimAmount.ts b/src/app/pages/RewardPage/hooks/useGetRewardSovClaimAmount.ts index 6597d0e57f..a9c4ebd30e 100644 --- a/src/app/pages/RewardPage/hooks/useGetRewardSovClaimAmount.ts +++ b/src/app/pages/RewardPage/hooks/useGetRewardSovClaimAmount.ts @@ -7,14 +7,18 @@ import { useGetTradingRewards } from '../components/RewardTab/hooks/useGetTradin type RewardSovClaimData = { availableLendingRewards: string; availableTradingRewards: string; - availableLiquidityRewards: string; + availableLiquidityRewardsVested: string; + availableLiquidityRewardsLiquid: string; amountToClaim: string; }; export const useGetRewardSovClaimAmount = (): RewardSovClaimData => { const availableLendingRewards = useGetAvailableLendingRewards(); const { data: availableTradingRewardsData } = useGetTradingRewards(); - const availableLiquidityRewards = useGetAvailableLiquidityRewards(); + const { + availableLiquidityRewardsVested, + availableLiquidityRewardsLiquid, + } = useGetAvailableLiquidityRewards(); const availableTradingRewards = useMemo( () => @@ -29,12 +33,14 @@ export const useGetRewardSovClaimAmount = (): RewardSovClaimData => { () => ( Number(availableLendingRewards) + - Number(availableLiquidityRewards) + + Number(availableLiquidityRewardsVested) + + Number(availableLiquidityRewardsLiquid) + Number(availableTradingRewards) ).toString(), [ availableLendingRewards, - availableLiquidityRewards, + availableLiquidityRewardsLiquid, + availableLiquidityRewardsVested, availableTradingRewards, ], ); @@ -42,7 +48,8 @@ export const useGetRewardSovClaimAmount = (): RewardSovClaimData => { return { availableLendingRewards: availableLendingRewards, availableTradingRewards: availableTradingRewards, - availableLiquidityRewards: availableLiquidityRewards, + availableLiquidityRewardsVested, + availableLiquidityRewardsLiquid, amountToClaim: amountToClaim, }; }; diff --git a/src/app/pages/RewardPage/hooks/useGetUserRewardsEarnedHistory.ts b/src/app/pages/RewardPage/hooks/useGetUserRewardsEarnedHistory.ts index 2c24ce7881..b742ac1445 100644 --- a/src/app/pages/RewardPage/hooks/useGetUserRewardsEarnedHistory.ts +++ b/src/app/pages/RewardPage/hooks/useGetUserRewardsEarnedHistory.ts @@ -8,7 +8,7 @@ import { export const useGetUserRewardsEarnedHistory = ( page: number, - action: RewardsEarnedAction, + actions: RewardsEarnedAction[], pageSize: number, ) => { const account = useAccount(); @@ -25,7 +25,7 @@ export const useGetUserRewardsEarnedHistory = ( user: account.toLowerCase(), skip: skip, pageSize: pageSize, - action: action, + actions, }, pollInterval: APOLLO_POLL_INTERVAL, }); diff --git a/src/app/pages/RewardPage/index.tsx b/src/app/pages/RewardPage/index.tsx index e57a555f85..cf6128e7d6 100644 --- a/src/app/pages/RewardPage/index.tsx +++ b/src/app/pages/RewardPage/index.tsx @@ -28,7 +28,8 @@ export function RewardPage() { const { availableLendingRewards, availableTradingRewards, - availableLiquidityRewards, + availableLiquidityRewardsVested, + availableLiquidityRewardsLiquid, amountToClaim: rewardSovClaimAmount, } = useGetRewardSovClaimAmount(); @@ -106,7 +107,12 @@ export function RewardPage() { {activeTab === RewardTabType.REWARD_SOV && ( diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 48ba641905..8e31fd01ab 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -1603,6 +1603,8 @@ "claimableRewards": "Claimable Rewards", "stakingReward": "Staking Rewards", "referralReward": "Referral Rewards", + "vested": "Vested", + "liquid": "Liquid", "comingSoon": "Coming Soon", "noRewardInfoText": { "rewardSovTab": { @@ -1634,7 +1636,7 @@ "title": "Amount to claim:", "available": "Total Reward SOV Available:", "cta": "CLAIM", - "note": "Claiming your rewards automatically starts a vesting period of 40 weeks with a maximum 4-week cliff.", + "note": "Claiming will simultaneously claim both liquid and vested rewards, and automatically start a vesting period for the vested rewards of 40 weeks with a maximum 4-week cliff.", "learn": "Learn more", "claimDisabled": "There is not enough SOV in the StakingRewardsProxy contract to facilitate a claim of rewards at this time. Therefore, to avoid a loss of gas fee by initiating a transaction that will inevitably fail, the CLAIM button has been disabled. Be assured that the team is aware and funds should be replenished shortly." }, diff --git a/src/locales/pt_br/translation.json b/src/locales/pt_br/translation.json index 9107d637fe..dc0ae20707 100644 --- a/src/locales/pt_br/translation.json +++ b/src/locales/pt_br/translation.json @@ -1619,7 +1619,6 @@ "title": "Quantidade:", "availble": "Total de SOVs disponíveis:", "cta": "REIVINDICAR", - "note": "Ao reivindicar seus SOVs Recompensa, eles ficam bloqueados por 10 meses. A cada mês uma quantidade é liberada.", "learn": "Saiba mais", "claimDisabled": "There is not enough SOV in the StakingRewardsProxy contract to facilitate a claim of rewards at this time. Therefore, to avoid a loss of gas fee by initiating a transaction that will inevitably fail, the CLAIM button has been disabled. Be assured that the team is aware and funds should be replenished shortly." }, diff --git a/src/utils/blockchain/abi/LiquidityMining.json b/src/utils/blockchain/abi/LiquidityMining.json index 4f643d1902..344275cc01 100644 --- a/src/utils/blockchain/abi/LiquidityMining.json +++ b/src/utils/blockchain/abi/LiquidityMining.json @@ -361,6 +361,27 @@ "stateMutability": "view", "type": "function" }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "_poolToken", + "type": "address" + } + ], + "name": "calcUnlockedImmediatelyPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, { "constant": false, "inputs": [ @@ -660,6 +681,48 @@ "stateMutability": "view", "type": "function" }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + } + ], + "name": "getUserAccumulatedRewardToBePaidLiquid", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + } + ], + "name": "getUserAccumulatedRewardToBeVested", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, { "constant": true, "inputs": [ @@ -934,6 +997,27 @@ "stateMutability": "view", "type": "function" }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "poolTokensUnlockedImmediatelyPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, { "constant": false, "inputs": [ @@ -979,6 +1063,26 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_poolToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_poolTokenUnlockedImmediatelyPercent", + "type": "uint256" + } + ], + "name": "setPoolTokenUnlockedImmediatelyPercent", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, { "constant": false, "inputs": [ diff --git a/src/utils/graphql/rsk/generated.tsx b/src/utils/graphql/rsk/generated.tsx index 8a2b7a8fad..058582e522 100644 --- a/src/utils/graphql/rsk/generated.tsx +++ b/src/utils/graphql/rsk/generated.tsx @@ -13222,7 +13222,7 @@ export type GetUserRewardsEarnedHistoryQueryVariables = Exact<{ user?: InputMaybe; skip: Scalars['Int']; pageSize: Scalars['Int']; - action: RewardsEarnedAction; + actions?: InputMaybe | RewardsEarnedAction>; }>; export type GetUserRewardsEarnedHistoryQuery = { @@ -14736,12 +14736,12 @@ export const GetUserRewardsEarnedHistoryDocument = gql` $user: String $skip: Int! $pageSize: Int! - $action: RewardsEarnedAction! + $actions: [RewardsEarnedAction!] ) { rewardsEarnedHistoryItems( first: $pageSize skip: $skip - where: { user: $user, amount_gt: 0, action: $action } + where: { user: $user, amount_gt: 0, action_in: $actions } orderBy: timestamp orderDirection: desc ) { @@ -14766,7 +14766,7 @@ export const GetUserRewardsEarnedHistoryDocument = gql` * user: // value for 'user' * skip: // value for 'skip' * pageSize: // value for 'pageSize' - * action: // value for 'action' + * actions: // value for 'actions' * }, * }); */ diff --git a/src/utils/graphql/rsk/operations/getUserRewardsEarnedHistory.graphql b/src/utils/graphql/rsk/operations/getUserRewardsEarnedHistory.graphql index 79357adc37..8036c4601b 100644 --- a/src/utils/graphql/rsk/operations/getUserRewardsEarnedHistory.graphql +++ b/src/utils/graphql/rsk/operations/getUserRewardsEarnedHistory.graphql @@ -2,12 +2,12 @@ query getUserRewardsEarnedHistory( $user: String $skip: Int! $pageSize: Int! - $action: RewardsEarnedAction! + $actions: [RewardsEarnedAction!] ) { rewardsEarnedHistoryItems( first: $pageSize skip: $skip - where: { user: $user, amount_gt: 0, action: $action } + where: { user: $user, amount_gt: 0, action_in: $actions } orderBy: timestamp orderDirection: desc ) {