diff --git a/api/src/ol/models/community-wallet.model.ts b/api/src/ol/community-wallets/community-wallet.model.ts similarity index 100% rename from api/src/ol/models/community-wallet.model.ts rename to api/src/ol/community-wallets/community-wallet.model.ts diff --git a/api/src/ol/community-wallets/community-wallets.resolver.ts b/api/src/ol/community-wallets/community-wallets.resolver.ts index 2f667aa..91cd4d3 100644 --- a/api/src/ol/community-wallets/community-wallets.resolver.ts +++ b/api/src/ol/community-wallets/community-wallets.resolver.ts @@ -1,66 +1,20 @@ import { Query, Resolver } from "@nestjs/graphql"; import _ from "lodash"; -import { GqlCommunityWallet } from "../models/community-wallet.model.js"; -import { communityWallets } from "./community-wallets.js"; -import { OlService } from "../ol.service.js"; -import { parseAddress } from "../../utils.js"; +import { Inject } from "@nestjs/common"; + +import { GqlCommunityWallet } from "./community-wallet.model.js"; +import { ICommunityWalletsService } from "./interfaces.js"; +import { Types } from "../../types.js"; @Resolver() export class CommunityWalletsResolver { - public constructor(private readonly olService: OlService) {} + public constructor( + @Inject(Types.ICommunityWalletsService) + private readonly communityWalletsService: ICommunityWalletsService, + ) {} @Query(() => [GqlCommunityWallet]) async communityWallets(): Promise { - const donorVoiceRegistry = - (await this.olService.aptosClient.getAccountResource( - "0x1", - "0x1::donor_voice::Registry", - )) as { - type: "0x1::donor_voice::Registry"; - data: { - liquidation_queue: []; - list: string[]; - }; - }; - - const addresses = donorVoiceRegistry.data.list.map((address) => - parseAddress(address).toString("hex").toUpperCase(), - ); - - const res = addresses.map((address) => { - const addrBuff = parseAddress(address); - const addr = addrBuff.toString("hex").toUpperCase(); - const info = communityWallets.get(addr); - - return new GqlCommunityWallet({ - address: addrBuff, - name: info?.name, - description: info?.description, - }); - }); - - const groups = _.groupBy(res, (wallet) => { - if (wallet.name && wallet.description) { - return "nameAndDescription"; - } - if (wallet.name) { - return "nameOnly"; - } - if (wallet.description) { - return "descriptionOnly"; - } - return "rest"; - }); - - const { nameAndDescription, nameOnly, descriptionOnly, rest } = groups; - const sorter = (wallet: GqlCommunityWallet) => - wallet.address.toString("hex"); - - return [ - ..._.sortBy(nameAndDescription, sorter), - ..._.sortBy(nameOnly, sorter), - ..._.sortBy(descriptionOnly, sorter), - ..._.sortBy(rest, sorter), - ]; + return this.communityWalletsService.getCommunityWallets(); } } diff --git a/api/src/ol/community-wallets/community-wallets.service.ts b/api/src/ol/community-wallets/community-wallets.service.ts new file mode 100644 index 0000000..b4e004b --- /dev/null +++ b/api/src/ol/community-wallets/community-wallets.service.ts @@ -0,0 +1,67 @@ +import { Injectable } from "@nestjs/common"; +import _ from "lodash"; + +import { OlService } from "../ol.service.js"; +import { GqlCommunityWallet } from "./community-wallet.model.js"; +import { parseAddress } from "../../utils.js"; +import { communityWallets } from "./community-wallets.js"; +import { ICommunityWalletsService } from "./interfaces.js"; + +@Injectable() +export class CommunityWalletsService implements ICommunityWalletsService { + public constructor(private readonly olService: OlService) {} + + public async getCommunityWallets(): Promise { + const donorVoiceRegistry = + (await this.olService.aptosClient.getAccountResource( + "0x1", + "0x1::donor_voice::Registry", + )) as { + type: "0x1::donor_voice::Registry"; + data: { + liquidation_queue: []; + list: string[]; + }; + }; + + const addresses = donorVoiceRegistry.data.list.map((address) => + parseAddress(address).toString("hex").toUpperCase(), + ); + + const res = addresses.map((address) => { + const addrBuff = parseAddress(address); + const addr = addrBuff.toString("hex").toUpperCase(); + const info = communityWallets.get(addr); + + return new GqlCommunityWallet({ + address: addrBuff, + name: info?.name, + description: info?.description, + }); + }); + + const groups = _.groupBy(res, (wallet) => { + if (wallet.name && wallet.description) { + return "nameAndDescription"; + } + if (wallet.name) { + return "nameOnly"; + } + if (wallet.description) { + return "descriptionOnly"; + } + return "rest"; + }); + + const { nameAndDescription, nameOnly, descriptionOnly, rest } = groups; + const sorter = (wallet: GqlCommunityWallet) => + wallet.address.toString("hex"); + + return [ + ..._.sortBy(nameAndDescription, sorter), + ..._.sortBy(nameOnly, sorter), + ..._.sortBy(descriptionOnly, sorter), + ..._.sortBy(rest, sorter), + ]; + } +} diff --git a/api/src/ol/community-wallets/interfaces.ts b/api/src/ol/community-wallets/interfaces.ts new file mode 100644 index 0000000..e765096 --- /dev/null +++ b/api/src/ol/community-wallets/interfaces.ts @@ -0,0 +1,5 @@ +import { GqlCommunityWallet } from "./community-wallet.model.js"; + +export interface ICommunityWalletsService { + getCommunityWallets(): Promise; +} \ No newline at end of file diff --git a/api/src/ol/ol.module.ts b/api/src/ol/ol.module.ts index 0760ffc..c9155ce 100644 --- a/api/src/ol/ol.module.ts +++ b/api/src/ol/ol.module.ts @@ -35,6 +35,7 @@ import { MovementsResolver } from "./movements/movements.resolver.js"; import { MovementsService } from "./movements/movements.service.js"; import { CommunityWalletsResolver } from "./community-wallets/community-wallets.resolver.js"; +import { CommunityWalletsService } from "./community-wallets/community-wallets.service.js"; import { TransactionsResolver } from "./transactions/TransactionsResolver.js"; import { TransactionResolver } from "./transactions/TransactionResolver.js"; @@ -107,7 +108,12 @@ for (const role of roles) { ValidatorResolver, ValidatorsResolver, + CommunityWalletsResolver, + { + provide: Types.ICommunityWalletsService, + useClass: CommunityWalletsService, + }, OlService, MovementsService, @@ -141,6 +147,6 @@ for (const role of roles) { ...workers, ], controllers: [OlController], - exports: [OlService, TransformerService, CommunityWalletsResolver], + exports: [OlService, TransformerService, Types.ICommunityWalletsService], }) export class OlModule {} diff --git a/api/src/stats/stats.service.ts b/api/src/stats/stats.service.ts index 780b832..0dd6750 100644 --- a/api/src/stats/stats.service.ts +++ b/api/src/stats/stats.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from "@nestjs/common"; +import { Inject, Injectable } from "@nestjs/common"; import { ConfigService } from "@nestjs/config"; import axios from "axios"; @@ -14,8 +14,9 @@ import { } from "./types.js"; import { ClickhouseService } from "../clickhouse/clickhouse.service.js"; import { OlService } from "../ol/ol.service.js"; +import { ICommunityWalletsService } from "../ol/community-wallets/interfaces.js"; +import { Types } from "../types.js"; import _ from "lodash"; -import { CommunityWalletsResolver } from "../ol/community-wallets.resolver.js"; @Injectable() export class StatsService { @@ -24,7 +25,10 @@ export class StatsService { public constructor( private readonly clickhouseService: ClickhouseService, private readonly olService: OlService, - private readonly communityWalletsResolver: CommunityWalletsResolver, + + @Inject(Types.ICommunityWalletsService) + private readonly communityWalletsService: ICommunityWalletsService, + config: ConfigService, ) { this.dataApiHost = config.get("dataApiHost")!; @@ -236,39 +240,6 @@ export class StatsService { return rows; } - // Calculates the libra balances of all accounts - private async getTotalLibraBalances(): Promise { - try { - const query = ` - SELECT - SUM(latest_balance) / 1e6 AS total_balance - FROM ( - SELECT - argMax(balance, version) AS latest_balance - FROM coin_balance - WHERE coin_module = 'libra_coin' - GROUP BY address - ) - `; - - const resultSet = await this.clickhouseService.client.query({ - query: query, - format: "JSONEachRow", - }); - - const result = await resultSet.json<{ total_balance: number }>(); - - // Assuming there's only one row returned - if (result.length > 0) { - return result[0].total_balance; - } - return 0; - } catch (error) { - console.error("Error in getTotalBalances:", error); - throw error; - } - } - private async getCommunityWalletsBalanceBreakdown(): Promise { // List of addresses and their names const addressNames = [ @@ -1286,7 +1257,8 @@ export class StatsService { } // Get the list of community wallets - const communityWallets = await this.communityWalletsResolver.communityWallets(); + const communityWallets = + await this.communityWalletsService.getCommunityWallets(); const communityAddresses = new Set(communityWallets.map(wallet => wallet.address.toString('hex').toUpperCase())); // Query to get the latest balances and versions from coin_balance diff --git a/api/src/types.ts b/api/src/types.ts index a28cb69..8f7c612 100644 --- a/api/src/types.ts +++ b/api/src/types.ts @@ -5,4 +5,5 @@ export enum Types { ITransactionsFactory = "ITransactionsFactory", ITransaction = "ITransaction", IOnChainTransactionsRepository = "IOnChainTransactionsRepository", + ICommunityWalletsService = "ICommunityWalletsService", } \ No newline at end of file diff --git a/web-app/src/modules/core/routes/Stats/Stats.tsx b/web-app/src/modules/core/routes/Stats/Stats.tsx index 8917e9b..4c56e95 100644 --- a/web-app/src/modules/core/routes/Stats/Stats.tsx +++ b/web-app/src/modules/core/routes/Stats/Stats.tsx @@ -231,55 +231,68 @@ const Coinstats = () => { +
-

Top 100 Liquid accounts

-
- - - - - - - - - - {data.topAccounts.map((account: { address: string; unlockedBalance: number; percentOfCirculating: number }, index: number) => ( - - - - +

Top 100 Liquid accounts

+ +
+
+
- Address - - Liquid Balance - - % of Circulating Supply -
- - {account.address} - - - {account.unlockedBalance} - - {account.percentOfCirculating.toFixed(4)}% -
+ + + + + - ))} - -
+ Address + + Liquid Balance + + % of Circulating Supply +
+ + + {data.topAccounts.map( + ( + account: { + address: string; + unlockedBalance: number; + percentOfCirculating: number; + }, + index: number, + ) => ( + + + + {account.address} + + + + {account.unlockedBalance} + + + {account.percentOfCirculating.toFixed(4)}% + + + ), + )} + + +
diff --git a/web-app/src/modules/ui/Layout/Footer/Footer.tsx b/web-app/src/modules/ui/Layout/Footer/Footer.tsx index f105cd8..6391f6e 100644 --- a/web-app/src/modules/ui/Layout/Footer/Footer.tsx +++ b/web-app/src/modules/ui/Layout/Footer/Footer.tsx @@ -7,7 +7,7 @@ const CI_COMMIT_SHA: string = import.meta.env.VITE_CI_COMMIT_SHA; const Footer: React.FC = () => { return ( -