1198 lines
36 KiB
TypeScript
1198 lines
36 KiB
TypeScript
|
//
|
||
|
// Copyright 2021 Vulcanize, Inc.
|
||
|
//
|
||
|
|
||
|
import assert from 'assert';
|
||
|
import { DeepPartial, FindConditions, FindManyOptions, ObjectLiteral } from 'typeorm';
|
||
|
import debug from 'debug';
|
||
|
import { ethers, constants } from 'ethers';
|
||
|
import { GraphQLResolveInfo } from 'graphql';
|
||
|
|
||
|
import { JsonFragment } from '@ethersproject/abi';
|
||
|
import { BaseProvider } from '@ethersproject/providers';
|
||
|
import { MappingKey, StorageLayout } from '@cerc-io/solidity-mapper';
|
||
|
import {
|
||
|
Indexer as BaseIndexer,
|
||
|
IndexerInterface,
|
||
|
ValueResult,
|
||
|
ServerConfig,
|
||
|
JobQueue,
|
||
|
Where,
|
||
|
QueryOptions,
|
||
|
BlockHeight,
|
||
|
ResultMeta,
|
||
|
updateSubgraphState,
|
||
|
dumpSubgraphState,
|
||
|
GraphWatcherInterface,
|
||
|
StateKind,
|
||
|
StateStatus,
|
||
|
ResultEvent,
|
||
|
getResultEvent,
|
||
|
DatabaseInterface,
|
||
|
Clients,
|
||
|
EthClient,
|
||
|
UpstreamConfig,
|
||
|
EthFullBlock,
|
||
|
EthFullTransaction,
|
||
|
ExtraEventData
|
||
|
} from '@cerc-io/util';
|
||
|
import { GraphWatcher } from '@cerc-io/graph-node';
|
||
|
|
||
|
import FactoryArtifacts from './artifacts/Factory.json';
|
||
|
import PairArtifacts from './artifacts/Pair.json';
|
||
|
import { Database, ENTITIES, SUBGRAPH_ENTITIES } from './database';
|
||
|
import { createInitialState, handleEvent, createStateDiff, createStateCheckpoint } from './hooks';
|
||
|
import { Contract } from './entity/Contract';
|
||
|
import { Event } from './entity/Event';
|
||
|
import { SyncStatus } from './entity/SyncStatus';
|
||
|
import { StateSyncStatus } from './entity/StateSyncStatus';
|
||
|
import { BlockProgress } from './entity/BlockProgress';
|
||
|
import { State } from './entity/State';
|
||
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||
|
import { Factory } from './entity/Factory';
|
||
|
import { Bundle } from './entity/Bundle';
|
||
|
import { Token } from './entity/Token';
|
||
|
import { TokenPrice } from './entity/TokenPrice';
|
||
|
import { _TokenPair } from './entity/_TokenPair';
|
||
|
import { _WhitelistedTokenPair } from './entity/_WhitelistedTokenPair';
|
||
|
import { Pair } from './entity/Pair';
|
||
|
import { User } from './entity/User';
|
||
|
import { LiquidityPosition } from './entity/LiquidityPosition';
|
||
|
import { Mint } from './entity/Mint';
|
||
|
import { Burn } from './entity/Burn';
|
||
|
import { Swap } from './entity/Swap';
|
||
|
import { Transaction } from './entity/Transaction';
|
||
|
import { LiquidityPositionSnapshot } from './entity/LiquidityPositionSnapshot';
|
||
|
import { PairHourSnapshot } from './entity/PairHourSnapshot';
|
||
|
import { PairDaySnapshot } from './entity/PairDaySnapshot';
|
||
|
import { TokenHourSnapshot } from './entity/TokenHourSnapshot';
|
||
|
import { TokenDaySnapshot } from './entity/TokenDaySnapshot';
|
||
|
import { FactoryHourSnapshot } from './entity/FactoryHourSnapshot';
|
||
|
import { FactoryDaySnapshot } from './entity/FactoryDaySnapshot';
|
||
|
/* eslint-enable @typescript-eslint/no-unused-vars */
|
||
|
|
||
|
import { FrothyEntity } from './entity/FrothyEntity';
|
||
|
|
||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||
|
const log = debug('vulcanize:indexer');
|
||
|
|
||
|
const KIND_FACTORY = 'Factory';
|
||
|
|
||
|
const KIND_PAIR = 'Pair';
|
||
|
|
||
|
export class Indexer implements IndexerInterface {
|
||
|
_db: Database;
|
||
|
_ethClient: EthClient;
|
||
|
_ethProvider: BaseProvider;
|
||
|
_baseIndexer: BaseIndexer;
|
||
|
_serverConfig: ServerConfig;
|
||
|
_upstreamConfig: UpstreamConfig;
|
||
|
_graphWatcher: GraphWatcher;
|
||
|
|
||
|
_abiMap: Map<string, JsonFragment[]>;
|
||
|
_storageLayoutMap: Map<string, StorageLayout>;
|
||
|
_contractMap: Map<string, ethers.utils.Interface>;
|
||
|
eventSignaturesMap: Map<string, string[]>;
|
||
|
|
||
|
_entityTypesMap: Map<string, { [key: string]: string }>;
|
||
|
_relationsMap: Map<any, { [key: string]: any }>;
|
||
|
|
||
|
_subgraphStateMap: Map<string, any>;
|
||
|
|
||
|
constructor (
|
||
|
config: {
|
||
|
server: ServerConfig;
|
||
|
upstream: UpstreamConfig;
|
||
|
},
|
||
|
db: DatabaseInterface,
|
||
|
clients: Clients,
|
||
|
ethProvider: BaseProvider,
|
||
|
jobQueue: JobQueue,
|
||
|
graphWatcher?: GraphWatcherInterface
|
||
|
) {
|
||
|
assert(db);
|
||
|
assert(clients.ethClient);
|
||
|
|
||
|
this._db = db as Database;
|
||
|
this._ethClient = clients.ethClient;
|
||
|
this._ethProvider = ethProvider;
|
||
|
this._serverConfig = config.server;
|
||
|
this._upstreamConfig = config.upstream;
|
||
|
this._baseIndexer = new BaseIndexer(config, this._db, this._ethClient, this._ethProvider, jobQueue);
|
||
|
assert(graphWatcher);
|
||
|
this._graphWatcher = graphWatcher as GraphWatcher;
|
||
|
|
||
|
this._abiMap = new Map();
|
||
|
this._storageLayoutMap = new Map();
|
||
|
this._contractMap = new Map();
|
||
|
this.eventSignaturesMap = new Map();
|
||
|
|
||
|
const { abi: FactoryABI } = FactoryArtifacts;
|
||
|
|
||
|
const { abi: PairABI } = PairArtifacts;
|
||
|
|
||
|
assert(FactoryABI);
|
||
|
this._abiMap.set(KIND_FACTORY, FactoryABI);
|
||
|
|
||
|
const FactoryContractInterface = new ethers.utils.Interface(FactoryABI);
|
||
|
this._contractMap.set(KIND_FACTORY, FactoryContractInterface);
|
||
|
|
||
|
const FactoryEventSignatures = Object.values(FactoryContractInterface.events).map(value => {
|
||
|
return FactoryContractInterface.getEventTopic(value);
|
||
|
});
|
||
|
this.eventSignaturesMap.set(KIND_FACTORY, FactoryEventSignatures);
|
||
|
|
||
|
assert(PairABI);
|
||
|
this._abiMap.set(KIND_PAIR, PairABI);
|
||
|
|
||
|
const PairContractInterface = new ethers.utils.Interface(PairABI);
|
||
|
this._contractMap.set(KIND_PAIR, PairContractInterface);
|
||
|
|
||
|
const PairEventSignatures = Object.values(PairContractInterface.events).map(value => {
|
||
|
return PairContractInterface.getEventTopic(value);
|
||
|
});
|
||
|
this.eventSignaturesMap.set(KIND_PAIR, PairEventSignatures);
|
||
|
|
||
|
this._entityTypesMap = new Map();
|
||
|
this._populateEntityTypesMap();
|
||
|
|
||
|
this._relationsMap = new Map();
|
||
|
this._populateRelationsMap();
|
||
|
|
||
|
this._subgraphStateMap = new Map();
|
||
|
}
|
||
|
|
||
|
get serverConfig (): ServerConfig {
|
||
|
return this._serverConfig;
|
||
|
}
|
||
|
|
||
|
get upstreamConfig (): UpstreamConfig {
|
||
|
return this._upstreamConfig;
|
||
|
}
|
||
|
|
||
|
get storageLayoutMap (): Map<string, StorageLayout> {
|
||
|
return this._storageLayoutMap;
|
||
|
}
|
||
|
|
||
|
get graphWatcher (): GraphWatcher {
|
||
|
return this._graphWatcher;
|
||
|
}
|
||
|
|
||
|
async init (): Promise<void> {
|
||
|
await this._baseIndexer.fetchContracts();
|
||
|
await this._baseIndexer.fetchStateStatus();
|
||
|
}
|
||
|
|
||
|
switchClients ({ ethClient, ethProvider }: { ethClient: EthClient, ethProvider: BaseProvider }): void {
|
||
|
this._ethClient = ethClient;
|
||
|
this._ethProvider = ethProvider;
|
||
|
this._baseIndexer.switchClients({ ethClient, ethProvider });
|
||
|
this._graphWatcher.switchClients({ ethClient, ethProvider });
|
||
|
}
|
||
|
|
||
|
async getMetaData (block: BlockHeight): Promise<ResultMeta | null> {
|
||
|
return this._baseIndexer.getMetaData(block);
|
||
|
}
|
||
|
|
||
|
getResultEvent (event: Event): ResultEvent {
|
||
|
return getResultEvent(event);
|
||
|
}
|
||
|
|
||
|
async getStorageValue (storageLayout: StorageLayout, blockHash: string, contractAddress: string, variable: string, ...mappingKeys: MappingKey[]): Promise<ValueResult> {
|
||
|
return this._baseIndexer.getStorageValue(
|
||
|
storageLayout,
|
||
|
blockHash,
|
||
|
contractAddress,
|
||
|
variable,
|
||
|
...mappingKeys
|
||
|
);
|
||
|
}
|
||
|
|
||
|
async getEntitiesForBlock (blockHash: string, tableName: string): Promise<any[]> {
|
||
|
return this._db.getEntitiesForBlock(blockHash, tableName);
|
||
|
}
|
||
|
|
||
|
async processInitialState (contractAddress: string, blockHash: string): Promise<any> {
|
||
|
// Call initial state hook.
|
||
|
return createInitialState(this, contractAddress, blockHash);
|
||
|
}
|
||
|
|
||
|
async processStateCheckpoint (contractAddress: string, blockHash: string): Promise<boolean> {
|
||
|
// Call checkpoint hook.
|
||
|
return createStateCheckpoint(this, contractAddress, blockHash);
|
||
|
}
|
||
|
|
||
|
async processCanonicalBlock (blockHash: string, blockNumber: number): Promise<void> {
|
||
|
console.time('time:indexer#processCanonicalBlock-finalize_auto_diffs');
|
||
|
// Finalize staged diff blocks if any.
|
||
|
await this._baseIndexer.finalizeDiffStaged(blockHash);
|
||
|
console.timeEnd('time:indexer#processCanonicalBlock-finalize_auto_diffs');
|
||
|
|
||
|
// Call custom stateDiff hook.
|
||
|
await createStateDiff(this, blockHash);
|
||
|
|
||
|
this._graphWatcher.pruneEntityCacheFrothyBlocks(blockHash, blockNumber);
|
||
|
}
|
||
|
|
||
|
async processCheckpoint (blockHash: string): Promise<void> {
|
||
|
// Return if checkpointInterval is <= 0.
|
||
|
const checkpointInterval = this._serverConfig.checkpointInterval;
|
||
|
if (checkpointInterval <= 0) return;
|
||
|
|
||
|
console.time('time:indexer#processCheckpoint-checkpoint');
|
||
|
await this._baseIndexer.processCheckpoint(this, blockHash, checkpointInterval);
|
||
|
console.timeEnd('time:indexer#processCheckpoint-checkpoint');
|
||
|
}
|
||
|
|
||
|
async processCLICheckpoint (contractAddress: string, blockHash?: string): Promise<string | undefined> {
|
||
|
return this._baseIndexer.processCLICheckpoint(this, contractAddress, blockHash);
|
||
|
}
|
||
|
|
||
|
async getPrevState (blockHash: string, contractAddress: string, kind?: string): Promise<State | undefined> {
|
||
|
return this._db.getPrevState(blockHash, contractAddress, kind);
|
||
|
}
|
||
|
|
||
|
async getLatestState (contractAddress: string, kind: StateKind | null, blockNumber?: number): Promise<State | undefined> {
|
||
|
return this._db.getLatestState(contractAddress, kind, blockNumber);
|
||
|
}
|
||
|
|
||
|
async getStatesByHash (blockHash: string): Promise<State[]> {
|
||
|
return this._baseIndexer.getStatesByHash(blockHash);
|
||
|
}
|
||
|
|
||
|
async getStateByCID (cid: string): Promise<State | undefined> {
|
||
|
return this._baseIndexer.getStateByCID(cid);
|
||
|
}
|
||
|
|
||
|
async getStates (where: FindConditions<State>): Promise<State[]> {
|
||
|
return this._db.getStates(where);
|
||
|
}
|
||
|
|
||
|
getStateData (state: State): any {
|
||
|
return this._baseIndexer.getStateData(state);
|
||
|
}
|
||
|
|
||
|
// Method used to create auto diffs (diff_staged).
|
||
|
async createDiffStaged (contractAddress: string, blockHash: string, data: any): Promise<void> {
|
||
|
console.time('time:indexer#createDiffStaged-auto_diff');
|
||
|
await this._baseIndexer.createDiffStaged(contractAddress, blockHash, data);
|
||
|
console.timeEnd('time:indexer#createDiffStaged-auto_diff');
|
||
|
}
|
||
|
|
||
|
// Method to be used by createStateDiff hook.
|
||
|
async createDiff (contractAddress: string, blockHash: string, data: any): Promise<void> {
|
||
|
const block = await this.getBlockProgress(blockHash);
|
||
|
assert(block);
|
||
|
|
||
|
await this._baseIndexer.createDiff(contractAddress, block, data);
|
||
|
}
|
||
|
|
||
|
// Method to be used by createStateCheckpoint hook.
|
||
|
async createStateCheckpoint (contractAddress: string, blockHash: string, data: any): Promise<void> {
|
||
|
const block = await this.getBlockProgress(blockHash);
|
||
|
assert(block);
|
||
|
|
||
|
return this._baseIndexer.createStateCheckpoint(contractAddress, block, data);
|
||
|
}
|
||
|
|
||
|
// Method to be used by export-state CLI.
|
||
|
async createCheckpoint (contractAddress: string, blockHash: string): Promise<string | undefined> {
|
||
|
const block = await this.getBlockProgress(blockHash);
|
||
|
assert(block);
|
||
|
|
||
|
return this._baseIndexer.createCheckpoint(this, contractAddress, block);
|
||
|
}
|
||
|
|
||
|
// Method to be used by fill-state CLI.
|
||
|
async createInit (blockHash: string, blockNumber: number): Promise<void> {
|
||
|
// Create initial state for contracts.
|
||
|
await this._baseIndexer.createInit(this, blockHash, blockNumber);
|
||
|
}
|
||
|
|
||
|
async saveOrUpdateState (state: State): Promise<State> {
|
||
|
return this._baseIndexer.saveOrUpdateState(state);
|
||
|
}
|
||
|
|
||
|
async removeStates (blockNumber: number, kind: StateKind): Promise<void> {
|
||
|
await this._baseIndexer.removeStates(blockNumber, kind);
|
||
|
}
|
||
|
|
||
|
async getSubgraphEntity<Entity extends ObjectLiteral> (
|
||
|
entity: new () => Entity,
|
||
|
id: string,
|
||
|
block: BlockHeight,
|
||
|
queryInfo: GraphQLResolveInfo
|
||
|
): Promise<any> {
|
||
|
const data = await this._graphWatcher.getEntity(entity, id, this._relationsMap, block, queryInfo);
|
||
|
|
||
|
return data;
|
||
|
}
|
||
|
|
||
|
async getSubgraphEntities<Entity extends ObjectLiteral> (
|
||
|
entity: new () => Entity,
|
||
|
block: BlockHeight,
|
||
|
where: { [key: string]: any } = {},
|
||
|
queryOptions: QueryOptions = {},
|
||
|
queryInfo: GraphQLResolveInfo
|
||
|
): Promise<any[]> {
|
||
|
return this._graphWatcher.getEntities(entity, this._relationsMap, block, where, queryOptions, queryInfo);
|
||
|
}
|
||
|
|
||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||
|
async triggerIndexingOnEvent (event: Event, extraData: ExtraEventData): Promise<void> {
|
||
|
const resultEvent = this.getResultEvent(event);
|
||
|
|
||
|
console.time('time:indexer#processEvent-mapping_code');
|
||
|
// Call subgraph handler for event.
|
||
|
await this._graphWatcher.handleEvent(resultEvent, extraData);
|
||
|
console.timeEnd('time:indexer#processEvent-mapping_code');
|
||
|
|
||
|
// Call custom hook function for indexing on event.
|
||
|
await handleEvent(this, resultEvent);
|
||
|
}
|
||
|
|
||
|
async processEvent (event: Event, extraData: ExtraEventData): Promise<void> {
|
||
|
// Trigger indexing of data based on the event.
|
||
|
await this.triggerIndexingOnEvent(event, extraData);
|
||
|
}
|
||
|
|
||
|
async processBlock (blockProgress: BlockProgress): Promise<void> {
|
||
|
console.time('time:indexer#processBlock-init_state');
|
||
|
// Call a function to create initial state for contracts.
|
||
|
await this._baseIndexer.createInit(this, blockProgress.blockHash, blockProgress.blockNumber);
|
||
|
console.timeEnd('time:indexer#processBlock-init_state');
|
||
|
|
||
|
this._graphWatcher.updateEntityCacheFrothyBlocks(blockProgress);
|
||
|
}
|
||
|
|
||
|
async processBlockAfterEvents (blockHash: string, blockNumber: number, extraData: ExtraEventData): Promise<void> {
|
||
|
console.time('time:indexer#processBlockAfterEvents-mapping_code');
|
||
|
// Call subgraph handler for block.
|
||
|
await this._graphWatcher.handleBlock(blockHash, blockNumber, extraData);
|
||
|
console.timeEnd('time:indexer#processBlockAfterEvents-mapping_code');
|
||
|
|
||
|
console.time('time:indexer#processBlockAfterEvents-dump_subgraph_state');
|
||
|
// Persist subgraph state to the DB.
|
||
|
await this.dumpSubgraphState(blockHash);
|
||
|
console.timeEnd('time:indexer#processBlockAfterEvents-dump_subgraph_state');
|
||
|
}
|
||
|
|
||
|
parseEventNameAndArgs (kind: string, logObj: any): { eventParsed: boolean, eventDetails: any } {
|
||
|
const { topics, data } = logObj;
|
||
|
|
||
|
const contract = this._contractMap.get(kind);
|
||
|
assert(contract);
|
||
|
|
||
|
let logDescription: ethers.utils.LogDescription;
|
||
|
try {
|
||
|
logDescription = contract.parseLog({ data, topics });
|
||
|
} catch (err) {
|
||
|
// Return if no matching event found
|
||
|
if ((err as Error).message.includes('no matching event')) {
|
||
|
log(`WARNING: Skipping event for contract ${kind} as no matching event found in the ABI`);
|
||
|
return { eventParsed: false, eventDetails: {} };
|
||
|
}
|
||
|
|
||
|
throw err;
|
||
|
}
|
||
|
|
||
|
const { eventName, eventInfo, eventSignature } = this._baseIndexer.parseEvent(logDescription);
|
||
|
|
||
|
return {
|
||
|
eventParsed: true,
|
||
|
eventDetails: {
|
||
|
eventName,
|
||
|
eventInfo,
|
||
|
eventSignature
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
async getStateSyncStatus (): Promise<StateSyncStatus | undefined> {
|
||
|
return this._db.getStateSyncStatus();
|
||
|
}
|
||
|
|
||
|
async updateStateSyncStatusIndexedBlock (blockNumber: number, force?: boolean): Promise<StateSyncStatus | undefined> {
|
||
|
if (!this._serverConfig.enableState) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const dbTx = await this._db.createTransactionRunner();
|
||
|
let res;
|
||
|
|
||
|
try {
|
||
|
res = await this._db.updateStateSyncStatusIndexedBlock(dbTx, blockNumber, force);
|
||
|
await dbTx.commitTransaction();
|
||
|
} catch (error) {
|
||
|
await dbTx.rollbackTransaction();
|
||
|
throw error;
|
||
|
} finally {
|
||
|
await dbTx.release();
|
||
|
}
|
||
|
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
async updateStateSyncStatusCheckpointBlock (blockNumber: number, force?: boolean): Promise<StateSyncStatus> {
|
||
|
const dbTx = await this._db.createTransactionRunner();
|
||
|
let res;
|
||
|
|
||
|
try {
|
||
|
res = await this._db.updateStateSyncStatusCheckpointBlock(dbTx, blockNumber, force);
|
||
|
await dbTx.commitTransaction();
|
||
|
} catch (error) {
|
||
|
await dbTx.rollbackTransaction();
|
||
|
throw error;
|
||
|
} finally {
|
||
|
await dbTx.release();
|
||
|
}
|
||
|
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
async getLatestCanonicalBlock (): Promise<BlockProgress | undefined> {
|
||
|
const syncStatus = await this.getSyncStatus();
|
||
|
assert(syncStatus);
|
||
|
|
||
|
if (syncStatus.latestCanonicalBlockHash === constants.HashZero) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const latestCanonicalBlock = await this.getBlockProgress(syncStatus.latestCanonicalBlockHash);
|
||
|
assert(latestCanonicalBlock);
|
||
|
|
||
|
return latestCanonicalBlock;
|
||
|
}
|
||
|
|
||
|
async getLatestStateIndexedBlock (): Promise<BlockProgress> {
|
||
|
return this._baseIndexer.getLatestStateIndexedBlock();
|
||
|
}
|
||
|
|
||
|
async addContracts (): Promise<void> {
|
||
|
// Watching all the contracts in the subgraph.
|
||
|
await this._graphWatcher.addContracts();
|
||
|
}
|
||
|
|
||
|
async watchContract (address: string, kind: string, checkpoint: boolean, startingBlock: number, context?: any): Promise<void> {
|
||
|
return this._baseIndexer.watchContract(address, kind, checkpoint, startingBlock, context);
|
||
|
}
|
||
|
|
||
|
updateStateStatusMap (address: string, stateStatus: StateStatus): void {
|
||
|
this._baseIndexer.updateStateStatusMap(address, stateStatus);
|
||
|
}
|
||
|
|
||
|
cacheContract (contract: Contract): void {
|
||
|
return this._baseIndexer.cacheContract(contract);
|
||
|
}
|
||
|
|
||
|
async saveEventEntity (dbEvent: Event): Promise<Event> {
|
||
|
return this._baseIndexer.saveEventEntity(dbEvent);
|
||
|
}
|
||
|
|
||
|
async saveEvents (dbEvents: Event[]): Promise<void> {
|
||
|
return this._baseIndexer.saveEvents(dbEvents);
|
||
|
}
|
||
|
|
||
|
async getEventsByFilter (blockHash: string, contract?: string, name?: string): Promise<Array<Event>> {
|
||
|
return this._baseIndexer.getEventsByFilter(blockHash, contract, name);
|
||
|
}
|
||
|
|
||
|
isWatchedContract (address : string): Contract | undefined {
|
||
|
return this._baseIndexer.isWatchedContract(address);
|
||
|
}
|
||
|
|
||
|
getWatchedContracts (): Contract[] {
|
||
|
return this._baseIndexer.getWatchedContracts();
|
||
|
}
|
||
|
|
||
|
getContractsByKind (kind: string): Contract[] {
|
||
|
return this._baseIndexer.getContractsByKind(kind);
|
||
|
}
|
||
|
|
||
|
async getProcessedBlockCountForRange (fromBlockNumber: number, toBlockNumber: number): Promise<{ expected: number, actual: number }> {
|
||
|
return this._baseIndexer.getProcessedBlockCountForRange(fromBlockNumber, toBlockNumber);
|
||
|
}
|
||
|
|
||
|
async getEventsInRange (fromBlockNumber: number, toBlockNumber: number): Promise<Array<Event>> {
|
||
|
return this._baseIndexer.getEventsInRange(fromBlockNumber, toBlockNumber, this._serverConfig.gql.maxEventsBlockRange);
|
||
|
}
|
||
|
|
||
|
async getSyncStatus (): Promise<SyncStatus | undefined> {
|
||
|
return this._baseIndexer.getSyncStatus();
|
||
|
}
|
||
|
|
||
|
async getBlocks (blockFilter: { blockHash?: string, blockNumber?: number }): Promise<any> {
|
||
|
return this._baseIndexer.getBlocks(blockFilter);
|
||
|
}
|
||
|
|
||
|
async updateSyncStatusIndexedBlock (blockHash: string, blockNumber: number, force = false): Promise<SyncStatus> {
|
||
|
return this._baseIndexer.updateSyncStatusIndexedBlock(blockHash, blockNumber, force);
|
||
|
}
|
||
|
|
||
|
async updateSyncStatusChainHead (blockHash: string, blockNumber: number, force = false): Promise<SyncStatus> {
|
||
|
return this._baseIndexer.updateSyncStatusChainHead(blockHash, blockNumber, force);
|
||
|
}
|
||
|
|
||
|
async updateSyncStatusCanonicalBlock (blockHash: string, blockNumber: number, force = false): Promise<SyncStatus> {
|
||
|
const syncStatus = this._baseIndexer.updateSyncStatusCanonicalBlock(blockHash, blockNumber, force);
|
||
|
await this.pruneFrothyEntities(blockNumber);
|
||
|
|
||
|
return syncStatus;
|
||
|
}
|
||
|
|
||
|
async updateSyncStatusProcessedBlock (blockHash: string, blockNumber: number, force = false): Promise<SyncStatus> {
|
||
|
return this._baseIndexer.updateSyncStatusProcessedBlock(blockHash, blockNumber, force);
|
||
|
}
|
||
|
|
||
|
async updateSyncStatusIndexingError (hasIndexingError: boolean): Promise<SyncStatus | undefined> {
|
||
|
return this._baseIndexer.updateSyncStatusIndexingError(hasIndexingError);
|
||
|
}
|
||
|
|
||
|
async updateSyncStatus (syncStatus: DeepPartial<SyncStatus>): Promise<SyncStatus> {
|
||
|
return this._baseIndexer.updateSyncStatus(syncStatus);
|
||
|
}
|
||
|
|
||
|
async getEvent (id: string): Promise<Event | undefined> {
|
||
|
return this._baseIndexer.getEvent(id);
|
||
|
}
|
||
|
|
||
|
async getBlockProgress (blockHash: string): Promise<BlockProgress | undefined> {
|
||
|
return this._baseIndexer.getBlockProgress(blockHash);
|
||
|
}
|
||
|
|
||
|
async getBlockProgressEntities (where: FindConditions<BlockProgress>, options: FindManyOptions<BlockProgress>): Promise<BlockProgress[]> {
|
||
|
return this._baseIndexer.getBlockProgressEntities(where, options);
|
||
|
}
|
||
|
|
||
|
async getBlocksAtHeight (height: number, isPruned: boolean): Promise<BlockProgress[]> {
|
||
|
return this._baseIndexer.getBlocksAtHeight(height, isPruned);
|
||
|
}
|
||
|
|
||
|
async fetchAndSaveFilteredEventsAndBlocks (startBlock: number, endBlock: number): Promise<{
|
||
|
blockProgress: BlockProgress,
|
||
|
events: DeepPartial<Event>[],
|
||
|
ethFullBlock: EthFullBlock;
|
||
|
ethFullTransactions: EthFullTransaction[];
|
||
|
}[]> {
|
||
|
return this._baseIndexer.fetchAndSaveFilteredEventsAndBlocks(startBlock, endBlock, this.eventSignaturesMap, this.parseEventNameAndArgs.bind(this));
|
||
|
}
|
||
|
|
||
|
async fetchEventsForContracts (blockHash: string, blockNumber: number, addresses: string[]): Promise<DeepPartial<Event>[]> {
|
||
|
return this._baseIndexer.fetchEventsForContracts(blockHash, blockNumber, addresses, this.eventSignaturesMap, this.parseEventNameAndArgs.bind(this));
|
||
|
}
|
||
|
|
||
|
async saveBlockAndFetchEvents (block: DeepPartial<BlockProgress>): Promise<[
|
||
|
BlockProgress,
|
||
|
DeepPartial<Event>[],
|
||
|
EthFullTransaction[]
|
||
|
]> {
|
||
|
return this._saveBlockAndFetchEvents(block);
|
||
|
}
|
||
|
|
||
|
async getBlockEvents (blockHash: string, where: Where, queryOptions: QueryOptions): Promise<Array<Event>> {
|
||
|
return this._baseIndexer.getBlockEvents(blockHash, where, queryOptions);
|
||
|
}
|
||
|
|
||
|
async removeUnknownEvents (block: BlockProgress): Promise<void> {
|
||
|
return this._baseIndexer.removeUnknownEvents(Event, block);
|
||
|
}
|
||
|
|
||
|
async markBlocksAsPruned (blocks: BlockProgress[]): Promise<void> {
|
||
|
await this._baseIndexer.markBlocksAsPruned(blocks);
|
||
|
|
||
|
await this._graphWatcher.pruneEntities(FrothyEntity, blocks, SUBGRAPH_ENTITIES);
|
||
|
}
|
||
|
|
||
|
async pruneFrothyEntities (blockNumber: number): Promise<void> {
|
||
|
await this._graphWatcher.pruneFrothyEntities(FrothyEntity, blockNumber);
|
||
|
}
|
||
|
|
||
|
async resetLatestEntities (blockNumber: number): Promise<void> {
|
||
|
await this._graphWatcher.resetLatestEntities(blockNumber);
|
||
|
}
|
||
|
|
||
|
async updateBlockProgress (block: BlockProgress, lastProcessedEventIndex: number): Promise<BlockProgress> {
|
||
|
return this._baseIndexer.updateBlockProgress(block, lastProcessedEventIndex);
|
||
|
}
|
||
|
|
||
|
async getAncestorAtHeight (blockHash: string, height: number): Promise<string> {
|
||
|
return this._baseIndexer.getAncestorAtHeight(blockHash, height);
|
||
|
}
|
||
|
|
||
|
async resetWatcherToBlock (blockNumber: number): Promise<void> {
|
||
|
const entities = [...ENTITIES, FrothyEntity];
|
||
|
await this._baseIndexer.resetWatcherToBlock(blockNumber, entities);
|
||
|
|
||
|
await this.resetLatestEntities(blockNumber);
|
||
|
}
|
||
|
|
||
|
async clearProcessedBlockData (block: BlockProgress): Promise<void> {
|
||
|
const entities = [...ENTITIES, FrothyEntity];
|
||
|
await this._baseIndexer.clearProcessedBlockData(block, entities);
|
||
|
|
||
|
await this.resetLatestEntities(block.blockNumber);
|
||
|
}
|
||
|
|
||
|
getEntityTypesMap (): Map<string, { [key: string]: string }> {
|
||
|
return this._entityTypesMap;
|
||
|
}
|
||
|
|
||
|
getRelationsMap (): Map<any, { [key: string]: any }> {
|
||
|
return this._relationsMap;
|
||
|
}
|
||
|
|
||
|
updateSubgraphState (contractAddress: string, data: any): void {
|
||
|
return updateSubgraphState(this._subgraphStateMap, contractAddress, data);
|
||
|
}
|
||
|
|
||
|
async dumpSubgraphState (blockHash: string, isStateFinalized = false): Promise<void> {
|
||
|
return dumpSubgraphState(this, this._subgraphStateMap, blockHash, isStateFinalized);
|
||
|
}
|
||
|
|
||
|
_populateEntityTypesMap (): void {
|
||
|
this._entityTypesMap.set('Factory', {
|
||
|
id: 'ID',
|
||
|
type: 'PairType',
|
||
|
volumeUSD: 'BigDecimal',
|
||
|
volumeNative: 'BigDecimal',
|
||
|
liquidityUSD: 'BigDecimal',
|
||
|
liquidityNative: 'BigDecimal',
|
||
|
feesUSD: 'BigDecimal',
|
||
|
feesNative: 'BigDecimal',
|
||
|
pairCount: 'BigInt',
|
||
|
transactionCount: 'BigInt',
|
||
|
tokenCount: 'BigInt',
|
||
|
userCount: 'BigInt'
|
||
|
});
|
||
|
this._entityTypesMap.set('Bundle', {
|
||
|
id: 'ID',
|
||
|
nativePrice: 'BigDecimal'
|
||
|
});
|
||
|
this._entityTypesMap.set('Token', {
|
||
|
id: 'ID',
|
||
|
price: 'TokenPrice',
|
||
|
symbol: 'String',
|
||
|
symbolSuccess: 'Boolean',
|
||
|
name: 'String',
|
||
|
nameSuccess: 'Boolean',
|
||
|
decimals: 'BigInt',
|
||
|
decimalsSuccess: 'Boolean',
|
||
|
liquidity: 'BigInt',
|
||
|
liquidityNative: 'BigDecimal',
|
||
|
liquidityUSD: 'BigDecimal',
|
||
|
volume: 'BigDecimal',
|
||
|
volumeNative: 'BigDecimal',
|
||
|
volumeUSD: 'BigDecimal',
|
||
|
feesNative: 'BigDecimal',
|
||
|
feesUSD: 'BigDecimal',
|
||
|
txCount: 'BigInt',
|
||
|
pairCount: 'BigInt',
|
||
|
whitelistedPairCount: 'BigInt'
|
||
|
});
|
||
|
this._entityTypesMap.set('TokenPrice', {
|
||
|
id: 'ID',
|
||
|
token: 'Token',
|
||
|
derivedNative: 'BigDecimal',
|
||
|
lastUsdPrice: 'BigDecimal',
|
||
|
pricedOffToken: 'Token',
|
||
|
pricedOffPair: 'Pair'
|
||
|
});
|
||
|
this._entityTypesMap.set('_TokenPair', {
|
||
|
id: 'ID',
|
||
|
pair: 'Pair',
|
||
|
token: 'Token'
|
||
|
});
|
||
|
this._entityTypesMap.set('_WhitelistedTokenPair', {
|
||
|
id: 'ID',
|
||
|
pair: 'Pair',
|
||
|
token: 'Token'
|
||
|
});
|
||
|
this._entityTypesMap.set('Pair', {
|
||
|
id: 'ID',
|
||
|
type: 'PairType',
|
||
|
swapFee: 'BigInt',
|
||
|
twapEnabled: 'Boolean',
|
||
|
name: 'String',
|
||
|
token0: 'Token',
|
||
|
token1: 'Token',
|
||
|
source: 'String',
|
||
|
createdAtBlock: 'BigInt',
|
||
|
createdAtTimestamp: 'BigInt',
|
||
|
reserve0: 'BigInt',
|
||
|
reserve1: 'BigInt',
|
||
|
liquidity: 'BigInt',
|
||
|
liquidityUSD: 'BigDecimal',
|
||
|
liquidityNative: 'BigDecimal',
|
||
|
trackedLiquidityNative: 'BigDecimal',
|
||
|
token0Price: 'BigDecimal',
|
||
|
token1Price: 'BigDecimal',
|
||
|
volumeNative: 'BigDecimal',
|
||
|
volumeUSD: 'BigDecimal',
|
||
|
volumeToken0: 'BigDecimal',
|
||
|
volumeToken1: 'BigDecimal',
|
||
|
feesNative: 'BigDecimal',
|
||
|
feesUSD: 'BigDecimal',
|
||
|
apr: 'BigDecimal',
|
||
|
aprUpdatedAtTimestamp: 'BigInt',
|
||
|
txCount: 'BigInt'
|
||
|
});
|
||
|
this._entityTypesMap.set('User', {
|
||
|
id: 'ID',
|
||
|
lpSnapshotsCount: 'BigInt'
|
||
|
});
|
||
|
this._entityTypesMap.set('LiquidityPosition', {
|
||
|
id: 'ID',
|
||
|
pair: 'Pair',
|
||
|
user: 'User',
|
||
|
balance: 'BigInt',
|
||
|
createdAtBlock: 'BigInt',
|
||
|
createdAtTimestamp: 'BigInt'
|
||
|
});
|
||
|
this._entityTypesMap.set('Mint', {
|
||
|
id: 'ID',
|
||
|
transaction: 'Transaction',
|
||
|
timestamp: 'BigInt',
|
||
|
pair: 'Pair',
|
||
|
to: 'String',
|
||
|
liquidity: 'BigDecimal',
|
||
|
sender: 'Bytes',
|
||
|
amount0: 'BigDecimal',
|
||
|
amount1: 'BigDecimal',
|
||
|
logIndex: 'BigInt',
|
||
|
amountUSD: 'BigDecimal',
|
||
|
feeTo: 'Bytes',
|
||
|
feeLiquidity: 'BigDecimal'
|
||
|
});
|
||
|
this._entityTypesMap.set('Burn', {
|
||
|
id: 'ID',
|
||
|
transaction: 'Transaction',
|
||
|
timestamp: 'BigInt',
|
||
|
pair: 'Pair',
|
||
|
liquidity: 'BigDecimal',
|
||
|
sender: 'String',
|
||
|
amount0: 'BigDecimal',
|
||
|
amount1: 'BigDecimal',
|
||
|
to: 'String',
|
||
|
logIndex: 'BigInt',
|
||
|
amountUSD: 'BigDecimal',
|
||
|
complete: 'Boolean',
|
||
|
feeTo: 'String',
|
||
|
feeLiquidity: 'BigDecimal'
|
||
|
});
|
||
|
this._entityTypesMap.set('Swap', {
|
||
|
id: 'ID',
|
||
|
transaction: 'Transaction',
|
||
|
timestamp: 'BigInt',
|
||
|
pair: 'Pair',
|
||
|
sender: 'String',
|
||
|
tokenIn: 'Token',
|
||
|
tokenOut: 'Token',
|
||
|
amountIn: 'BigDecimal',
|
||
|
amountOut: 'BigDecimal',
|
||
|
to: 'String',
|
||
|
logIndex: 'BigInt',
|
||
|
amountUSD: 'BigDecimal'
|
||
|
});
|
||
|
this._entityTypesMap.set('Transaction', {
|
||
|
id: 'ID',
|
||
|
gasLimit: 'BigInt',
|
||
|
gasPrice: 'BigInt',
|
||
|
mints: 'Mint',
|
||
|
burns: 'Burn',
|
||
|
swaps: 'Swap',
|
||
|
createdAtBlock: 'BigInt',
|
||
|
createdAtTimestamp: 'BigInt'
|
||
|
});
|
||
|
this._entityTypesMap.set('LiquidityPositionSnapshot', {
|
||
|
id: 'ID',
|
||
|
liquidityPosition: 'LiquidityPosition',
|
||
|
timestamp: 'Int',
|
||
|
block: 'Int',
|
||
|
user: 'User',
|
||
|
pair: 'Pair',
|
||
|
token0PriceUSD: 'BigDecimal',
|
||
|
token1PriceUSD: 'BigDecimal',
|
||
|
reserve0: 'BigInt',
|
||
|
reserve1: 'BigInt',
|
||
|
reserveUSD: 'BigDecimal',
|
||
|
liquidityTokenTotalSupply: 'BigInt',
|
||
|
liquidityTokenBalance: 'BigInt'
|
||
|
});
|
||
|
this._entityTypesMap.set('PairHourSnapshot', {
|
||
|
id: 'ID',
|
||
|
pair: 'Pair',
|
||
|
date: 'Int',
|
||
|
cumulativeVolumeUSD: 'BigDecimal',
|
||
|
volumeUSD: 'BigDecimal',
|
||
|
volumeNative: 'BigDecimal',
|
||
|
volumeToken0: 'BigDecimal',
|
||
|
volumeToken1: 'BigDecimal',
|
||
|
liquidity: 'BigDecimal',
|
||
|
liquidityNative: 'BigDecimal',
|
||
|
liquidityUSD: 'BigDecimal',
|
||
|
feesNative: 'BigDecimal',
|
||
|
feesUSD: 'BigDecimal',
|
||
|
apr: 'BigDecimal',
|
||
|
transactionCount: 'BigInt'
|
||
|
});
|
||
|
this._entityTypesMap.set('PairDaySnapshot', {
|
||
|
id: 'ID',
|
||
|
pair: 'Pair',
|
||
|
date: 'Int',
|
||
|
cumulativeVolumeUSD: 'BigDecimal',
|
||
|
volumeUSD: 'BigDecimal',
|
||
|
volumeNative: 'BigDecimal',
|
||
|
volumeToken0: 'BigDecimal',
|
||
|
volumeToken1: 'BigDecimal',
|
||
|
liquidity: 'BigDecimal',
|
||
|
liquidityNative: 'BigDecimal',
|
||
|
liquidityUSD: 'BigDecimal',
|
||
|
feesNative: 'BigDecimal',
|
||
|
feesUSD: 'BigDecimal',
|
||
|
apr: 'BigDecimal',
|
||
|
transactionCount: 'BigInt'
|
||
|
});
|
||
|
this._entityTypesMap.set('TokenHourSnapshot', {
|
||
|
id: 'ID',
|
||
|
date: 'Int',
|
||
|
token: 'Token',
|
||
|
liquidity: 'BigDecimal',
|
||
|
liquidityNative: 'BigDecimal',
|
||
|
liquidityUSD: 'BigDecimal',
|
||
|
volume: 'BigDecimal',
|
||
|
volumeNative: 'BigDecimal',
|
||
|
volumeUSD: 'BigDecimal',
|
||
|
priceNative: 'BigDecimal',
|
||
|
priceUSD: 'BigDecimal',
|
||
|
feesNative: 'BigDecimal',
|
||
|
feesUSD: 'BigDecimal',
|
||
|
transactionCount: 'BigInt'
|
||
|
});
|
||
|
this._entityTypesMap.set('TokenDaySnapshot', {
|
||
|
id: 'ID',
|
||
|
date: 'Int',
|
||
|
token: 'Token',
|
||
|
liquidity: 'BigDecimal',
|
||
|
liquidityNative: 'BigDecimal',
|
||
|
liquidityUSD: 'BigDecimal',
|
||
|
volume: 'BigDecimal',
|
||
|
volumeNative: 'BigDecimal',
|
||
|
volumeUSD: 'BigDecimal',
|
||
|
priceNative: 'BigDecimal',
|
||
|
priceUSD: 'BigDecimal',
|
||
|
feesNative: 'BigDecimal',
|
||
|
feesUSD: 'BigDecimal',
|
||
|
transactionCount: 'BigInt'
|
||
|
});
|
||
|
this._entityTypesMap.set('FactoryHourSnapshot', {
|
||
|
id: 'ID',
|
||
|
factory: 'Factory',
|
||
|
date: 'Int',
|
||
|
volumeUSD: 'BigDecimal',
|
||
|
volumeNative: 'BigDecimal',
|
||
|
liquidityNative: 'BigDecimal',
|
||
|
liquidityUSD: 'BigDecimal',
|
||
|
feesNative: 'BigDecimal',
|
||
|
feesUSD: 'BigDecimal',
|
||
|
transactionCount: 'BigInt'
|
||
|
});
|
||
|
this._entityTypesMap.set('FactoryDaySnapshot', {
|
||
|
id: 'ID',
|
||
|
factory: 'Factory',
|
||
|
date: 'Int',
|
||
|
volumeUSD: 'BigDecimal',
|
||
|
volumeNative: 'BigDecimal',
|
||
|
liquidityNative: 'BigDecimal',
|
||
|
liquidityUSD: 'BigDecimal',
|
||
|
feesNative: 'BigDecimal',
|
||
|
feesUSD: 'BigDecimal',
|
||
|
transactionCount: 'BigInt'
|
||
|
});
|
||
|
}
|
||
|
|
||
|
_populateRelationsMap (): void {
|
||
|
this._relationsMap.set(Token, {
|
||
|
price: {
|
||
|
entity: TokenPrice,
|
||
|
isArray: false,
|
||
|
isDerived: false
|
||
|
},
|
||
|
pairs: {
|
||
|
entity: _TokenPair,
|
||
|
isArray: true,
|
||
|
isDerived: true,
|
||
|
field: 'token'
|
||
|
},
|
||
|
whitelistedPairs: {
|
||
|
entity: _WhitelistedTokenPair,
|
||
|
isArray: true,
|
||
|
isDerived: true,
|
||
|
field: 'token'
|
||
|
}
|
||
|
});
|
||
|
this._relationsMap.set(TokenPrice, {
|
||
|
token: {
|
||
|
entity: Token,
|
||
|
isArray: false,
|
||
|
isDerived: false
|
||
|
},
|
||
|
pricedOffToken: {
|
||
|
entity: Token,
|
||
|
isArray: false,
|
||
|
isDerived: false
|
||
|
},
|
||
|
pricedOffPair: {
|
||
|
entity: Pair,
|
||
|
isArray: false,
|
||
|
isDerived: false
|
||
|
}
|
||
|
});
|
||
|
this._relationsMap.set(_TokenPair, {
|
||
|
pair: {
|
||
|
entity: Pair,
|
||
|
isArray: false,
|
||
|
isDerived: false
|
||
|
},
|
||
|
token: {
|
||
|
entity: Token,
|
||
|
isArray: false,
|
||
|
isDerived: false
|
||
|
}
|
||
|
});
|
||
|
this._relationsMap.set(_WhitelistedTokenPair, {
|
||
|
pair: {
|
||
|
entity: Pair,
|
||
|
isArray: false,
|
||
|
isDerived: false
|
||
|
},
|
||
|
token: {
|
||
|
entity: Token,
|
||
|
isArray: false,
|
||
|
isDerived: false
|
||
|
}
|
||
|
});
|
||
|
this._relationsMap.set(Pair, {
|
||
|
token0: {
|
||
|
entity: Token,
|
||
|
isArray: false,
|
||
|
isDerived: false
|
||
|
},
|
||
|
token1: {
|
||
|
entity: Token,
|
||
|
isArray: false,
|
||
|
isDerived: false
|
||
|
},
|
||
|
liquidityPositions: {
|
||
|
entity: LiquidityPosition,
|
||
|
isArray: true,
|
||
|
isDerived: true,
|
||
|
field: 'pair'
|
||
|
},
|
||
|
liquidityPositionSnapshots: {
|
||
|
entity: LiquidityPositionSnapshot,
|
||
|
isArray: true,
|
||
|
isDerived: true,
|
||
|
field: 'pair'
|
||
|
},
|
||
|
hourSnapshots: {
|
||
|
entity: PairHourSnapshot,
|
||
|
isArray: true,
|
||
|
isDerived: true,
|
||
|
field: 'pair'
|
||
|
},
|
||
|
daySnapshots: {
|
||
|
entity: PairDaySnapshot,
|
||
|
isArray: true,
|
||
|
isDerived: true,
|
||
|
field: 'pair'
|
||
|
}
|
||
|
});
|
||
|
this._relationsMap.set(User, {
|
||
|
liquidityPositions: {
|
||
|
entity: LiquidityPosition,
|
||
|
isArray: true,
|
||
|
isDerived: true,
|
||
|
field: 'user'
|
||
|
}
|
||
|
});
|
||
|
this._relationsMap.set(LiquidityPosition, {
|
||
|
pair: {
|
||
|
entity: Pair,
|
||
|
isArray: false,
|
||
|
isDerived: false
|
||
|
},
|
||
|
user: {
|
||
|
entity: User,
|
||
|
isArray: false,
|
||
|
isDerived: false
|
||
|
}
|
||
|
});
|
||
|
this._relationsMap.set(Mint, {
|
||
|
transaction: {
|
||
|
entity: Transaction,
|
||
|
isArray: false,
|
||
|
isDerived: false
|
||
|
},
|
||
|
pair: {
|
||
|
entity: Pair,
|
||
|
isArray: false,
|
||
|
isDerived: false
|
||
|
}
|
||
|
});
|
||
|
this._relationsMap.set(Burn, {
|
||
|
transaction: {
|
||
|
entity: Transaction,
|
||
|
isArray: false,
|
||
|
isDerived: false
|
||
|
},
|
||
|
pair: {
|
||
|
entity: Pair,
|
||
|
isArray: false,
|
||
|
isDerived: false
|
||
|
}
|
||
|
});
|
||
|
this._relationsMap.set(Swap, {
|
||
|
transaction: {
|
||
|
entity: Transaction,
|
||
|
isArray: false,
|
||
|
isDerived: false
|
||
|
},
|
||
|
pair: {
|
||
|
entity: Pair,
|
||
|
isArray: false,
|
||
|
isDerived: false
|
||
|
},
|
||
|
tokenIn: {
|
||
|
entity: Token,
|
||
|
isArray: false,
|
||
|
isDerived: false
|
||
|
},
|
||
|
tokenOut: {
|
||
|
entity: Token,
|
||
|
isArray: false,
|
||
|
isDerived: false
|
||
|
}
|
||
|
});
|
||
|
this._relationsMap.set(Transaction, {
|
||
|
mints: {
|
||
|
entity: Mint,
|
||
|
isArray: true,
|
||
|
isDerived: false
|
||
|
},
|
||
|
burns: {
|
||
|
entity: Burn,
|
||
|
isArray: true,
|
||
|
isDerived: false
|
||
|
},
|
||
|
swaps: {
|
||
|
entity: Swap,
|
||
|
isArray: true,
|
||
|
isDerived: false
|
||
|
}
|
||
|
});
|
||
|
this._relationsMap.set(LiquidityPositionSnapshot, {
|
||
|
liquidityPosition: {
|
||
|
entity: LiquidityPosition,
|
||
|
isArray: false,
|
||
|
isDerived: false
|
||
|
},
|
||
|
user: {
|
||
|
entity: User,
|
||
|
isArray: false,
|
||
|
isDerived: false
|
||
|
},
|
||
|
pair: {
|
||
|
entity: Pair,
|
||
|
isArray: false,
|
||
|
isDerived: false
|
||
|
}
|
||
|
});
|
||
|
this._relationsMap.set(PairHourSnapshot, {
|
||
|
pair: {
|
||
|
entity: Pair,
|
||
|
isArray: false,
|
||
|
isDerived: false
|
||
|
}
|
||
|
});
|
||
|
this._relationsMap.set(PairDaySnapshot, {
|
||
|
pair: {
|
||
|
entity: Pair,
|
||
|
isArray: false,
|
||
|
isDerived: false
|
||
|
}
|
||
|
});
|
||
|
this._relationsMap.set(TokenHourSnapshot, {
|
||
|
token: {
|
||
|
entity: Token,
|
||
|
isArray: false,
|
||
|
isDerived: false
|
||
|
}
|
||
|
});
|
||
|
this._relationsMap.set(TokenDaySnapshot, {
|
||
|
token: {
|
||
|
entity: Token,
|
||
|
isArray: false,
|
||
|
isDerived: false
|
||
|
}
|
||
|
});
|
||
|
this._relationsMap.set(FactoryHourSnapshot, {
|
||
|
factory: {
|
||
|
entity: Factory,
|
||
|
isArray: false,
|
||
|
isDerived: false
|
||
|
}
|
||
|
});
|
||
|
this._relationsMap.set(FactoryDaySnapshot, {
|
||
|
factory: {
|
||
|
entity: Factory,
|
||
|
isArray: false,
|
||
|
isDerived: false
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
async _saveBlockAndFetchEvents ({
|
||
|
cid: blockCid,
|
||
|
blockHash,
|
||
|
blockNumber,
|
||
|
blockTimestamp,
|
||
|
parentHash
|
||
|
}: DeepPartial<BlockProgress>): Promise<[
|
||
|
BlockProgress,
|
||
|
DeepPartial<Event>[],
|
||
|
EthFullTransaction[]
|
||
|
]> {
|
||
|
assert(blockHash);
|
||
|
assert(blockNumber);
|
||
|
|
||
|
const { events: dbEvents, transactions } = await this._baseIndexer.fetchEvents(blockHash, blockNumber, this.eventSignaturesMap, this.parseEventNameAndArgs.bind(this));
|
||
|
|
||
|
const dbTx = await this._db.createTransactionRunner();
|
||
|
try {
|
||
|
const block = {
|
||
|
cid: blockCid,
|
||
|
blockHash,
|
||
|
blockNumber,
|
||
|
blockTimestamp,
|
||
|
parentHash
|
||
|
};
|
||
|
|
||
|
console.time(`time:indexer#_saveBlockAndFetchEvents-db-save-${blockNumber}`);
|
||
|
const blockProgress = await this._db.saveBlockWithEvents(dbTx, block, dbEvents);
|
||
|
await dbTx.commitTransaction();
|
||
|
console.timeEnd(`time:indexer#_saveBlockAndFetchEvents-db-save-${blockNumber}`);
|
||
|
|
||
|
return [blockProgress, [], transactions];
|
||
|
} catch (error) {
|
||
|
await dbTx.rollbackTransaction();
|
||
|
throw error;
|
||
|
} finally {
|
||
|
await dbTx.release();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
async getFullTransactions (txHashList: string[]): Promise<EthFullTransaction[]> {
|
||
|
return this._baseIndexer.getFullTransactions(txHashList);
|
||
|
}
|
||
|
}
|