From 6fdf21d344b96dfddc4c24cf0cada1e9ad35be83 Mon Sep 17 00:00:00 2001 From: shane-moore Date: Tue, 20 Aug 2024 14:04:32 -0700 Subject: [PATCH] chore: token holders api --- .../grpc/IndexerGrpcPortfolioApi.spec.ts | 24 ++++++++++ .../indexer/grpc/IndexerGrpcPortfolioApi.ts | 47 +++++++++++++++++++ .../IndexerAccountPortfolioTransformer.ts | 17 +++++-- .../client/indexer/types/account-portfolio.ts | 11 +++++ 4 files changed, 96 insertions(+), 3 deletions(-) diff --git a/packages/sdk-ts/src/client/indexer/grpc/IndexerGrpcPortfolioApi.spec.ts b/packages/sdk-ts/src/client/indexer/grpc/IndexerGrpcPortfolioApi.spec.ts index 17dad7a41..96b9fb213 100644 --- a/packages/sdk-ts/src/client/indexer/grpc/IndexerGrpcPortfolioApi.spec.ts +++ b/packages/sdk-ts/src/client/indexer/grpc/IndexerGrpcPortfolioApi.spec.ts @@ -1,4 +1,5 @@ import { getNetworkEndpoints, Network } from '@injectivelabs/networks' +import { INJ_DENOM } from '@injectivelabs/utils' import { mockFactory } from '@injectivelabs/test-utils' import { IndexerGrpcAccountPortfolioTransformer } from '../transformers' import { IndexerGrpcAccountPortfolioApi } from './IndexerGrpcPortfolioApi' @@ -32,3 +33,26 @@ describe('IndexerGrpcAccountPortfolioApi', () => { } }) }) + +test('fetchAccountPortfolioTokenHolders', async () => { + try { + const response = + await indexerGrpcPortfolioApi.fetchAccountPortfolioTokenHolders({ + denom: INJ_DENOM, + }) + + expect(response).toBeDefined() + expect(response).toEqual( + expect.objectContaining< + ReturnType< + typeof IndexerGrpcAccountPortfolioTransformer.tokenHoldersResponseToTokenHolders + > + >(response), + ) + } catch (e) { + console.error( + 'IndexerGrpcAccountPortfolioApi.fetchAccountPortfolioTokenHolders => ' + + (e as any).message, + ) + } +}) diff --git a/packages/sdk-ts/src/client/indexer/grpc/IndexerGrpcPortfolioApi.ts b/packages/sdk-ts/src/client/indexer/grpc/IndexerGrpcPortfolioApi.ts index 7b532fb90..26f4a71c1 100644 --- a/packages/sdk-ts/src/client/indexer/grpc/IndexerGrpcPortfolioApi.ts +++ b/packages/sdk-ts/src/client/indexer/grpc/IndexerGrpcPortfolioApi.ts @@ -104,4 +104,51 @@ export class IndexerGrpcAccountPortfolioApi extends BaseGrpcConsumer { }) } } + + async fetchAccountPortfolioTokenHolders({ + denom, + cursor, + limit, + }: { + denom: string + cursor?: string + limit?: number + }) { + const request = InjectivePortfolioRpc.TokenHoldersRequest.create() + + request.denom = denom + + if (cursor) { + request.cursor = cursor + } + + if (limit) { + request.limit = limit + } + + try { + const response = + await this.retry(() => + this.client.TokenHolders(request), + ) + + return IndexerGrpcAccountPortfolioTransformer.tokenHoldersResponseToTokenHolders( + response, + ) + } catch (e: unknown) { + if (e instanceof InjectivePortfolioRpc.GrpcWebError) { + throw new GrpcUnaryRequestException(new Error(e.toString()), { + code: e.code, + context: 'TokenHolders', + contextModule: this.module, + }) + } + + throw new GrpcUnaryRequestException(e as Error, { + code: UnspecifiedErrorCode, + context: 'TokenHolders', + contextModule: this.module, + }) + } + } } diff --git a/packages/sdk-ts/src/client/indexer/transformers/IndexerAccountPortfolioTransformer.ts b/packages/sdk-ts/src/client/indexer/transformers/IndexerAccountPortfolioTransformer.ts index 149c96625..4f269b2e6 100644 --- a/packages/sdk-ts/src/client/indexer/transformers/IndexerAccountPortfolioTransformer.ts +++ b/packages/sdk-ts/src/client/indexer/transformers/IndexerAccountPortfolioTransformer.ts @@ -1,6 +1,7 @@ import { Coin } from '@injectivelabs/ts-types' import { GrpcCoin } from '../../../types' import { + TokenHolders, PositionsWithUPNL, AccountPortfolioV2, SubaccountDepositV2, @@ -87,9 +88,7 @@ export class IndexerGrpcAccountPortfolioTransformer { return { position: grpcPosition - ? IndexerGrpcDerivativeTransformer.grpcPositionToPosition( - grpcPosition, - ) + ? IndexerGrpcDerivativeTransformer.grpcPositionToPosition(grpcPosition) : undefined, unrealizedPnl: positionsWithUPNL.unrealizedPnl, } @@ -119,4 +118,16 @@ export class IndexerGrpcAccountPortfolioTransformer { : undefined, } } + + static tokenHoldersResponseToTokenHolders( + response: InjectivePortfolioRpc.TokenHoldersResponse, + ): TokenHolders { + return { + holders: response.holders.map((holder) => ({ + accountAddress: holder.accountAddress, + balance: holder.balance, + })), + nextCursors: response.nextCursors, + } + } } diff --git a/packages/sdk-ts/src/client/indexer/types/account-portfolio.ts b/packages/sdk-ts/src/client/indexer/types/account-portfolio.ts index dc80eccaf..2c2199532 100644 --- a/packages/sdk-ts/src/client/indexer/types/account-portfolio.ts +++ b/packages/sdk-ts/src/client/indexer/types/account-portfolio.ts @@ -31,9 +31,20 @@ export interface AccountPortfolioBalances { subaccountsList: PortfolioSubaccountBalanceV2[] } +export interface Holder { + accountAddress: string + balance: string +} + +export interface TokenHolders { + holders: Holder[] + nextCursors: string[] +} + export type GrpcPositionV2 = InjectivePortfolioRpc.DerivativePosition export type GrpcAccountPortfolioV2 = InjectivePortfolioRpc.Portfolio export type GrpcSubaccountDepositV2 = InjectivePortfolioRpc.SubaccountDeposit export type GrpcPositionsWithUPNL = InjectivePortfolioRpc.PositionsWithUPNL export type GrpcPortfolioSubaccountBalanceV2 = InjectivePortfolioRpc.SubaccountBalanceV2 +export type GrpcTokenHolders = InjectivePortfolioRpc.TokenHoldersResponse