Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/mappings/bank/moduleAccounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
getBlockId,
} from "../utils/ids";
import { stringify } from "../utils/json";
import getQueryClient from "../utils/query_client";
import { enforceAccountsExists } from "./balanceChange";

export type ExtendedAccount = ModuleAccount & {
Expand All @@ -25,15 +26,15 @@ export function getModuleAccountProps(account: ModuleAccount): ModuleAccountProp
};
}

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

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

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

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

for (const moduleAccount of moduleAccounts) {
Expand Down
86 changes: 39 additions & 47 deletions src/mappings/bank/supply.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { BlockSupplyProps } from "../../types/models/BlockSupply";
import { fetchPaginatedRecords } from "../utils/db";
import { getBlockId } from "../utils/ids";
import { stringify } from "../utils/json";
import getQueryClient from "../utils/query_client";

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

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

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

// handleSupply, referenced in project.ts, handles supply information from block
export async function handleSupply(block: CosmosBlock): Promise<void> {
const supplyDenoms = await fetchAllSupplyDenom();
const [supplyDenoms, totalSupply] = await Promise.all([
fetchAllSupplyDenom(),
// TODO: (@jorgecuesta) we should update supply handling with proper msg/event once it is implemented on pocket
queryTotalSupply(block)
]);

const denominationsSaved = supplyDenoms.map(supplyDenom => supplyDenom.id)

// here we need to create the denoms that are not saved in the database
if (totalSupply.some(supply => !denominationsSaved.includes(supply.denom))) {
const supplyDenomsToSave = totalSupply.filter(supply => !denominationsSaved.includes(supply.denom)).map(supply => SupplyDenom.create({id: supply.denom}));

await store.bulkCreate(
"SupplyDenom",
supplyDenomsToSave
);

supplyDenoms.push(...supplyDenomsToSave);
}

const supplyIdHeight = block.header.height === 1 ? block.header.height : block.header.height - 1;

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

if (supplyDenoms.length === 0) {
// if this happens is because we may start in a higher block than genesis.
// note: we may need to evaluate genesis in more than just block == genesis.block
// like, for example, check if we already have a genesis parsed.
const supplyDenom = SupplyDenom.create({ id: "upokt" });
await supplyDenom.save();
supplyDenoms.push(supplyDenom);
}
for (const supplyDenom of supplyDenoms) {
const blockSupply = await BlockSupply.get(
getSupplyId(supplyDenom.id, supplyIdHeight),
);

if (block.header.height > 1) {
// on any block after genesis, we need to look up for the previous BlockSupply to copy the supply id of the
// right one; then the claim/proof settlement or ibc txs will update to the right supply id if a new one
// is created for this denom@block
for (const supplyDenom of supplyDenoms) {
const blockSupply = await BlockSupply.get(
getSupplyId(supplyDenom.id, supplyIdHeight),
);

const blockSupplyId = getSupplyId(supplyDenom.id, block.header.height);

// normally this should not be null,
// but if you start syncing from a greater height than genesis, probably will not exist
const supplyId = blockSupply ? blockSupply.supplyId : blockSupplyId;

const blockSupplyProps = {
id: blockSupplyId,
blockId: getBlockId(block),
supplyId,
};
blockSuppliesMap.set(blockSupplyId, blockSupplyProps);
}
} else {
// create a base record for each supply denomination because is the first block.
supplyDenoms.forEach((supplyDenom) => {
const blockSupplyId = getSupplyId(supplyDenom.id, block.header.height);
const blockSupplyProps = {
id: blockSupplyId,
blockId: getBlockId(block),
supplyId: getSupplyId(supplyDenom.id, supplyIdHeight),
};
blockSuppliesMap.set(blockSupplyId, blockSupplyProps);
});
}
const blockSupplyId = getSupplyId(supplyDenom.id, block.header.height);

// TODO: (@jorgecuesta) we should update supply handling with proper msg/event once it is implemented on pocket
const totalSupply = await queryTotalSupply();
// normally this should not be null,
// but if you start syncing from a greater height than genesis, probably will not exist
const supplyId = blockSupply ? blockSupply.supplyId : blockSupplyId;

const blockSupplyProps = {
id: blockSupplyId,
blockId: getBlockId(block),
supplyId,
};
blockSuppliesMap.set(blockSupplyId, blockSupplyProps);
}

if (totalSupply.length === 0) {
throw new Error(`[handleSupply]: query.totalSupply returned 0 records, block.header.height=${block.header.height}`);
Expand Down
12 changes: 6 additions & 6 deletions src/mappings/pocket/reports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,10 +209,10 @@ async function getStakedSuppliersData() {
tokens: BigInt(0),
amount: 0,
};
} else {
stakedSuppliersByServiceMap[serviceId].tokens += supplier.stakeAmount;
stakedSuppliersByServiceMap[serviceId].amount += 1;
}

stakedSuppliersByServiceMap[serviceId].tokens += supplier.stakeAmount;
stakedSuppliersByServiceMap[serviceId].amount += 1;
}
}

Expand Down Expand Up @@ -307,10 +307,10 @@ async function getStakedAppsData() {
tokens: BigInt(0),
amount: 0,
};
} else {
stakedAppsByServiceMap[serviceId].tokens += app.stakeAmount;
stakedAppsByServiceMap[serviceId].amount += 1;
}

stakedAppsByServiceMap[serviceId].tokens += app.stakeAmount;
stakedAppsByServiceMap[serviceId].amount += 1;
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/mappings/primitives/genesis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ export async function handleGenesis(block: CosmosBlock): Promise<void> {
}

async function _handleModuleAccounts(block: CosmosBlock): Promise<void> {
const moduleAccounts = await queryModuleAccounts();
const moduleAccounts = await queryModuleAccounts(block);

const accounts: Array<EnforceAccountExistenceParams> = [];
// const mAccounts: Array<ModuleAccountProps> = [];
Expand Down
74 changes: 74 additions & 0 deletions src/mappings/utils/query_client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { createPagination, ProtobufRpcClient, QueryClient } from "@cosmjs/stargate";
import {QueryClientImpl as AuthQueryClientImpl} from 'cosmjs-types/cosmos/auth/v1beta1/query'
import {
QueryAllBalancesRequest,
QueryClientImpl as BankQueryClientImpl,
QueryTotalSupplyResponse,
} from "cosmjs-types/cosmos/bank/v1beta1/query";
import { Coin } from "cosmjs-types/cosmos/base/v1beta1/coin";
import { Any } from "cosmjs-types/google/protobuf/any";

interface PocketdexExtension {
readonly bank: {
readonly totalSupply: (paginationKey?: Uint8Array) => Promise<QueryTotalSupplyResponse>;
readonly allBalances: (address: string) => Promise<Coin[]>;
}
readonly auth: {
readonly moduleAccounts: () => Promise<Any[]>;
}
}

export function createProtobufRpcClient(base: QueryClient, height?: number): ProtobufRpcClient {
return {
request: async (service: string, method: string, data: Uint8Array): Promise<Uint8Array> => {
const path = `/${service}/${method}`;
const response = await base.queryAbci(path, data, height);
return response.value;
},
};
}

const setupPocketdexExtension = (height?: number) => (base: QueryClient): PocketdexExtension => {
const rpc = createProtobufRpcClient(base, height);

// Use this service to get easy typed access to query methods
// This cannot be used for proof verification
const bankQueryService = new BankQueryClientImpl(rpc);
const authQueryService = new AuthQueryClientImpl(rpc);

return {
bank: {
totalSupply: async (paginationKey?: Uint8Array) => {
return bankQueryService.TotalSupply({
pagination: createPagination(paginationKey),
});
},
allBalances: async (address: string) => {
const { balances } = await bankQueryService.AllBalances(
QueryAllBalancesRequest.fromPartial({ address: address }),
);
return balances;
},
},
auth: {
moduleAccounts: async () => {
const { accounts } = await authQueryService.ModuleAccounts();
return accounts ?? [];
}
}
};
}

// The purpose of creating a new query client instead of using the one injected by subql
// is because with the injected one, we cannot pass a custom height so it always queries the latest height.
// With this new query client, we can pass a custom height, and it will query the data for the block that is being indexed.
export default function getQueryClient(height: number): QueryClient & PocketdexExtension {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const cometClient = api.forceGetCometClient();

return QueryClient.withExtensions(
cometClient,
setupPocketdexExtension(height),
)
}