UNPKG

@superfluid-finance/sdk-core

Version:
267 lines 13.4 kB
import _ from "lodash"; import { validateAccountTokenSnapshotRequest, validateEventRequest, validateIndexRequest, validateIndexSubscriptionRequest, validateStreamRequest, validateSuperTokenRequest, } from "./ajvValidations.generated"; import { mapGetAllEventsQueryEvents } from "./mapGetAllEventsQueryEvents"; import { createLastIdPaging, createPagedResult, createSkipPaging, takePlusOne, } from "./pagination"; import { SubgraphClient } from "./subgraph/SubgraphClient"; import { GetAccountTokenSnapshotsDocument, } from "./subgraph/queries/getAccountTokenSnapshots.generated"; import { GetAllEventsDocument, } from "./subgraph/queries/getAllEvents.generated"; import { GetIndexSubscriptionsDocument, } from "./subgraph/queries/getIndexSubscriptions.generated"; import { GetIndexesDocument, } from "./subgraph/queries/getIndexes.generated"; import { GetStreamsDocument, } from "./subgraph/queries/getStreams.generated"; import { GetTokensDocument, } from "./subgraph/queries/getTokens.generated"; import { typeGuard } from "./utils"; /** * Query Helper Class * @description A helper class to create `Query` objects which can be used to query different data. */ export default class Query { constructor(options) { this.listAllSuperTokens = async (filter, paging = createSkipPaging(), ordering = { orderBy: "createdAtBlockNumber", orderDirection: "desc", }) => { validateSuperTokenRequest(filter); const response = await this.subgraphClient.request(GetTokensDocument, { where: { isListed: filter.isListed, isSuperToken: true, id_gt: paging.lastId, }, orderBy: ordering === null || ordering === void 0 ? void 0 : ordering.orderBy, orderDirection: ordering === null || ordering === void 0 ? void 0 : ordering.orderDirection, first: takePlusOne(paging), skip: paging.skip, }); const mappedResult = response.result.map((x) => typeGuard({ ...x, createdAtTimestamp: Number(x.createdAtTimestamp), createdAtBlockNumber: Number(x.createdAtBlockNumber), })); return createPagedResult(mappedResult, paging); }; this.listIndexes = async (filter, paging = createSkipPaging(), ordering = { orderBy: "createdAtBlockNumber", orderDirection: "desc", }) => { var _a, _b; validateIndexRequest(filter); const response = await this.subgraphClient.request(GetIndexesDocument, { where: { indexId: filter.indexId, publisher: (_a = filter.publisher) === null || _a === void 0 ? void 0 : _a.toLowerCase(), token: (_b = filter.token) === null || _b === void 0 ? void 0 : _b.toLowerCase(), id_gt: paging.lastId, }, orderBy: ordering === null || ordering === void 0 ? void 0 : ordering.orderBy, orderDirection: ordering === null || ordering === void 0 ? void 0 : ordering.orderDirection, first: takePlusOne(paging), skip: paging.skip, }); const mappedResult = response.result.map((x) => typeGuard({ ...x, publisher: x.publisher.id, createdAtTimestamp: Number(x.createdAtTimestamp), createdAtBlockNumber: Number(x.createdAtBlockNumber), updatedAtTimestamp: Number(x.updatedAtTimestamp), updatedAtBlockNumber: Number(x.updatedAtBlockNumber), token: { ...x.token, createdAtTimestamp: Number(x.token.createdAtTimestamp), createdAtBlockNumber: Number(x.token.createdAtBlockNumber), }, })); return createPagedResult(mappedResult, paging); }; this.listIndexSubscriptions = async (filter, paging = createSkipPaging(), ordering = { orderBy: "createdAtBlockNumber", orderDirection: "desc", }) => { var _a; validateIndexSubscriptionRequest(filter); const response = await this.subgraphClient.request(GetIndexSubscriptionsDocument, { where: { subscriber: (_a = filter.subscriber) === null || _a === void 0 ? void 0 : _a.toLowerCase(), approved: filter.approved, id_gt: paging.lastId, }, orderBy: ordering === null || ordering === void 0 ? void 0 : ordering.orderBy, orderDirection: ordering === null || ordering === void 0 ? void 0 : ordering.orderDirection, first: takePlusOne(paging), skip: paging.skip, }); const mappedResult = response.result.map((x) => typeGuard({ ...x, subscriber: x.subscriber.id, createdAtTimestamp: Number(x.createdAtTimestamp), createdAtBlockNumber: Number(x.createdAtBlockNumber), updatedAtTimestamp: Number(x.updatedAtTimestamp), updatedAtBlockNumber: Number(x.updatedAtBlockNumber), index: { ...x.index, token: { ...x.index.token, createdAtTimestamp: Number(x.index.token.createdAtTimestamp), createdAtBlockNumber: Number(x.index.token.createdAtBlockNumber), }, }, })); return createPagedResult(mappedResult, paging); }; this.listStreams = async (filter, paging = createSkipPaging(), ordering = { orderBy: "createdAtBlockNumber", orderDirection: "desc", }) => { var _a, _b, _c; validateStreamRequest(filter); const response = await this.subgraphClient.request(GetStreamsDocument, { where: { sender: (_a = filter.sender) === null || _a === void 0 ? void 0 : _a.toLowerCase(), receiver: (_b = filter.receiver) === null || _b === void 0 ? void 0 : _b.toLowerCase(), token: (_c = filter.token) === null || _c === void 0 ? void 0 : _c.toLowerCase(), id_gt: paging.lastId, }, orderBy: ordering === null || ordering === void 0 ? void 0 : ordering.orderBy, orderDirection: ordering === null || ordering === void 0 ? void 0 : ordering.orderDirection, first: takePlusOne(paging), skip: paging.skip, }); const mappedResult = response.result.map((x) => typeGuard({ ...x, sender: x.sender.id, receiver: x.receiver.id, createdAtTimestamp: Number(x.createdAtTimestamp), createdAtBlockNumber: Number(x.createdAtBlockNumber), updatedAtTimestamp: Number(x.updatedAtTimestamp), updatedAtBlockNumber: Number(x.updatedAtBlockNumber), token: { ...x.token, createdAtTimestamp: Number(x.token.createdAtTimestamp), createdAtBlockNumber: Number(x.token.createdAtBlockNumber), }, flowUpdatedEvents: x.flowUpdatedEvents.map((y) => ({ ...y, blockNumber: Number(y.blockNumber), timestamp: Number(y.timestamp), })), })); return createPagedResult(mappedResult, paging); }; this.listUserInteractedSuperTokens = async (filter, paging = createSkipPaging(), ordering = { orderBy: "updatedAtBlockNumber", orderDirection: "desc", }) => { var _a, _b; validateAccountTokenSnapshotRequest(filter); const response = await this.subgraphClient.request(GetAccountTokenSnapshotsDocument, { where: { account: (_a = filter.account) === null || _a === void 0 ? void 0 : _a.toLowerCase(), token: (_b = filter.token) === null || _b === void 0 ? void 0 : _b.toLowerCase(), id_gt: paging.lastId, }, orderBy: ordering === null || ordering === void 0 ? void 0 : ordering.orderBy, orderDirection: ordering === null || ordering === void 0 ? void 0 : ordering.orderDirection, first: takePlusOne(paging), skip: paging.skip, }); const mappedResult = response.result.map((x) => typeGuard({ ...x, account: x.account.id, updatedAtTimestamp: Number(x.updatedAtTimestamp), updatedAtBlockNumber: Number(x.updatedAtBlockNumber), token: { ...x.token, createdAtTimestamp: Number(x.token.createdAtTimestamp), createdAtBlockNumber: Number(x.token.createdAtBlockNumber), }, })); return createPagedResult(mappedResult, paging); }; this.listEvents = async (filter, paging = createSkipPaging(), ordering = { orderBy: "blockNumber", orderDirection: "desc", }) => { var _a, _b; validateEventRequest(filter); const response = await this.subgraphClient.request(GetAllEventsDocument, { orderBy: ordering === null || ordering === void 0 ? void 0 : ordering.orderBy, orderDirection: ordering === null || ordering === void 0 ? void 0 : ordering.orderDirection, where: { addresses_contains: filter.account ? [(_a = filter.account) === null || _a === void 0 ? void 0 : _a.toLowerCase()] : undefined, timestamp_gt: (_b = filter.timestamp_gt) === null || _b === void 0 ? void 0 : _b.toString(), id_gt: paging.lastId, }, first: takePlusOne(paging), skip: paging.skip, }); return createPagedResult(mapGetAllEventsQueryEvents(response.events), paging); }; this.options = options; this.subgraphClient = new SubgraphClient(this.options.customSubgraphQueriesEndpoint); } // TODO(KK): error callback? // TODO(KK): retries? // TODO(KK): tests on(callback, ms, account, timeout) { if (ms < 1000) throw Error("Let's not go crazy with the queries..."); // Account for the fact that Subgraph has lag and will insert events with the timestamp of the event from blockchain. const clockSkew = 25000; // Convert millisecond-based time to second-based time (which Subgraph uses). let eventQueryTimestamp = Math.floor((new Date().getTime() - clockSkew) / 1000); let isUnsubscribed = false; const unsubscribe = () => { isUnsubscribed = true; }; const pollingStep = async () => { if (isUnsubscribed) { return; } const allEvents = await listAllResults((paging) => this.listEvents({ account: account, timestamp_gt: eventQueryTimestamp, }, paging, { orderBy: "timestamp", orderDirection: "asc", })); // Filter next events by last timestamp of an event. // NOTE: Make sure to order events by timestamp in ascending order. const lastEvent = _.last(allEvents); if (lastEvent) { callback(allEvents, unsubscribe); // Next event polling is done for events that have a timestamp later than the current latest event. eventQueryTimestamp = lastEvent.timestamp; } // This solution sets the interval based on last query returning, opposed to not taking request-response cycles into account at all. // This solution is more friendly to the Subgraph & more effective resource-wise with slow internet. return setTimeout(() => { // Fire and forget pollingStep(); }, ms); }; if (timeout) { setTimeout(() => { unsubscribe(); }, timeout); } // Fire and forget pollingStep(); return unsubscribe; } } /** * A recursive function to fetch all possible results of a paged query. * @param pagedQuery A paginated query that takes {@link Paging} as input. */ export const listAllResults = async (pagedQuery) => { const listAllRecursively = async (paging) => { const pagedResult = await pagedQuery(paging); if (!pagedResult.nextPaging) return pagedResult.data; const nextResults = await listAllRecursively(pagedResult.nextPaging); return pagedResult.data.concat(nextResults); }; return listAllRecursively(createLastIdPaging({ take: 999 })); }; //# sourceMappingURL=Query.js.map