From 683ed3b4413ebcbe2493e02840111309d7022b32 Mon Sep 17 00:00:00 2001 From: Gregory Luneau Date: Thu, 18 Jan 2024 20:02:11 -0500 Subject: [PATCH 1/2] list of stakers per dapp with total stake --- public/swagger.json | 34 +++++++++++++ src/controllers/DappsStakingV3Controller.ts | 26 ++++++++++ src/services/DappsStakingEvents.ts | 53 ++++++++++++++++++++- src/services/ServiceBase.ts | 1 + 4 files changed, 113 insertions(+), 1 deletion(-) diff --git a/public/swagger.json b/public/swagger.json index f133195..2e207d2 100644 --- a/public/swagger.json +++ b/public/swagger.json @@ -711,6 +711,40 @@ } } }, + "/api/v3/{network}/dapps-staking/stakerslist/{contractAddress}": { + "get": { + "tags": [ + "Dapps Staking" + ], + "description": "Retrieves dapps staking stakers list for a given network and period.", + "parameters": [ + { + "name": "network", + "in": "path", + "required": true, + "type": "string", + "description": "The network name. Supported networks: astar", + "enum": [ + "astar", + "shiden", + "shibuya" + ] + }, + { + "name": "contractAddress", + "in": "path", + "required": true, + "type": "string", + "description": "Contract address to get stats for" + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, "/api/v3/{network}/dapps-staking/chaindapps": { "get": { "tags": [ diff --git a/src/controllers/DappsStakingV3Controller.ts b/src/controllers/DappsStakingV3Controller.ts index e3fd3a4..c20f4d3 100644 --- a/src/controllers/DappsStakingV3Controller.ts +++ b/src/controllers/DappsStakingV3Controller.ts @@ -75,6 +75,32 @@ export class DappsStakingV3Controller extends ControllerBase implements IControl }, ); + app.route('/api/v3/:network/dapps-staking/stakerslist/:contractAddress').get( + async (req: Request, res: Response) => { + /* + #swagger.description = 'Retrieves dapps staking stakers list for a given network and period.' + #swagger.tags = ['Dapps Staking'] + #swagger.parameters['network'] = { + in: 'path', + description: 'The network name. Supported networks: astar', + required: true, + enum: ['astar', 'shiden', 'shibuya'] + } + #swagger.parameters['contractAddress'] = { + in: 'path', + description: 'Contract address to get stats for', + required: true + } + */ + res.json( + await this._dappsStakingEvents.getDappStakingStakersList( + req.params.network as NetworkType, + req.params.contractAddress as string, + ), + ); + }, + ); + app.route('/api/v3/:network/dapps-staking/chaindapps').get(async (req: Request, res: Response) => { /* #swagger.description = 'Retrieves list of dapps (basic info) registered for dapps staking' diff --git a/src/services/DappsStakingEvents.ts b/src/services/DappsStakingEvents.ts index fd584f1..9e471ad 100644 --- a/src/services/DappsStakingEvents.ts +++ b/src/services/DappsStakingEvents.ts @@ -2,7 +2,7 @@ import { injectable } from 'inversify'; import axios from 'axios'; import { NetworkType } from '../networks'; import { Guard } from '../guard'; -import { Pair, PeriodType, ServiceBase } from './ServiceBase'; +import { Pair, PeriodType, ServiceBase, List } from './ServiceBase'; import { DappStakingEventData, DappStakingEventResponse, @@ -23,8 +23,18 @@ export interface IDappsStakingEvents { getAggregatedData(network: NetworkType, period: PeriodType): Promise; getDappStakingTvl(network: NetworkType, period: PeriodType): Promise; getDappStakingStakersCount(network: NetworkType, contractAddress: string, period: PeriodType): Promise; + getDappStakingStakersList(network: NetworkType, contractAddress: string): Promise; } +declare global { + interface BigInt { + toJSON: () => string; + } +} +BigInt.prototype.toJSON = function () { + return this.toString(); +}; + @injectable() export class DappsStakingEvents extends ServiceBase implements IDappsStakingEvents { public async getStakingEvents( @@ -175,6 +185,47 @@ export class DappsStakingEvents extends ServiceBase implements IDappsStakingEven } } + public async getDappStakingStakersList(network: NetworkType, contractAddress: string): Promise { + if (network !== 'astar' && network !== 'shiden' && network !== 'shibuya') { + return []; + } + + try { + const result = await axios.post(this.getApiUrl(network), { + query: `query { + stakes( + where: { + dappAddress_eq: "${contractAddress}" + } + ) { + stakerAddress + amount + } + }`, + }); + + const sumsByStaker: { [key: string]: bigint } = result.data.data.stakes.reduce( + (acc: { [key: string]: bigint }, { stakerAddress, amount }: List) => { + acc[stakerAddress] = (acc[stakerAddress] || BigInt(0)) + BigInt(amount); + return acc; + }, + {}, + ); + + const stakersList: List[] = Object.entries(sumsByStaker) + .map(([stakerAddress, amount]) => ({ + stakerAddress, + amount, + })) + .filter((staker) => staker.amount !== BigInt(0)); + + return stakersList; + } catch (e) { + console.error(e); + return []; + } + } + public async getDapps(network: NetworkType): Promise<[]> { if (network !== 'astar' && network !== 'shiden' && network !== 'shibuya') { return []; diff --git a/src/services/ServiceBase.ts b/src/services/ServiceBase.ts index 0ed53a8..fa3a844 100644 --- a/src/services/ServiceBase.ts +++ b/src/services/ServiceBase.ts @@ -3,6 +3,7 @@ import { injectable } from 'inversify'; export type PeriodType = '1 day' | '7 days' | '30 days' | '90 days' | '1 year'; export type PeriodTypeEra = '7 eras' | '30 eras' | '90 eras' | 'all'; export type Pair = { date: number; value: number }; +export type List = { stakerAddress: string; amount: bigint }; export type DateRange = { start: Date; end: Date }; const DEFAULT_RANGE_LENGTH_DAYS = 7; From b599a7f8139855e65dde96249a29c810300bfbea Mon Sep 17 00:00:00 2001 From: Gregory Luneau Date: Thu, 18 Jan 2024 20:52:28 -0500 Subject: [PATCH 2/2] not expired --- src/services/DappsStakingEvents.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/services/DappsStakingEvents.ts b/src/services/DappsStakingEvents.ts index 9e471ad..ca7e91f 100644 --- a/src/services/DappsStakingEvents.ts +++ b/src/services/DappsStakingEvents.ts @@ -195,6 +195,7 @@ export class DappsStakingEvents extends ServiceBase implements IDappsStakingEven query: `query { stakes( where: { + expiredAt_isNull: true, dappAddress_eq: "${contractAddress}" } ) {