@superfluid-finance/sdk-core
Version:
SDK Core for building with Superfluid Protocol
267 lines • 13.4 kB
JavaScript
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