fix: use correct multiplier for vesting
This commit is contained in:
@@ -2,9 +2,14 @@ query RewardsPage($partyId: ID!) {
|
||||
party(id: $partyId) {
|
||||
id
|
||||
vestingStats {
|
||||
epochSeq
|
||||
# AKA hoarder reward multiplier
|
||||
rewardBonusMultiplier
|
||||
quantumBalance
|
||||
}
|
||||
activityStreak {
|
||||
# vesting multiplier
|
||||
rewardVestingMultiplier
|
||||
# AKA streak multiplier
|
||||
rewardDistributionMultiplier
|
||||
}
|
||||
vestingBalancesSummary {
|
||||
epoch
|
||||
|
||||
@@ -8,7 +8,7 @@ export type RewardsPageQueryVariables = Types.Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type RewardsPageQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, vestingStats?: { __typename?: 'PartyVestingStats', epochSeq: number, rewardBonusMultiplier: string, quantumBalance: string } | null, vestingBalancesSummary: { __typename?: 'PartyVestingBalancesSummary', epoch?: number | null, vestingBalances?: Array<{ __typename?: 'PartyVestingBalance', balance: string, asset: { __typename?: 'Asset', id: string, symbol: string, decimals: number, quantum: string } }> | null, lockedBalances?: Array<{ __typename?: 'PartyLockedBalance', balance: string, untilEpoch: number, asset: { __typename?: 'Asset', id: string, symbol: string, decimals: number, quantum: string } }> | null } } | null };
|
||||
export type RewardsPageQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, vestingStats?: { __typename?: 'PartyVestingStats', rewardBonusMultiplier: string } | null, activityStreak?: { __typename?: 'PartyActivityStreak', rewardVestingMultiplier: string, rewardDistributionMultiplier: string } | null, vestingBalancesSummary: { __typename?: 'PartyVestingBalancesSummary', epoch?: number | null, vestingBalances?: Array<{ __typename?: 'PartyVestingBalance', balance: string, asset: { __typename?: 'Asset', id: string, symbol: string, decimals: number, quantum: string } }> | null, lockedBalances?: Array<{ __typename?: 'PartyLockedBalance', balance: string, untilEpoch: number, asset: { __typename?: 'Asset', id: string, symbol: string, decimals: number, quantum: string } }> | null } } | null };
|
||||
|
||||
export type RewardsHistoryQueryVariables = Types.Exact<{
|
||||
partyId: Types.Scalars['ID'];
|
||||
@@ -32,9 +32,11 @@ export const RewardsPageDocument = gql`
|
||||
party(id: $partyId) {
|
||||
id
|
||||
vestingStats {
|
||||
epochSeq
|
||||
rewardBonusMultiplier
|
||||
quantumBalance
|
||||
}
|
||||
activityStreak {
|
||||
rewardVestingMultiplier
|
||||
rewardDistributionMultiplier
|
||||
}
|
||||
vestingBalancesSummary {
|
||||
epoch
|
||||
|
||||
@@ -3,9 +3,6 @@ import type { Account } from '@vegaprotocol/accounts';
|
||||
import { AccountType, AssetStatus } from '@vegaprotocol/types';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { RewardPot, Vesting, type RewardPotProps } from './rewards-container';
|
||||
import type { MockedResponse } from '@apollo/react-testing';
|
||||
import { MockedProvider } from '@apollo/react-testing';
|
||||
import { type AssetQuery, AssetDocument } from '@vegaprotocol/assets';
|
||||
|
||||
const rewardAsset = {
|
||||
id: 'asset-1',
|
||||
@@ -154,65 +151,27 @@ describe('RewardPot', () => {
|
||||
});
|
||||
|
||||
describe('Vesting', () => {
|
||||
it('renders vesting rates', async () => {
|
||||
const assetMock: MockedResponse<AssetQuery> = {
|
||||
request: {
|
||||
query: AssetDocument,
|
||||
variables: {
|
||||
assetId: rewardAsset.id,
|
||||
},
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
assetsConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: rewardAsset,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
render(
|
||||
<MockedProvider mocks={[assetMock]}>
|
||||
<Vesting
|
||||
assetId={rewardAsset.id}
|
||||
baseRate={'0.25'}
|
||||
pubKey="pubKey"
|
||||
multiplier="2"
|
||||
vestingBalancesSummary={{
|
||||
epoch: 5,
|
||||
lockedBalances: [
|
||||
{
|
||||
balance: '150',
|
||||
asset: rewardAsset,
|
||||
untilEpoch: 6,
|
||||
},
|
||||
{
|
||||
balance: '100',
|
||||
asset: rewardAsset,
|
||||
untilEpoch: 6,
|
||||
},
|
||||
{
|
||||
balance: '100',
|
||||
asset: rewardAsset,
|
||||
untilEpoch: 7,
|
||||
},
|
||||
],
|
||||
}}
|
||||
epoch={5}
|
||||
/>
|
||||
</MockedProvider>
|
||||
it('renders vesting rates', () => {
|
||||
render(<Vesting baseRate={'0.25'} pubKey="pubKey" multiplier="2" />);
|
||||
|
||||
expect(screen.getByTestId('vesting-rate')).toHaveTextContent('50%');
|
||||
|
||||
expect(screen.getByText('Base rate').nextElementSibling).toHaveTextContent(
|
||||
'25%'
|
||||
);
|
||||
expect(
|
||||
screen.getByText('Vesting multiplier').nextSibling
|
||||
).toHaveTextContent('2x');
|
||||
});
|
||||
|
||||
const computedRate = await screen.findByTestId('vesting-rate');
|
||||
expect(computedRate).toHaveTextContent('50%');
|
||||
it('doesnt use multiplier if not connected', () => {
|
||||
render(<Vesting baseRate={'0.25'} pubKey={null} multiplier={undefined} />);
|
||||
|
||||
const baseRateLabel = screen.getByText(/Base rate/);
|
||||
expect(baseRateLabel.nextElementSibling).toHaveTextContent('25%');
|
||||
expect(screen.getByTestId('vesting-rate')).toHaveTextContent('25%');
|
||||
|
||||
const nextEpochLabel = screen.getByText('Available to withdraw next epoch');
|
||||
expect(nextEpochLabel.nextElementSibling).toHaveTextContent('2.50');
|
||||
expect(screen.getByText('Base rate').nextElementSibling).toHaveTextContent(
|
||||
'25%'
|
||||
);
|
||||
expect(screen.queryByText('Vesting multiplier')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -31,7 +31,6 @@ 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 { useAssetDataProvider } from '@vegaprotocol/assets';
|
||||
|
||||
export const RewardsContainer = () => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
@@ -72,7 +71,7 @@ export const RewardsContainer = () => {
|
||||
// TODO: Fix grid rows, they break on small screens when things stack
|
||||
return (
|
||||
<div className="grid auto-rows-min grid-cols-6 gap-3">
|
||||
{/* Always show the rewards pot for the reward asset AKA Vega */}
|
||||
{/* Always show reward information for vega */}
|
||||
<Card
|
||||
key={params.reward_asset}
|
||||
title={t('Vega Reward pot')}
|
||||
@@ -87,8 +86,32 @@ export const RewardsContainer = () => {
|
||||
vestingBalancesSummary={rewardsData?.party?.vestingBalancesSummary}
|
||||
/>
|
||||
</Card>
|
||||
<Card title={t('Vesting')} className="lg:col-span-2" loading={loading}>
|
||||
<Vesting
|
||||
pubKey={pubKey}
|
||||
baseRate={params.rewards_vesting_baseRate}
|
||||
multiplier={
|
||||
rewardsData?.party?.activityStreak?.rewardVestingMultiplier
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
<Card
|
||||
title={t('Rewards multipliers')}
|
||||
className="lg:col-span-2"
|
||||
loading={loading}
|
||||
highlight={true}
|
||||
>
|
||||
<Multipliers
|
||||
hoarderMultiplier={
|
||||
rewardsData?.party?.vestingStats?.rewardBonusMultiplier
|
||||
}
|
||||
streakMultiplier={
|
||||
rewardsData?.party?.activityStreak?.rewardDistributionMultiplier
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
{/* Show all other rewards */}
|
||||
{/* 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 (
|
||||
@@ -109,23 +132,6 @@ export const RewardsContainer = () => {
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
<Card title={t('Vesting')} className="lg:col-span-2" loading={loading}>
|
||||
<Vesting
|
||||
pubKey={pubKey}
|
||||
assetId={params.reward_asset}
|
||||
baseRate={params.rewards_vesting_baseRate}
|
||||
multiplier={rewardsData?.party?.vestingStats?.rewardBonusMultiplier}
|
||||
vestingBalancesSummary={rewardsData?.party?.vestingBalancesSummary}
|
||||
epoch={Number(epochData.epoch.id)}
|
||||
/>
|
||||
</Card>
|
||||
<Card
|
||||
title={t('Rewards multipliers')}
|
||||
className="lg:col-span-2"
|
||||
loading={loading}
|
||||
>
|
||||
<Multipliers />
|
||||
</Card>
|
||||
<Card
|
||||
title={t('Rewards history')}
|
||||
className="lg:col-span-full"
|
||||
@@ -291,42 +297,18 @@ export const RewardPot = ({
|
||||
};
|
||||
|
||||
export const Vesting = ({
|
||||
assetId,
|
||||
pubKey,
|
||||
baseRate,
|
||||
multiplier = '1',
|
||||
vestingBalancesSummary,
|
||||
epoch,
|
||||
}: {
|
||||
assetId: string;
|
||||
pubKey: string | null;
|
||||
baseRate: string;
|
||||
multiplier?: string;
|
||||
vestingBalancesSummary: VestingBalances | undefined;
|
||||
epoch: number;
|
||||
}) => {
|
||||
const { data: asset } = useAssetDataProvider(assetId);
|
||||
|
||||
if (!asset) return null;
|
||||
|
||||
const rate = new BigNumber(baseRate).times(multiplier);
|
||||
const rateFormatted = formatPercentage(Number(rate));
|
||||
const baseRateFormatted = formatPercentage(Number(baseRate));
|
||||
|
||||
const lockedEntries = vestingBalancesSummary?.lockedBalances?.filter(
|
||||
(b) => b.asset.id === assetId
|
||||
);
|
||||
|
||||
const unlockingNextEpoch = lockedEntries?.filter(
|
||||
(e) => e.untilEpoch === epoch + 1
|
||||
);
|
||||
|
||||
const nextEpochBalances = unlockingNextEpoch?.length
|
||||
? unlockingNextEpoch.map((e) => e.balance)
|
||||
: [0];
|
||||
|
||||
const totalUnlockingNextEpoch = BigNumber.sum.apply(null, nextEpochBalances);
|
||||
|
||||
return (
|
||||
<div className="pt-4">
|
||||
<CardStat value={rateFormatted + '%'} testId={'vesting-rate'} />
|
||||
@@ -336,32 +318,44 @@ export const Vesting = ({
|
||||
<CardTableTD>{baseRateFormatted}%</CardTableTD>
|
||||
</tr>
|
||||
{pubKey && (
|
||||
<>
|
||||
<tr>
|
||||
<CardTableTH>{t('Vesting multiplier')}</CardTableTH>
|
||||
<CardTableTD>{multiplier}</CardTableTD>
|
||||
</tr>
|
||||
<tr>
|
||||
<CardTableTH>{t('Available to withdraw next epoch')}</CardTableTH>
|
||||
<CardTableTD>
|
||||
{addDecimalsFormatNumberQuantum(
|
||||
totalUnlockingNextEpoch.toString(),
|
||||
asset.decimals,
|
||||
asset.quantum
|
||||
)}
|
||||
</CardTableTD>
|
||||
</tr>
|
||||
</>
|
||||
<tr>
|
||||
<CardTableTH>{t('Vesting multiplier')}</CardTableTH>
|
||||
<CardTableTD>{multiplier}x</CardTableTD>
|
||||
</tr>
|
||||
)}
|
||||
</CardTable>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Multipliers = () => {
|
||||
const Multipliers = ({
|
||||
streakMultiplier = '1',
|
||||
hoarderMultiplier = '1',
|
||||
}: {
|
||||
streakMultiplier?: string;
|
||||
hoarderMultiplier?: string;
|
||||
}) => {
|
||||
const combinedMultiplier = new BigNumber(streakMultiplier).times(
|
||||
hoarderMultiplier
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="pt-4">
|
||||
<p className="text-muted text-sm">{t('No active reward bonuses')}</p>
|
||||
<CardStat
|
||||
value={combinedMultiplier.toString() + 'x'}
|
||||
testId={'vesting-rate'}
|
||||
highlight={true}
|
||||
/>
|
||||
<CardTable>
|
||||
<tr>
|
||||
<CardTableTH>{t('Streak reward multiplier')}</CardTableTH>
|
||||
<CardTableTD>{streakMultiplier}x</CardTableTD>
|
||||
</tr>
|
||||
<tr>
|
||||
<CardTableTH>{t('Hoarder reward multiplier')}</CardTableTH>
|
||||
<CardTableTD>{hoarderMultiplier}x</CardTableTD>
|
||||
</tr>
|
||||
</CardTable>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user