From 17f02a179fb403b910107971e60eef6344d9b8c3 Mon Sep 17 00:00:00 2001 From: Bobo Date: Fri, 12 Jul 2024 14:16:11 +0200 Subject: [PATCH] Burn events endpoint --- src/container.ts | 4 ++ src/containertypes.ts | 3 ++ src/controllers/BurnController.ts | 38 ++++++++++++++ src/services/BurnService.ts | 40 +++++++++++++++ src/services/DappStakingV3IndexerBase.ts | 17 +++++++ src/services/DappsStakingEvents.ts | 63 +++++++----------------- 6 files changed, 119 insertions(+), 46 deletions(-) create mode 100644 src/controllers/BurnController.ts create mode 100644 src/services/BurnService.ts create mode 100644 src/services/DappStakingV3IndexerBase.ts diff --git a/src/container.ts b/src/container.ts index 7a91d83..7808b8f 100644 --- a/src/container.ts +++ b/src/container.ts @@ -45,6 +45,8 @@ import { import { BluezNftService, INftService } from './services/NftService'; import { NftController } from './controllers/NftController'; import { TokenStatsControllerV2 } from './controllers/TokenStatsControllerV2'; +import { BurnService, IBurnService } from './services/BurnService'; +import { BurnController } from './controllers/BurnController'; const container = new Container(); @@ -75,6 +77,7 @@ container.bind(ContainerTypes.ApiFactory).to(ApiFactory).inSingleto container.bind(ContainerTypes.StatsService).to(StatsService).inSingletonScope(); container.bind(ContainerTypes.DappsStakingEvents).to(DappsStakingEvents).inSingletonScope(); +container.bind(ContainerTypes.BurnService).to(BurnService).inSingletonScope(); container .bind(ContainerTypes.DappsStakingService) @@ -145,5 +148,6 @@ container.bind(ContainerTypes.Controller).to(NodeController); container.bind(ContainerTypes.Controller).to(TxQueryController); container.bind(ContainerTypes.Controller).to(MonthlyActiveWalletsController); container.bind(ContainerTypes.Controller).to(NftController); +container.bind(ContainerTypes.Controller).to(BurnController); export default container; diff --git a/src/containertypes.ts b/src/containertypes.ts index d699d1c..9073dde 100644 --- a/src/containertypes.ts +++ b/src/containertypes.ts @@ -1,3 +1,5 @@ +import { BurnService } from './services/BurnService'; + export const ContainerTypes = { Controller: 'Controller', StatsService: 'StatsService', @@ -16,4 +18,5 @@ export const ContainerTypes = { DappRadarService: 'DappRadarService', GiantSquidService: 'GiantSquidService', BluezNftService: 'BluezNftService', + BurnService: 'BurnService', }; diff --git a/src/controllers/BurnController.ts b/src/controllers/BurnController.ts new file mode 100644 index 0000000..bb11d45 --- /dev/null +++ b/src/controllers/BurnController.ts @@ -0,0 +1,38 @@ +import express, { Request, Response } from 'express'; +import { inject, injectable } from 'inversify'; +import { ControllerBase } from './ControllerBase'; +import { IControllerBase } from './IControllerBase'; +import { ContainerTypes } from '../containertypes'; +import { IBurnService } from '../services/BurnService'; +import { NetworkType } from '../networks'; + +@injectable() +export class BurnController extends ControllerBase implements IControllerBase { + constructor(@inject(ContainerTypes.BurnService) private _burnService: IBurnService) { + super(); + } + + public register(app: express.Application): void { + /** + * @description Burn events route + */ + app.route('/api/v1/:network/burn/events').get(async (req: Request, res: Response) => { + /* + #swagger.description = 'Retrieves burn events for a given network.' + #swagger.tags = ['Burn'] + #swagger.parameters['network'] = { + in: 'path', + description: 'The network name. Supported networks: astar, shiden, shibuya', + required: true, + enum: ['astar', 'shiden', 'shibuya'] + } + */ + try { + const network = req.params.network as NetworkType; + res.json(await this._burnService.getBurnEvents(network)); + } catch (err) { + this.handleError(res, err as Error); + } + }); + } +} diff --git a/src/services/BurnService.ts b/src/services/BurnService.ts new file mode 100644 index 0000000..0101458 --- /dev/null +++ b/src/services/BurnService.ts @@ -0,0 +1,40 @@ +import { injectable } from 'inversify'; +import { NetworkType } from '../networks'; +import axios from 'axios'; +import { DappStakingV3IndexerBase } from './DappStakingV3IndexerBase'; + +export type BurnEvent = { + blockNumber: number; + timestamp: number; + amount: bigint; + user: string; +}; + +export interface IBurnService { + getBurnEvents(network: NetworkType): Promise; +} + +@injectable() +export class BurnService extends DappStakingV3IndexerBase implements IBurnService { + public async getBurnEvents(network: NetworkType): Promise { + this.GuardNetwork(network); + + try { + const result = await axios.post(this.getApiUrl(network), { + query: `query { + burns(orderBy: id_ASC) { + blockNumber + timestamp + user + amount + } + }`, + }); + + return result.data.data.burns; + } catch (e) { + console.error(e); + return []; + } + } +} diff --git a/src/services/DappStakingV3IndexerBase.ts b/src/services/DappStakingV3IndexerBase.ts new file mode 100644 index 0000000..a6ff9ff --- /dev/null +++ b/src/services/DappStakingV3IndexerBase.ts @@ -0,0 +1,17 @@ +import { Guard } from '../guard'; +import { NetworkType } from '../networks'; +import { ServiceBase } from './ServiceBase'; + +export class DappStakingV3IndexerBase extends ServiceBase { + protected GuardNetwork(network: NetworkType) { + Guard.ThrowIfUndefined('network', network); + if (!['shibuya', 'shiden', 'astar'].includes(network)) { + throw new Error(`This method is not supported for the network ${network}`); + } + } + + protected getApiUrl(network: NetworkType): string { + // For local development: `http://localhost:4350/graphql`; + return `https://astar-network.squids.live/dapps-staking-indexer-${network}/graphql`; + } +} diff --git a/src/services/DappsStakingEvents.ts b/src/services/DappsStakingEvents.ts index a9aac2c..70e9819 100644 --- a/src/services/DappsStakingEvents.ts +++ b/src/services/DappsStakingEvents.ts @@ -3,7 +3,7 @@ import axios from 'axios'; import { formatEther } from 'ethers'; import { NetworkType } from '../networks'; import { Guard } from '../guard'; -import { TotalAmountCount, Triplet, Pair, PeriodType, ServiceBase, List } from './ServiceBase'; +import { TotalAmountCount, Triplet, Pair, PeriodType, List } from './ServiceBase'; import { IApiFactory } from '../client/ApiFactory'; import { ContainerTypes } from '../containertypes'; import { @@ -16,6 +16,7 @@ import { StakerPeriodTotalResponse, } from './DappStaking/ResponseData'; import { IStatsIndexerService } from './StatsIndexerService'; +import { DappStakingV3IndexerBase } from './DappStakingV3IndexerBase'; export interface IDappsStakingEvents { getDapps(network: NetworkType): Promise<[]>; @@ -55,7 +56,7 @@ BigInt.prototype.toJSON = function () { }; @injectable() -export class DappsStakingEvents extends ServiceBase implements IDappsStakingEvents { +export class DappsStakingEvents extends DappStakingV3IndexerBase implements IDappsStakingEvents { constructor( @inject(ContainerTypes.ApiFactory) private _apiFactory: IApiFactory, @inject(ContainerTypes.StatsIndexerService) private _statsService: IStatsIndexerService, @@ -155,9 +156,7 @@ export class DappsStakingEvents extends ServiceBase implements IDappsStakingEven } public async getDappStakingTvl(network: NetworkType, period: PeriodType): Promise { - if (network !== 'astar' && network !== 'shiden' && network !== 'shibuya') { - return []; - } + this.GuardNetwork(network); const range = this.getDateRange(period); @@ -201,9 +200,7 @@ export class DappsStakingEvents extends ServiceBase implements IDappsStakingEven period: PeriodType, transaction: RewardEventType, ): Promise { - if (network !== 'astar' && network !== 'shiden' && network !== 'shibuya') { - return []; - } + this.GuardNetwork(network); const range = this.getDateRange(period); @@ -242,9 +239,7 @@ export class DappsStakingEvents extends ServiceBase implements IDappsStakingEven address: string, period: PeriodType, ): Promise { - if (network !== 'astar' && network !== 'shiden' && network !== 'shibuya') { - return []; - } + this.GuardNetwork(network); const range = this.getDateRange(period); @@ -283,9 +278,7 @@ export class DappsStakingEvents extends ServiceBase implements IDappsStakingEven contractAddress: string, period: PeriodType, ): Promise { - if (network !== 'astar' && network !== 'shiden' && network !== 'shibuya') { - return []; - } + this.GuardNetwork(network); const range = this.getDateRange(period); @@ -320,9 +313,7 @@ export class DappsStakingEvents extends ServiceBase implements IDappsStakingEven } public async getDappStakingStakersList(network: NetworkType, contractAddress: string): Promise { - if (network !== 'astar' && network !== 'shiden' && network !== 'shibuya') { - return []; - } + this.GuardNetwork(network); try { const result = await axios.post(this.getApiUrl(network), { @@ -362,9 +353,7 @@ export class DappsStakingEvents extends ServiceBase implements IDappsStakingEven } public async getDappStakingStakersCountTotal(network: NetworkType, period: PeriodType): Promise { - if (network !== 'astar' && network !== 'shiden' && network !== 'shibuya') { - return []; - } + this.GuardNetwork(network); const range = this.getDateRange(period); @@ -399,9 +388,7 @@ export class DappsStakingEvents extends ServiceBase implements IDappsStakingEven } public async getDappStakingStakersTotal(network: NetworkType, period: PeriodType): Promise { - if (network !== 'astar' && network !== 'shiden' && network !== 'shibuya') { - return []; - } + this.GuardNetwork(network); const range = this.getDateRange(period); @@ -431,9 +418,7 @@ export class DappsStakingEvents extends ServiceBase implements IDappsStakingEven } public async getDappStakingLockersTotal(network: NetworkType, period: PeriodType): Promise { - if (network !== 'astar' && network !== 'shiden' && network !== 'shibuya') { - return []; - } + this.GuardNetwork(network); const range = this.getDateRange(period); @@ -463,9 +448,7 @@ export class DappsStakingEvents extends ServiceBase implements IDappsStakingEven network: NetworkType, period: PeriodType, ): Promise { - if (!['astar', 'shiden', 'shibuya'].includes(network)) { - return []; - } + this.GuardNetwork(network); const range = this.getDateRange(period); @@ -518,9 +501,7 @@ export class DappsStakingEvents extends ServiceBase implements IDappsStakingEven } public async getDapps(network: NetworkType): Promise<[]> { - if (network !== 'astar' && network !== 'shiden' && network !== 'shibuya') { - return []; - } + this.GuardNetwork(network); try { const result = await axios.post(this.getApiUrl(network), { @@ -548,9 +529,7 @@ export class DappsStakingEvents extends ServiceBase implements IDappsStakingEven } public async getAggregatedPeriodData(network: NetworkType, period: number): Promise { - if (!['shibuya', 'shiden', 'astar'].includes(network)) { - throw new Error(`This method is not supported for the network ${network}`); - } + this.GuardNetwork(network); try { const result = await axios.post(this.getApiUrl(network), { @@ -574,11 +553,8 @@ export class DappsStakingEvents extends ServiceBase implements IDappsStakingEven network: NetworkType, stakerAddress: string, ): Promise { - Guard.ThrowIfUndefined('network', network); Guard.ThrowIfUndefined('stakerAddress', stakerAddress); - if (!['shibuya', 'shiden', 'astar'].includes(network)) { - throw new Error(`This method is not supported for the network ${network}`); - } + this.GuardNetwork(network); try { const result = await axios.post(this.getApiUrl(network), { @@ -604,6 +580,8 @@ export class DappsStakingEvents extends ServiceBase implements IDappsStakingEven network: NetworkType, stakerAddress: string, ): Promise { + this.GuardNetwork(network); + const data = await this.getAggregatedStakerData(network, stakerAddress); let maxPeriod = -1; const total = data.reduce( @@ -626,11 +604,4 @@ export class DappsStakingEvents extends ServiceBase implements IDappsStakingEven return total; } - - private getApiUrl(network: NetworkType): string { - // For local development: `http://localhost:4350/graphql`; - return ['astar', 'shiden', 'shibuya'].includes(network) - ? `https://astar-network.squids.live/dapps-staking-indexer-${network}/graphql` - : ''; - } }