Skip to content

Commit

Permalink
User burn events in inflation charts (#1357)
Browse files Browse the repository at this point in the history
* Initial logic with dummy data

* Load burn events from Token API
  • Loading branch information
bobo-k2 authored Jul 19, 2024
1 parent 4fd8a91 commit 06aa139
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 49 deletions.
12 changes: 6 additions & 6 deletions src/components/dashboard/Inflation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,12 @@ export default defineComponent({
const numberFromPercentage = (value?: number): number | string =>
value !== undefined ? value * 100 : '--';

const adjustableStakersPercentage = computed<number>(() =>
Number(
(
realizedAdjustableStakersPart.value / inflationParameters.value.adjustableStakersPart
).toFixed(3)
)
const adjustableStakersPercentage = computed<number>(
() =>
Math.round(
(realizedAdjustableStakersPart.value / inflationParameters.value.adjustableStakersPart) *
100
) / 100
);

watch(
Expand Down
17 changes: 13 additions & 4 deletions src/components/dashboard/InflationRateChart.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
</template>

<script lang="ts">
import { defineComponent, computed, ref, watch } from 'vue';
import { defineComponent, computed, ref, watch, onMounted } from 'vue';
import { Chart } from 'highcharts-vue';
import { useStore } from 'src/store';
import { titleFormatter, seriesFormatter } from 'src/modules/token-api';
Expand All @@ -43,11 +43,16 @@ export default defineComponent({
const getTextColor = (): string => (isDarkTheme.value ? '#5F656F' : '#B1B7C1');
const hasData = ref<boolean>(false);
const { t } = useI18n();
const { maximumInflationData, realizedInflationData, estimatedInflation, inflationParameters } =
useInflation();
const {
maximumInflationData,
realizedInflationData,
estimatedInflation,
inflationParameters,
estimateRealizedInflation,
} = useInflation();

const maximumInflationRate = computed<string>(() =>
(inflationParameters.value.maxInflationRate * 100).toFixed(1)
((inflationParameters.value?.maxInflationRate ?? 0) * 100).toFixed(1)
);

Highcharts.setOptions({
Expand Down Expand Up @@ -154,6 +159,10 @@ export default defineComponent({
}
});

onMounted(() => {
estimateRealizedInflation();
});

return {
estimatedInflation,
chartOptions,
Expand Down
118 changes: 81 additions & 37 deletions src/hooks/useInflation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import { computed, watch, ref, Ref, ComputedRef } from 'vue';
import { useI18n } from 'vue-i18n';
import { useStore } from 'src/store';
import { container } from 'src/v2/common';
import { IBalancesRepository, IInflationRepository } from 'src/v2/repositories';
import {
BurnEvent,
IBalancesRepository,
IInflationRepository,
ITokenApiRepository,
} from 'src/v2/repositories';
import { Symbols } from 'src/v2/symbols';
import { InflationConfiguration } from 'src/v2/models';
import { InflationParam, useDappStaking } from 'src/staking-v3';
Expand All @@ -20,6 +25,7 @@ type UseInflation = {
fetchActiveConfigurationToStore: () => Promise<void>;
fetchInflationParamsToStore: () => Promise<void>;
getInflationParameters: () => Promise<InflationParam>;
estimateRealizedInflation: () => Promise<void>;
};

export function useInflation(): UseInflation {
Expand Down Expand Up @@ -57,12 +63,24 @@ export function useInflation(): UseInflation {
return await inflationRepository.getInflationParams();
};

const getBurnEvents = async (): Promise<BurnEvent[]> => {
// Ignore burn events with less than 1M ASTAR. They are not impacting charts a lot small burn amounts
// could be a spam.
const minBurn = BigInt('1000000000000000000000000');
const tokenApiRepository = container.get<ITokenApiRepository>(Symbols.TokenApiRepository);
const burnEvents = await tokenApiRepository.getBurnEvents(
networkNameSubstrate.value.toLowerCase()
);

return burnEvents.filter((item) => item.amount >= minBurn);
};

/**
* Estimates the realized inflation rate percentage based on the actual total issuance at the beginning
* and estimated total issuance at the end of the current cycle.
* According to the https://github.com/AstarNetwork/astar-apps/issues/1259
*/
const estimateRealizedInflation = async (): Promise<number | undefined> => {
const estimateRealizedInflation = async (): Promise<void> => {
let inflation: number | undefined;

try {
Expand All @@ -76,13 +94,28 @@ export function useInflation(): UseInflation {
}

const balancesRepository = container.get<IBalancesRepository>(Symbols.BalancesRepository);
const initialTotalIssuance =
(await balancesRepository.getTotalIssuance(period1StartBlock - 1)) -
(networkNameSubstrate.value.toLowerCase() === 'astar'
? BigInt('350000000000000000000000000')
: BigInt(0)); // Quick fox for token burning event. TODO make a proper solution
const initialTotalIssuance = await balancesRepository.getTotalIssuance(period1StartBlock - 1); // -
const realizedTotalIssuance = await balancesRepository.getTotalIssuance();

const burnEvents = await getBurnEvents();
// Add first and last block so charts can be easily drawn.
burnEvents.splice(0, 0, {
blockNumber: period1StartBlock,
amount: BigInt(0),
user: '',
timestamp: 0,
});
burnEvents.push({
blockNumber: currentBlock.value,
amount: BigInt(0),
user: '',
timestamp: 0,
});

const totalBurn = burnEvents.reduce((acc, item) => acc + item.amount, BigInt(0));
// Used to calculate inflation rate.
const initialTotalIssuanceWithoutBurn = initialTotalIssuance - totalBurn;

const {
periodsPerCycle,
standardEraLength,
Expand All @@ -95,21 +128,22 @@ export function useInflation(): UseInflation {
(standardErasPerBuildAndEarnPeriod + standardErasPerVotingPeriod);
const blockDifference = BigInt(currentBlock.value - period1StartBlock);
const slope =
BigInt((realizedTotalIssuance - initialTotalIssuance).toString()) / blockDifference;
BigInt((realizedTotalIssuance - initialTotalIssuanceWithoutBurn).toString()) /
blockDifference;

// Estimate total issuance at the end of the current cycle.
const endOfCycleBlock = period1StartBlock + cycleLengthInBlocks;
const endOfCycleTotalIssuance = Number(
ethers.utils.formatEther(
slope * BigInt(endOfCycleBlock - period1StartBlock) + initialTotalIssuance
slope * BigInt(endOfCycleBlock - period1StartBlock) + initialTotalIssuanceWithoutBurn
)
);

// Estimated inflation at the end of the current cycle.
inflation =
(100 *
(endOfCycleTotalIssuance -
Number(ethers.utils.formatEther(initialTotalIssuance.toString())))) /
Number(ethers.utils.formatEther(initialTotalIssuanceWithoutBurn.toString())))) /
endOfCycleTotalIssuance;

// Calculate maximum and realized inflation for each era in the cycle.
Expand All @@ -118,16 +152,18 @@ export function useInflation(): UseInflation {
endOfCycleBlock,
initialTotalIssuance,
cycleLengthInBlocks,
inflationParameters.value.maxInflationRate,
eraLengths.value.standardEraLength
inflationParameters.value?.maxInflationRate ?? 0,
eraLengths.value.standardEraLength,
burnEvents
);

calculateRealizedInflationData(
period1StartBlock,
currentBlock.value,
slope,
eraLengths.value.standardEraLength,
initialTotalIssuance
initialTotalIssuance,
burnEvents
);

calculateAdjustableStakerRewards(
Expand All @@ -140,7 +176,7 @@ export function useInflation(): UseInflation {
console.error('Error calculating realized inflation', error);
}

return inflation;
estimatedInflation.value = inflation;
};

const calculateMaximumInflationData = (
Expand All @@ -149,22 +185,30 @@ export function useInflation(): UseInflation {
firstBlockIssuance: bigint,
cycleLengthInBlocks: number,
maxInflation: number,
eraLength: number
eraLength: number,
burnEvents: BurnEvent[]
): void => {
const result: [number, number][] = [];
const inflation = BigInt(Math.floor(maxInflation * 100)) * BigInt('10000000000000000');
const cycleProgression = (firstBlockIssuance * inflation) / BigInt('1000000000000000000');
const cycleLength = BigInt(cycleLengthInBlocks);

// One sample per era.
for (let i = firstBlock; i <= lastBlock; i += eraLength) {
const inflation =
(cycleProgression * BigInt(i - firstBlock)) / cycleLength + firstBlockIssuance;
for (let j = 0; j < burnEvents.length - 1; j++) {
for (
let i = burnEvents[j].blockNumber;
i <= burnEvents[j + 1].blockNumber + eraLength;
i += eraLength
) {
const inflation =
(cycleProgression * BigInt(i - firstBlock)) / cycleLength +
firstBlockIssuance -
burnEvents[j].amount;

result.push([i, Number(ethers.utils.formatEther(inflation.toString()))]);
result.push([i, Number(ethers.utils.formatEther(inflation.toString()))]);
}
}

// console.log((result[result.length - 1][1] - result[0][1]) / result[result.length - 1][1]);
maximumInflationData.value = result;
};

Expand All @@ -173,16 +217,25 @@ export function useInflation(): UseInflation {
lastBlock: number,
slope: bigint,
eraLength: number,
firstBlockIssuance: bigint
firstBlockIssuance: bigint,
burnEvents: BurnEvent[]
): void => {
const result: [number, number][] = [];

for (let i = firstBlock; i <= lastBlock; i += eraLength) {
const currentBlockIssuance = Number(
ethers.utils.formatEther(slope * BigInt(i - firstBlock) + firstBlockIssuance)
);
for (let j = 0; j < burnEvents.length - 1; j++) {
for (
let i = burnEvents[j].blockNumber;
i <= burnEvents[j + 1].blockNumber + eraLength;
i += eraLength
) {
const currentBlockIssuance = Number(
ethers.utils.formatEther(
slope * BigInt(i - firstBlock) + firstBlockIssuance - burnEvents[j].amount
)
);

result.push([i, currentBlockIssuance]);
result.push([i, currentBlockIssuance]);
}
}

realizedInflationData.value = result;
Expand All @@ -203,18 +256,8 @@ export function useInflation(): UseInflation {
realizedAdjustableStakersPart.value = Number(result.toFixed(3));
};

watch(
[activeInflationConfiguration, inflationParameters],
async () => {
if (activeInflationConfiguration.value && inflationParameters.value) {
estimatedInflation.value = await estimateRealizedInflation();
}
},
{ immediate: true }
);

return {
activeInflationConfiguration,
activeInflationConfiguration: activeInflationConfiguration,
estimatedInflation,
inflationParameters,
maximumInflationData,
Expand All @@ -223,5 +266,6 @@ export function useInflation(): UseInflation {
fetchActiveConfigurationToStore,
fetchInflationParamsToStore,
getInflationParameters,
estimateRealizedInflation,
};
}
13 changes: 13 additions & 0 deletions src/v2/repositories/ITokenApiRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ export type PeriodData = {
stakeAmount: bigint;
};

export type BurnEvent = {
blockNumber: number;
timestamp: number;
amount: bigint;
user: string;
};

/**
* Definition of repository for access token price.
*/
Expand All @@ -20,4 +27,10 @@ export interface ITokenApiRepository {
* @param period Period number.
*/
getStakingPeriodStatistics(network: string, period: number): Promise<PeriodData[]>;

/**
* Gets burn events for the given network.
* @param network Network name.
*/
getBurnEvents(network: string): Promise<BurnEvent[]>;
}
2 changes: 1 addition & 1 deletion src/v2/repositories/implementations/SubscanRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class SubscanRepository implements ISubscanRepository {

const payload = {
module,
event: eventName,
event_id: eventName,
page,
row: pageSize,
};
Expand Down
19 changes: 18 additions & 1 deletion src/v2/repositories/implementations/TokenApiRepository.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import axios from 'axios';
import { injectable } from 'inversify';
import { ITokenApiRepository, PeriodData } from '../ITokenApiRepository';
import { BurnEvent, ITokenApiRepository, PeriodData } from '../ITokenApiRepository';

@injectable()
export class TokenApiRepository implements ITokenApiRepository {
Expand Down Expand Up @@ -31,4 +31,21 @@ export class TokenApiRepository implements ITokenApiRepository {
return [];
}
}

public async getBurnEvents(network: string): Promise<BurnEvent[]> {
try {
const url = `${TokenApiRepository.BaseUrl}/v1/${network}/burn/events`;
const response = await axios.get<BurnEvent[]>(url);
return response.data.map((data) => {
return {
blockNumber: data.blockNumber,
timestamp: data.timestamp,
amount: BigInt(data.amount),
user: data.user,
};
});
} catch (error) {
return [];
}
}
}

0 comments on commit 06aa139

Please sign in to comment.