import groupBy from 'lodash/groupBy';
import type { Account } from '@vegaprotocol/accounts';
import { useAccounts } from '@vegaprotocol/accounts';
import {
NetworkParams,
useNetworkParams,
} from '@vegaprotocol/network-parameters';
import { AccountType } from '@vegaprotocol/types';
import { useVegaWallet } from '@vegaprotocol/wallet';
import BigNumber from 'bignumber.js';
import {
Card,
CardStat,
CardTable,
CardTableTD,
CardTableTH,
} from '../card/card';
import {
type RewardsPageQuery,
useRewardsPageQuery,
useRewardsEpochQuery,
} from './__generated__/Rewards';
import {
TradingButton,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import { formatPercentage } from '../fees-container/utils';
import { addDecimalsFormatNumberQuantum } from '@vegaprotocol/utils';
import { ViewType, useSidebar } from '../sidebar';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { RewardsHistoryContainer } from './rewards-history';
import { useT } from '../../lib/use-t';
export const RewardsContainer = () => {
const t = useT();
const { pubKey } = useVegaWallet();
const { params, loading: paramsLoading } = useNetworkParams([
NetworkParams.reward_asset,
NetworkParams.rewards_activityStreak_benefitTiers,
NetworkParams.rewards_vesting_baseRate,
]);
const { data: accounts, loading: accountsLoading } = useAccounts(pubKey);
const { data: epochData } = useRewardsEpochQuery();
// No need to specify the fromEpoch as it will by default give you the last
const { data: rewardsData, loading: rewardsLoading } = useRewardsPageQuery({
variables: {
partyId: pubKey || '',
},
});
if (!epochData?.epoch) return null;
const loading = paramsLoading || accountsLoading || rewardsLoading;
const rewardAccounts = accounts
? accounts.filter((a) =>
[
AccountType.ACCOUNT_TYPE_VESTED_REWARDS,
AccountType.ACCOUNT_TYPE_VESTING_REWARDS,
].includes(a.type)
)
: [];
const rewardAssetsMap = groupBy(
rewardAccounts.filter((a) => a.asset.id !== params.reward_asset),
'asset.id'
);
return (
{/* Always show reward information for vega */}
{/* Show all other reward pots, most of the time users will not have other rewards */}
{Object.keys(rewardAssetsMap).map((assetId) => {
const asset = rewardAssetsMap[assetId][0].asset;
return (
);
})}
);
};
type VestingBalances = NonNullable<
RewardsPageQuery['party']
>['vestingBalancesSummary'];
export type RewardPotProps = {
pubKey: string | null;
accounts: Account[] | null;
assetId: string; // VEGA
vestingBalancesSummary: VestingBalances | undefined;
};
export const RewardPot = ({
pubKey,
accounts,
assetId,
vestingBalancesSummary,
}: RewardPotProps) => {
const t = useT();
// TODO: Opening the sidebar for the first time works, but then clicking on redeem
// for a different asset does not update the form
const currentRouteId = useGetCurrentRouteId();
const setViews = useSidebar((store) => store.setViews);
// All vested rewards accounts
const availableRewardAssetAccounts = accounts
? accounts.filter((a) => {
return (
a.asset.id === assetId &&
a.type === AccountType.ACCOUNT_TYPE_VESTED_REWARDS
);
})
: [];
// Sum of all vested reward account balances
const totalVestedRewardsByRewardAsset = BigNumber.sum.apply(
null,
availableRewardAssetAccounts.length
? availableRewardAssetAccounts.map((a) => a.balance)
: [0]
);
const lockedEntries = vestingBalancesSummary?.lockedBalances?.filter(
(b) => b.asset.id === assetId
);
const lockedBalances = lockedEntries?.length
? lockedEntries.map((e) => e.balance)
: [0];
const totalLocked = BigNumber.sum.apply(null, lockedBalances);
const vestingEntries = vestingBalancesSummary?.vestingBalances?.filter(
(b) => b.asset.id === assetId
);
const vestingBalances = vestingEntries?.length
? vestingEntries.map((e) => e.balance)
: [0];
const totalVesting = BigNumber.sum.apply(null, vestingBalances);
const totalRewards = totalLocked.plus(totalVesting);
let rewardAsset = undefined;
if (availableRewardAssetAccounts.length) {
rewardAsset = availableRewardAssetAccounts[0].asset;
} else if (lockedEntries?.length) {
rewardAsset = lockedEntries[0].asset;
} else if (vestingEntries?.length) {
rewardAsset = vestingEntries[0].asset;
}
if (!pubKey) {
return (
);
}
return (
{rewardAsset ? (
<>
{t('Locked {{assetSymbol}}', {
assetSymbol: rewardAsset.symbol,
})}
{addDecimalsFormatNumberQuantum(
totalLocked.toString(),
rewardAsset.decimals,
rewardAsset.quantum
)}
{t('Vesting {{assetSymbol}}', {
assetSymbol: rewardAsset.symbol,
})}
{addDecimalsFormatNumberQuantum(
totalVesting.toString(),
rewardAsset.decimals,
rewardAsset.quantum
)}
{t('Available to withdraw this epoch')}
{addDecimalsFormatNumberQuantum(
totalVestedRewardsByRewardAsset.toString(),
rewardAsset.decimals,
rewardAsset.quantum
)}
{totalVestedRewardsByRewardAsset.isGreaterThan(0) && (
setViews(
{ type: ViewType.Transfer, assetId },
currentRouteId
)
}
size="small"
>
{t('Redeem rewards')}
)}
>
) : (
{t('No rewards')}
)}
);
};
export const Vesting = ({
pubKey,
baseRate,
multiplier = '1',
}: {
pubKey: string | null;
baseRate: string;
multiplier?: string;
}) => {
const t = useT();
const rate = new BigNumber(baseRate).times(multiplier);
const rateFormatted = formatPercentage(Number(rate));
const baseRateFormatted = formatPercentage(Number(baseRate));
return (
{t('Base rate')}
{baseRateFormatted}%
{pubKey && (
{t('Vesting multiplier')}
{multiplier}x
)}
);
};
export const Multipliers = ({
pubKey,
streakMultiplier = '1',
hoarderMultiplier = '1',
}: {
pubKey: string | null;
streakMultiplier?: string;
hoarderMultiplier?: string;
}) => {
const t = useT();
const combinedMultiplier = new BigNumber(streakMultiplier).times(
hoarderMultiplier
);
if (!pubKey) {
return (
);
}
return (
{t('Streak reward multiplier')}
{streakMultiplier}x
{t('Hoarder reward multiplier')}
{hoarderMultiplier}x
);
};