Skip to content

Commit

Permalink
Enable period statistics (#1352)
Browse files Browse the repository at this point in the history
* Fetch stake from chain

* Optimizations and skeletons
  • Loading branch information
bobo-k2 committed Jul 15, 2024
1 parent 4f60ce0 commit 4fd8a91
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 12 deletions.
3 changes: 2 additions & 1 deletion src/staking-v3/components/DappStatsPanel.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<template>
<div class="panel-wrapper">
<div class="panel-title">{{ title }}</div>
<div class="wrapper--stats">
<q-skeleton v-if="!pages.length" class="skeleton-dapp-stats" />
<div v-else class="wrapper--stats">
<swiper class="swiper--stats" :navigation="true" :modules="modules">
<swiper-slide v-for="(dapps, page) in pages" :key="page">
<div class="container-dapps">
Expand Down
13 changes: 9 additions & 4 deletions src/staking-v3/components/PeriodStats.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<div v-if="false" class="wrapper--period-stats">
<div class="wrapper--period-stats">
<div class="title">
<span>{{ period.toString().padStart(3, '0') }}</span>
<span>{{ $t('stakingV3.stats') }}</span>
Expand All @@ -13,28 +13,33 @@
<div class="apr-basic">
<div class="apr-title">{{ $t('stakingV3.basicApr') }}</div>
<div class="value-unit">
<span>{{ stakerApr ? stakerApr.toFixed(2) : '--' }}<small>%</small></span>
<q-skeleton v-if="!stakerApr" class="skeleton-number" />
<span v-else>{{ stakerApr ? stakerApr.toFixed(2) : '--' }}<small>%</small></span>
</div>
</div>
<div class="apr-bonus">
<div class="apr-title">{{ $t('stakingV3.bonusAPR') }}</div>
<div class="value-unit">
<span>{{ bonusApr ? bonusApr.toFixed(2) : '--' }}<small>%</small></span>
<q-skeleton v-if="!bonusApr" class="skeleton-number" />
<span v-else>{{ bonusApr ? bonusApr.toFixed(2) : '--' }}<small>%</small></span>
</div>
</div>
</div>
</div>
<div class="period-kpi-container">
<div class="kpi-title">{{ $t('stakingV3.percentageLocked') }}</div>
<div class="value-unit">
<span>{{ tvlRatio ? (tvlRatio * 100).toFixed(1) : '--' }}<small>%</small></span>
<q-skeleton v-if="!tvlRatio" class="skeleton-number" />
<span v-else>{{ tvlRatio ? (tvlRatio * 100).toFixed(1) : '--' }}<small>%</small></span>
</div>
</div>
<div class="period-kpi-container">
<div class="kpi-title">{{ $t('stakingV3.unmintedTokens') }}</div>
<div>
<div class="value-unit">
<q-skeleton v-if="!tokensToBeBurned" class="skeleton-number" />
<token-balance-native
v-else
:balance="tokensToBeBurned?.toString() ?? '0'"
:show-token-symbol="false"
:decimals="0"
Expand Down
6 changes: 6 additions & 0 deletions src/staking-v3/components/styles/dapp-stats-panel.scss
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,9 @@
flex-wrap: wrap;
justify-content: flex-end;
}

.skeleton-dapp-stats {
width: 100%;
height: 344px;
border-radius: 8px;
}
10 changes: 10 additions & 0 deletions src/staking-v3/components/styles/period-stats.scss
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@
font-weight: 400;
}
}

.q-skeleton {
margin-left: auto;
}
}

.more-info {
Expand Down Expand Up @@ -130,3 +134,9 @@
}
}
}

.skeleton-number {
width: 110px;
height: 38px;
border-radius: 8px;
}
37 changes: 30 additions & 7 deletions src/staking-v3/hooks/usePeriodStats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ export function usePeriodStats(period: Ref<number>) {
const { currentNetworkName } = useNetworkInfo();
const { eraLengths, protocolState, currentBlock } = useDappStaking();
const { calculateTotalTokensToBeBurned } = useDataCalculations();
const { getDapp } = useDapps();
const { allDapps, getDapp } = useDapps();

const periodData = ref<PeriodData[]>([]);
const tvlRatio = ref<number>();
const stakerApr = ref<number>();
const bonusApr = ref<number>();
const tokensToBeBurned = ref<bigint>();
const tokensToBeBurned = ref<bigint | undefined>();

const dappStatistics = computed<DappStatistics[]>(() => {
const combinedData = periodData.value.map((data) => {
Expand Down Expand Up @@ -81,17 +81,41 @@ export function usePeriodStats(period: Ref<number>) {
Symbols.DappStakingRepositoryV3
);

const [stats, totalIssuance, periodInfo] = await Promise.all([
const allDappsId = allDapps.value.map((dapp) => dapp.chain.id);
const [stats, totalIssuance, periodInfo, stakes] = await Promise.all([
repository.getStakingPeriodStatistics(currentNetworkName.value.toLowerCase(), period.value),
balancesRepository.getTotalIssuance(block),
dappStakingRepository.getCurrentEraInfo(block),
dappStakingRepository.getContractsStake(
allDappsId,
getPeriodEndBlock(period.value, protocolState.value?.periodInfo.number ?? period.value)
),
]);

// Update skates receiver from indexer although the indexer data is correct, we are using on chain data
// This discrepancy is caused by runtime bug.
stats.forEach((stat) => {
const dapp = getDapp(stat.dappAddress);
if (dapp) {
const stake = stakes.get(dapp.chain.id);
if (stake) {
stat.stakeAmount = stake.staked.totalStake;
}
} else {
console.warn(`Dapp ${stat.dappAddress} not found in dapps list`);
}
});

periodData.value = stats;
const issuance = Number(ethers.utils.formatEther(totalIssuance));
const locked = Number(ethers.utils.formatEther(periodInfo.totalLocked));
tvlRatio.value = locked / issuance;
};

const setTokensToBeBurned = async (block: number): Promise<void> => {
tokensToBeBurned.value = await calculateTotalTokensToBeBurned(block);
};

watch(
[period, currentNetworkName, protocolState, eraLengths],
async () => {
Expand All @@ -109,18 +133,17 @@ export function usePeriodStats(period: Ref<number>) {
const stakingService = container.get<IDappStakingService>(Symbols.DappStakingServiceV3);

const block = Math.min(periodEndBlock, currentBlock.value) - 1;
const [, sApr, bApr, burned] = await Promise.all([
calculateTvlRatio(block),
calculateTvlRatio(block);
setTokensToBeBurned(block);
const [sApr, bApr] = await Promise.all([
// Passing periodEndBlock - 1 to APR calculations is because in the last block of the period
// everything is unstaked and all stakes are set to 0 and with 0 stake APR can't be calculated
stakingService.getStakerApr(block),
stakingService.getBonusApr(undefined, block),
calculateTotalTokensToBeBurned(block),
]);

stakerApr.value = sApr;
bonusApr.value = bApr.value;
tokensToBeBurned.value = burned;
} catch (error) {
console.error('Failed to get staking period statistics', error);
}
Expand Down
18 changes: 18 additions & 0 deletions src/staking-v3/logic/repositories/DappStakingRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,24 @@ export class DappStakingRepository implements IDappStakingRepository {
return this.mapContractStakeAmount(contractStake);
}

public async getContractsStake(
dappIds: number[],
block?: number
): Promise<Map<number, ContractStakeAmount>> {
const api = await this.api.getApi(block);
const contractStakes =
await api.query.dappStaking.contractStake.multi<PalletDappStakingV3ContractStakeAmount>(
dappIds
);

return new Map<number, ContractStakeAmount>(
contractStakes.map((contractStake, index) => [
dappIds[index],
this.mapContractStakeAmount(contractStake),
])
);
}

//* @inheritdoc
public async getClaimUnlockedTokensCall(): Promise<ExtrinsicPayload> {
const api = await this.api.getApi();
Expand Down
7 changes: 7 additions & 0 deletions src/staking-v3/logic/repositories/IDappStakingRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,13 @@ export interface IDappStakingRepository {
*/
getContractStake(dappId: number): Promise<ContractStakeAmount>;

/**
* Gets the contract staking info for multiple dapps.
* @param dappIds Dapp id to get staking info for.
* @param block Block number to get the data for or undefined to get data for the current block.
*/
getContractsStake(dappIds: number[], block?: number): Promise<Map<number, ContractStakeAmount>>;

/**
* Gets a call to claim all fully unlocked chunks.
*/
Expand Down

0 comments on commit 4fd8a91

Please sign in to comment.