diff --git a/public/swagger.json b/public/swagger.json index 7175e1e..892ae83 100644 --- a/public/swagger.json +++ b/public/swagger.json @@ -275,6 +275,27 @@ } } }, + "/api/v1/{network}/token/supply-history/": { + "get": { + "tags": [ + "Token" + ], + "description": "Retreives total supply history at the beginning of a new dApp staking era for a given network and period.", + "parameters": [ + { + "name": "network", + "in": "path", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, "/api/v1/{network}/dapps-staking/apr": { "get": { "tags": [ diff --git a/src/controllers/TokenStatsController.ts b/src/controllers/TokenStatsController.ts index 10215f5..f108af0 100644 --- a/src/controllers/TokenStatsController.ts +++ b/src/controllers/TokenStatsController.ts @@ -243,5 +243,22 @@ export class TokenStatsController extends ControllerBase implements IControllerB */ res.json(await this._indexerService.getHolders(req.params.network as NetworkType)); }); + + /** + * @description Total issuance history. + */ + app.route('/api/v1/:network/token/supply-history/').get(async (req: Request, res: Response) => { + /* + #swagger.description = 'Retreives total supply history at the beginning of a new dApp staking era for a given network and period.' + #swagger.tags = ['Token'] + #swagger.parameters['network'] = { + in: 'path', + description: 'The network name. Supported networks: astar, shiden, shibuya' + required: true, + enum: ['astar', 'shiden', 'shibuya'] + } + */ + res.json(await this._statsService.getTotalIssuanceHistory(req.params.network as NetworkType)); + }); } } diff --git a/src/services/DappStakingV3IndexerBase.ts b/src/services/DappStakingV3IndexerBase.ts index 5b1142a..b3b1398 100644 --- a/src/services/DappStakingV3IndexerBase.ts +++ b/src/services/DappStakingV3IndexerBase.ts @@ -12,6 +12,6 @@ export class DappStakingV3IndexerBase extends ServiceBase { protected getApiUrl(network: NetworkType): string { // For local development: `http://localhost:4350/graphql`; - return `https://astar-network.squids.live/dapps-staking-indexer-${network}/v/v14/graphql`; + return `https://astar-network.squids.live/dapps-staking-indexer-${network}/v/v15/graphql`; } } diff --git a/src/services/StatsService.ts b/src/services/StatsService.ts index dae4d46..befa1cd 100644 --- a/src/services/StatsService.ts +++ b/src/services/StatsService.ts @@ -8,18 +8,29 @@ import { NetworkType } from '../networks'; import { addressesToExclude } from './AddressesToExclude'; import { AccountData } from '../models/AccountData'; import { Guard } from '../guard'; +import { DappStakingV3IndexerBase } from './DappStakingV3IndexerBase'; +import axios from 'axios'; + +export type TotalSupply = { + block: number; + timestamp: number; + balance: bigint; +}; export interface IStatsService { getTokenStats(network: NetworkType): Promise; getTotalSupply(network: NetworkType): Promise; + getTotalIssuanceHistory(network: NetworkType): Promise; } @injectable() /** * Token statistics calculation service. */ -export class StatsService implements IStatsService { - constructor(@inject(ContainerTypes.ApiFactory) private _apiFactory: IApiFactory) {} +export class StatsService extends DappStakingV3IndexerBase implements IStatsService { + constructor(@inject(ContainerTypes.ApiFactory) private _apiFactory: IApiFactory) { + super(); + } /** * Calculates token circulation supply by substracting sum of all token holder accounts @@ -29,7 +40,7 @@ export class StatsService implements IStatsService { */ public async getTokenStats(network: NetworkType): Promise { Guard.ThrowIfUndefined(network, 'network'); - this.throwIfNetworkIsNotSupported(network); + this.GuardNetwork(network); try { const api = this._apiFactory.getApiInstance(network); @@ -53,7 +64,7 @@ export class StatsService implements IStatsService { public async getTotalSupply(network: NetworkType): Promise { Guard.ThrowIfUndefined(network, 'network'); - this.throwIfNetworkIsNotSupported(network); + this.GuardNetwork(network); try { const api = this._apiFactory.getApiInstance(network); @@ -67,6 +78,33 @@ export class StatsService implements IStatsService { } } + public async getTotalIssuanceHistory(network: NetworkType): Promise { + this.GuardNetwork(network); + + try { + const result = await axios.post(this.getApiUrl(network), { + query: `query { + totalIssuances(orderBy: id_ASC) { + id + timestamp + balance + } + }`, + }); + + return result.data.data.totalIssuances.map((item: { id: string; timestamp: string; balance: string }) => { + return { + block: Number(item.id), + timestamp: Number(item.timestamp), + balance: BigInt(item.balance), + }; + }); + } catch (e) { + console.error(e); + return []; + } + } + private getTotalBalanceToExclude(balances: AccountData[]): BN { const sum = balances .map((balance) => { @@ -82,10 +120,4 @@ export class StatsService implements IStatsService { return parseInt(result.replaceAll(',', '')); } - - private throwIfNetworkIsNotSupported(network: NetworkType): void { - if (network !== 'astar' && network !== 'shiden' && network !== 'shibuya' && network !== 'rocstar') { - throw new Error(`Network ${network} is not supported.`); - } - } }