Skip to content

Commit 300daed

Browse files
authored
Fix totalSupply and moduleAccounts (#47)
## Summary - Added getQueryClient to enable querying data from the block currently being indexed. - Updated the supply logic to save denoms that are not yet stored in the database, removing the need for hardcoding. - Fixed the StakedSuppliersByBlockAndService and StakedAppsByBlockAndService reports.
1 parent c41c9e7 commit 300daed

File tree

5 files changed

+124
-57
lines changed

5 files changed

+124
-57
lines changed

src/mappings/bank/moduleAccounts.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
getBlockId,
1010
} from "../utils/ids";
1111
import { stringify } from "../utils/json";
12+
import getQueryClient from "../utils/query_client";
1213
import { enforceAccountsExists } from "./balanceChange";
1314

1415
export type ExtendedAccount = ModuleAccount & {
@@ -25,15 +26,15 @@ export function getModuleAccountProps(account: ModuleAccount): ModuleAccountProp
2526
};
2627
}
2728

28-
export async function queryModuleAccounts(): Promise<Array<ExtendedAccount>> {
29+
export async function queryModuleAccounts(block: CosmosBlock): Promise<Array<ExtendedAccount>> {
2930
// Here we force the use of a private property, breaking TypeScript limitation, due to the need of call a total supply
3031
// rpc query of @cosmjs that is not exposed on the implemented client by SubQuery team.
3132
// To avoid this, we need to move to implement our own rpc client and also use `unsafe` parameter which I prefer to avoid.
3233

3334
// In this opportunity this moduleAccounts() function is only existent over a fork made by pokt-scan/cosmjs to include this.
3435
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
3536
// @ts-ignore
36-
const queryClient = api.forceGetQueryClient();
37+
const queryClient = getQueryClient(block.header.height);
3738

3839
const accounts = await queryClient.auth.moduleAccounts() as Array<Any>;
3940

@@ -60,7 +61,7 @@ export async function queryModuleAccounts(): Promise<Array<ExtendedAccount>> {
6061
export async function handleModuleAccounts(block: CosmosBlock): Promise<Set<string>> {
6162
const blockId = getBlockId(block);
6263
const moduleAccountsSet: Set<string> = new Set();
63-
const moduleAccounts = await queryModuleAccounts();
64+
const moduleAccounts = await queryModuleAccounts(block);
6465
const accounts = [];
6566

6667
for (const moduleAccount of moduleAccounts) {

src/mappings/bank/supply.ts

Lines changed: 39 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { BlockSupplyProps } from "../../types/models/BlockSupply";
1212
import { fetchPaginatedRecords } from "../utils/db";
1313
import { getBlockId } from "../utils/ids";
1414
import { stringify } from "../utils/json";
15+
import getQueryClient from "../utils/query_client";
1516

1617
export const getSupplyId = function(denom: string, height: number): string {
1718
return `${denom}@${height}`;
@@ -25,7 +26,7 @@ export const getSupplyRecord = function(supply: Coin, block: CosmosBlock): Suppl
2526
});
2627
};
2728

28-
export async function queryTotalSupply(): Promise<Coin[]> {
29+
export async function queryTotalSupply(block: CosmosBlock): Promise<Coin[]> {
2930
logger.debug(`[handleSupply] querying total supply`);
3031
const finalSupply: Coin[] = [];
3132
let paginationKey: Uint8Array | undefined;
@@ -35,7 +36,7 @@ export async function queryTotalSupply(): Promise<Coin[]> {
3536
// To avoid this, we need to move to implement our own rpc client and also use `unsafe` parameter which I prefer to avoid.
3637
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
3738
// @ts-ignore
38-
const queryClient = api.forceGetQueryClient();
39+
const queryClient = getQueryClient(block.header.height);
3940

4041
// Initial call to get the first set of results
4142
const initialResponse: QueryTotalSupplyResponse = await queryClient.bank.totalSupply() as unknown as QueryTotalSupplyResponse;
@@ -69,57 +70,48 @@ export async function fetchAllSupplyDenom(): Promise<SupplyDenom[]> {
6970

7071
// handleSupply, referenced in project.ts, handles supply information from block
7172
export async function handleSupply(block: CosmosBlock): Promise<void> {
72-
const supplyDenoms = await fetchAllSupplyDenom();
73+
const [supplyDenoms, totalSupply] = await Promise.all([
74+
fetchAllSupplyDenom(),
75+
// TODO: (@jorgecuesta) we should update supply handling with proper msg/event once it is implemented on pocket
76+
queryTotalSupply(block)
77+
]);
78+
79+
const denominationsSaved = supplyDenoms.map(supplyDenom => supplyDenom.id)
80+
81+
// here we need to create the denoms that are not saved in the database
82+
if (totalSupply.some(supply => !denominationsSaved.includes(supply.denom))) {
83+
const supplyDenomsToSave = totalSupply.filter(supply => !denominationsSaved.includes(supply.denom)).map(supply => SupplyDenom.create({id: supply.denom}));
84+
85+
await store.bulkCreate(
86+
"SupplyDenom",
87+
supplyDenomsToSave
88+
);
89+
90+
supplyDenoms.push(...supplyDenomsToSave);
91+
}
92+
7393
const supplyIdHeight = block.header.height === 1 ? block.header.height : block.header.height - 1;
7494

7595
const blockSuppliesMap: Map<string, BlockSupplyProps> = new Map();
7696

77-
if (supplyDenoms.length === 0) {
78-
// if this happens is because we may start in a higher block than genesis.
79-
// note: we may need to evaluate genesis in more than just block == genesis.block
80-
// like, for example, check if we already have a genesis parsed.
81-
const supplyDenom = SupplyDenom.create({ id: "upokt" });
82-
await supplyDenom.save();
83-
supplyDenoms.push(supplyDenom);
84-
}
97+
for (const supplyDenom of supplyDenoms) {
98+
const blockSupply = await BlockSupply.get(
99+
getSupplyId(supplyDenom.id, supplyIdHeight),
100+
);
85101

86-
if (block.header.height > 1) {
87-
// on any block after genesis, we need to look up for the previous BlockSupply to copy the supply id of the
88-
// right one; then the claim/proof settlement or ibc txs will update to the right supply id if a new one
89-
// is created for this denom@block
90-
for (const supplyDenom of supplyDenoms) {
91-
const blockSupply = await BlockSupply.get(
92-
getSupplyId(supplyDenom.id, supplyIdHeight),
93-
);
94-
95-
const blockSupplyId = getSupplyId(supplyDenom.id, block.header.height);
96-
97-
// normally this should not be null,
98-
// but if you start syncing from a greater height than genesis, probably will not exist
99-
const supplyId = blockSupply ? blockSupply.supplyId : blockSupplyId;
100-
101-
const blockSupplyProps = {
102-
id: blockSupplyId,
103-
blockId: getBlockId(block),
104-
supplyId,
105-
};
106-
blockSuppliesMap.set(blockSupplyId, blockSupplyProps);
107-
}
108-
} else {
109-
// create a base record for each supply denomination because is the first block.
110-
supplyDenoms.forEach((supplyDenom) => {
111-
const blockSupplyId = getSupplyId(supplyDenom.id, block.header.height);
112-
const blockSupplyProps = {
113-
id: blockSupplyId,
114-
blockId: getBlockId(block),
115-
supplyId: getSupplyId(supplyDenom.id, supplyIdHeight),
116-
};
117-
blockSuppliesMap.set(blockSupplyId, blockSupplyProps);
118-
});
119-
}
102+
const blockSupplyId = getSupplyId(supplyDenom.id, block.header.height);
120103

121-
// TODO: (@jorgecuesta) we should update supply handling with proper msg/event once it is implemented on pocket
122-
const totalSupply = await queryTotalSupply();
104+
// normally this should not be null,
105+
// but if you start syncing from a greater height than genesis, probably will not exist
106+
const supplyId = blockSupply ? blockSupply.supplyId : blockSupplyId;
107+
108+
const blockSupplyProps = {
109+
id: blockSupplyId,
110+
blockId: getBlockId(block),
111+
supplyId,
112+
};
113+
blockSuppliesMap.set(blockSupplyId, blockSupplyProps);
114+
}
123115

124116
if (totalSupply.length === 0) {
125117
throw new Error(`[handleSupply]: query.totalSupply returned 0 records, block.header.height=${block.header.height}`);

src/mappings/pocket/reports.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -209,10 +209,10 @@ async function getStakedSuppliersData() {
209209
tokens: BigInt(0),
210210
amount: 0,
211211
};
212-
} else {
213-
stakedSuppliersByServiceMap[serviceId].tokens += supplier.stakeAmount;
214-
stakedSuppliersByServiceMap[serviceId].amount += 1;
215212
}
213+
214+
stakedSuppliersByServiceMap[serviceId].tokens += supplier.stakeAmount;
215+
stakedSuppliersByServiceMap[serviceId].amount += 1;
216216
}
217217
}
218218

@@ -307,10 +307,10 @@ async function getStakedAppsData() {
307307
tokens: BigInt(0),
308308
amount: 0,
309309
};
310-
} else {
311-
stakedAppsByServiceMap[serviceId].tokens += app.stakeAmount;
312-
stakedAppsByServiceMap[serviceId].amount += 1;
313310
}
311+
312+
stakedAppsByServiceMap[serviceId].tokens += app.stakeAmount;
313+
stakedAppsByServiceMap[serviceId].amount += 1;
314314
}
315315
}
316316

src/mappings/primitives/genesis.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ export async function handleGenesis(block: CosmosBlock): Promise<void> {
187187
}
188188

189189
async function _handleModuleAccounts(block: CosmosBlock): Promise<void> {
190-
const moduleAccounts = await queryModuleAccounts();
190+
const moduleAccounts = await queryModuleAccounts(block);
191191

192192
const accounts: Array<EnforceAccountExistenceParams> = [];
193193
// const mAccounts: Array<ModuleAccountProps> = [];

src/mappings/utils/query_client.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { createPagination, ProtobufRpcClient, QueryClient } from "@cosmjs/stargate";
2+
import {QueryClientImpl as AuthQueryClientImpl} from 'cosmjs-types/cosmos/auth/v1beta1/query'
3+
import {
4+
QueryAllBalancesRequest,
5+
QueryClientImpl as BankQueryClientImpl,
6+
QueryTotalSupplyResponse,
7+
} from "cosmjs-types/cosmos/bank/v1beta1/query";
8+
import { Coin } from "cosmjs-types/cosmos/base/v1beta1/coin";
9+
import { Any } from "cosmjs-types/google/protobuf/any";
10+
11+
interface PocketdexExtension {
12+
readonly bank: {
13+
readonly totalSupply: (paginationKey?: Uint8Array) => Promise<QueryTotalSupplyResponse>;
14+
readonly allBalances: (address: string) => Promise<Coin[]>;
15+
}
16+
readonly auth: {
17+
readonly moduleAccounts: () => Promise<Any[]>;
18+
}
19+
}
20+
21+
export function createProtobufRpcClient(base: QueryClient, height?: number): ProtobufRpcClient {
22+
return {
23+
request: async (service: string, method: string, data: Uint8Array): Promise<Uint8Array> => {
24+
const path = `/${service}/${method}`;
25+
const response = await base.queryAbci(path, data, height);
26+
return response.value;
27+
},
28+
};
29+
}
30+
31+
const setupPocketdexExtension = (height?: number) => (base: QueryClient): PocketdexExtension => {
32+
const rpc = createProtobufRpcClient(base, height);
33+
34+
// Use this service to get easy typed access to query methods
35+
// This cannot be used for proof verification
36+
const bankQueryService = new BankQueryClientImpl(rpc);
37+
const authQueryService = new AuthQueryClientImpl(rpc);
38+
39+
return {
40+
bank: {
41+
totalSupply: async (paginationKey?: Uint8Array) => {
42+
return bankQueryService.TotalSupply({
43+
pagination: createPagination(paginationKey),
44+
});
45+
},
46+
allBalances: async (address: string) => {
47+
const { balances } = await bankQueryService.AllBalances(
48+
QueryAllBalancesRequest.fromPartial({ address: address }),
49+
);
50+
return balances;
51+
},
52+
},
53+
auth: {
54+
moduleAccounts: async () => {
55+
const { accounts } = await authQueryService.ModuleAccounts();
56+
return accounts ?? [];
57+
}
58+
}
59+
};
60+
}
61+
62+
// The purpose of creating a new query client instead of using the one injected by subql
63+
// is because with the injected one, we cannot pass a custom height so it always queries the latest height.
64+
// With this new query client, we can pass a custom height, and it will query the data for the block that is being indexed.
65+
export default function getQueryClient(height: number): QueryClient & PocketdexExtension {
66+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
67+
// @ts-ignore
68+
const cometClient = api.forceGetCometClient();
69+
70+
return QueryClient.withExtensions(
71+
cometClient,
72+
setupPocketdexExtension(height),
73+
)
74+
}

0 commit comments

Comments
 (0)