mirror of
https://github.com/cerc-io/watcher-ts
synced 2024-11-19 20:36:19 +00:00
Resolver for Mint, Pool, PoolDayData and Swap entities (#173)
* Use getEntities to get result for queries. * Implement resolver for Mint entity. * Implement resolvers for Pool and PoolDayData entities. * Add query resolver for Swap entity. Co-authored-by: nabarun <nabarun@deepstacksoft.com>
This commit is contained in:
parent
d4a19d15a9
commit
18ebed0b36
@ -1,5 +1,5 @@
|
||||
import assert from 'assert';
|
||||
import { Connection, ConnectionOptions, createConnection, DeepPartial, FindConditions, FindOneOptions, In, LessThanOrEqual, Repository } from 'typeorm';
|
||||
import { Brackets, Connection, ConnectionOptions, createConnection, DeepPartial, FindConditions, FindOneOptions, In, LessThanOrEqual, Repository } from 'typeorm';
|
||||
import { SnakeNamingStrategy } from 'typeorm-naming-strategies';
|
||||
import { MAX_REORG_DEPTH } from '@vulcanize/util';
|
||||
|
||||
@ -25,6 +25,9 @@ import { BlockProgress } from './entity/BlockProgress';
|
||||
import { Block } from './events';
|
||||
import { SyncStatus } from './entity/SyncStatus';
|
||||
|
||||
const DEFAULT_LIMIT = 100;
|
||||
const DEFAULT_SKIP = 0;
|
||||
|
||||
export enum OrderDirection {
|
||||
asc = 'asc',
|
||||
desc = 'desc'
|
||||
@ -32,6 +35,7 @@ export enum OrderDirection {
|
||||
|
||||
export interface QueryOptions {
|
||||
limit?: number;
|
||||
skip?: number;
|
||||
orderBy?: string;
|
||||
orderDirection?: OrderDirection;
|
||||
}
|
||||
@ -135,7 +139,7 @@ export class Database {
|
||||
return entity;
|
||||
}
|
||||
|
||||
async getPool ({ id, blockHash }: DeepPartial<Pool>): Promise<Pool | undefined> {
|
||||
async getPool ({ id, blockHash, blockNumber }: DeepPartial<Pool>): Promise<Pool | undefined> {
|
||||
const repo = this._conn.getRepository(Pool);
|
||||
const whereOptions: FindConditions<Pool> = { id };
|
||||
|
||||
@ -143,6 +147,10 @@ export class Database {
|
||||
whereOptions.blockHash = blockHash;
|
||||
}
|
||||
|
||||
if (blockNumber) {
|
||||
whereOptions.blockNumber = LessThanOrEqual(blockNumber);
|
||||
}
|
||||
|
||||
const findOptions = {
|
||||
where: whereOptions,
|
||||
relations: ['token0', 'token1'],
|
||||
@ -354,85 +362,46 @@ export class Database {
|
||||
return entity;
|
||||
}
|
||||
|
||||
async getFactories ({ blockHash, blockNumber }: Partial<Factory>, queryOptions: QueryOptions): Promise<Array<Factory>> {
|
||||
const repo = this._conn.getRepository(Factory);
|
||||
async getEntities<Entity> (entity: new () => Entity, where: { [key: string]: any } = {}, queryOptions: QueryOptions = {}, relations: string[] = []): Promise<Entity[]> {
|
||||
const { blockHash, blockNumber, ...filter } = where;
|
||||
const repo = this._conn.getRepository(entity);
|
||||
const { tableName } = repo.metadata;
|
||||
|
||||
let selectQueryBuilder = repo.createQueryBuilder('factory')
|
||||
.distinctOn(['id'])
|
||||
.orderBy('id')
|
||||
.addOrderBy('block_number', 'DESC');
|
||||
let selectQueryBuilder = repo.createQueryBuilder(tableName)
|
||||
.distinctOn([`${tableName}.id`])
|
||||
.orderBy(`${tableName}.id`)
|
||||
.addOrderBy(`${tableName}.block_number`, 'DESC');
|
||||
|
||||
if (blockHash) {
|
||||
const { canonicalBlockNumber, blockHashes } = await this._getBranchInfo(blockHash);
|
||||
|
||||
selectQueryBuilder = selectQueryBuilder
|
||||
.where('block_hash IN (:...blockHashes)', { blockHashes })
|
||||
.orWhere('block_number <= :canonicalBlockNumber', { canonicalBlockNumber });
|
||||
}
|
||||
|
||||
if (blockNumber) {
|
||||
selectQueryBuilder = selectQueryBuilder.where('block_number <= :blockNumber', { blockNumber });
|
||||
}
|
||||
|
||||
const { limit } = queryOptions;
|
||||
|
||||
if (limit) {
|
||||
selectQueryBuilder = selectQueryBuilder.limit(limit);
|
||||
}
|
||||
|
||||
return selectQueryBuilder.getMany();
|
||||
}
|
||||
|
||||
async getBundles ({ blockHash, blockNumber }: Partial<Bundle>, queryOptions: QueryOptions): Promise<Array<Bundle>> {
|
||||
const repo = this._conn.getRepository(Bundle);
|
||||
|
||||
let selectQueryBuilder = repo.createQueryBuilder('bundle')
|
||||
.distinctOn(['id'])
|
||||
.orderBy('id')
|
||||
.addOrderBy('block_number', 'DESC');
|
||||
|
||||
if (blockHash) {
|
||||
const { canonicalBlockNumber, blockHashes } = await this._getBranchInfo(blockHash);
|
||||
|
||||
selectQueryBuilder = selectQueryBuilder
|
||||
.where('block_hash IN (:...blockHashes)', { blockHashes })
|
||||
.orWhere('block_number <= :canonicalBlockNumber', { canonicalBlockNumber });
|
||||
}
|
||||
|
||||
if (blockNumber) {
|
||||
selectQueryBuilder = selectQueryBuilder.where('block_number <= :blockNumber', { blockNumber });
|
||||
}
|
||||
|
||||
const { limit } = queryOptions;
|
||||
|
||||
if (limit) {
|
||||
selectQueryBuilder = selectQueryBuilder.limit(limit);
|
||||
}
|
||||
|
||||
return selectQueryBuilder.getMany();
|
||||
}
|
||||
|
||||
async getBurns (where: Partial<Burn> = {}, queryOptions: QueryOptions): Promise<Array<Burn>> {
|
||||
const repo = this._conn.getRepository(Burn);
|
||||
|
||||
let selectQueryBuilder = repo.createQueryBuilder('burn')
|
||||
.distinctOn(['burn.id'])
|
||||
.orderBy('burn.id')
|
||||
.addOrderBy('burn.block_number', 'DESC')
|
||||
.innerJoinAndSelect('burn.pool', 'pool');
|
||||
|
||||
Object.entries(where).forEach(([field, value]) => {
|
||||
selectQueryBuilder = selectQueryBuilder.andWhere(`burn.${field} = :value`, { value });
|
||||
relations.forEach(relation => {
|
||||
selectQueryBuilder = selectQueryBuilder.leftJoinAndSelect(`${repo.metadata.tableName}.${relation}`, relation);
|
||||
});
|
||||
|
||||
const { limit, orderBy, orderDirection } = queryOptions;
|
||||
Object.entries(filter).forEach(([field, value]) => {
|
||||
selectQueryBuilder = selectQueryBuilder.andWhere(`${tableName}.${field} = :value`, { value });
|
||||
});
|
||||
|
||||
if (limit) {
|
||||
selectQueryBuilder = selectQueryBuilder.limit(limit);
|
||||
if (blockHash) {
|
||||
const { canonicalBlockNumber, blockHashes } = await this._getBranchInfo(blockHash);
|
||||
|
||||
selectQueryBuilder = selectQueryBuilder
|
||||
.andWhere(new Brackets(qb => {
|
||||
qb.where(`${tableName}.block_hash IN (:...blockHashes)`, { blockHashes })
|
||||
.orWhere(`${tableName}.block_number <= :canonicalBlockNumber`, { canonicalBlockNumber });
|
||||
}));
|
||||
}
|
||||
|
||||
if (blockNumber) {
|
||||
selectQueryBuilder = selectQueryBuilder.andWhere(`${tableName}.block_number <= :blockNumber`, { blockNumber });
|
||||
}
|
||||
|
||||
const { limit = DEFAULT_LIMIT, orderBy, orderDirection, skip = DEFAULT_SKIP } = queryOptions;
|
||||
|
||||
// TODO: Use skip and take methods. Currently throws error when using with join.
|
||||
selectQueryBuilder = selectQueryBuilder.offset(skip)
|
||||
.limit(limit);
|
||||
|
||||
if (orderBy) {
|
||||
selectQueryBuilder = selectQueryBuilder.addOrderBy(orderBy, orderDirection === 'desc' ? 'DESC' : 'ASC');
|
||||
selectQueryBuilder = selectQueryBuilder.addOrderBy(`${tableName}.${orderBy}`, orderDirection === 'desc' ? 'DESC' : 'ASC');
|
||||
}
|
||||
|
||||
return selectQueryBuilder.getMany();
|
||||
|
@ -14,7 +14,7 @@ import { convertTokenToDecimal, loadTransaction, safeDiv } from './utils';
|
||||
import { createTick } from './utils/tick';
|
||||
import Decimal from 'decimal.js';
|
||||
import { Position } from './entity/Position';
|
||||
import { Database, QueryOptions } from './database';
|
||||
import { Database, QueryOptions, OrderDirection } from './database';
|
||||
import { Event } from './entity/Event';
|
||||
import { ResultEvent, Block, Transaction, PoolCreatedEvent, InitializeEvent, MintEvent, BurnEvent, SwapEvent, IncreaseLiquidityEvent, DecreaseLiquidityEvent, CollectEvent, TransferEvent } from './events';
|
||||
import { Factory } from './entity/Factory';
|
||||
@ -37,6 +37,8 @@ export interface ValueResult {
|
||||
}
|
||||
}
|
||||
|
||||
export { OrderDirection };
|
||||
|
||||
export class Indexer {
|
||||
_db: Database
|
||||
_uniClient: UniClient
|
||||
@ -190,19 +192,16 @@ export class Indexer {
|
||||
}
|
||||
|
||||
async getBundle (id: string, block: BlockHeight): Promise<Bundle | undefined> {
|
||||
return this._db.getBundle({ id, blockHash: block.hash });
|
||||
return this._db.getBundle({ id, blockHash: block.hash, blockNumber: block.number });
|
||||
}
|
||||
|
||||
async getBundles (block: BlockHeight, queryOptions: QueryOptions): Promise<Bundle[]> {
|
||||
return this._db.getBundles({ blockHash: block.hash, blockNumber: block.number }, queryOptions);
|
||||
async getPool (id: string, block: BlockHeight): Promise<Pool | undefined> {
|
||||
return this._db.getPool({ id, blockHash: block.hash, blockNumber: block.number });
|
||||
}
|
||||
|
||||
async getBurns (where: Partial<Burn>, queryOptions: QueryOptions): Promise<Burn[]> {
|
||||
return this._db.getBurns(where, queryOptions);
|
||||
}
|
||||
|
||||
async getFactories (block: BlockHeight, queryOptions: QueryOptions): Promise<Factory[]> {
|
||||
return this._db.getFactories({ blockHash: block.hash, blockNumber: block.number }, queryOptions);
|
||||
async getEntities<Entity> (entity: new () => Entity, where: Partial<Entity>, queryOptions: QueryOptions, relations?: string[]): Promise<Entity[]> {
|
||||
const res = await this._db.getEntities(entity, where, queryOptions, relations);
|
||||
return res;
|
||||
}
|
||||
|
||||
async _fetchAndSaveEvents (block: Block): Promise<void> {
|
||||
@ -365,7 +364,7 @@ export class Indexer {
|
||||
|
||||
// TODO: In subgraph factory is fetched by hardcoded factory address.
|
||||
// Currently fetching first factory in database as only one exists.
|
||||
const [factory] = await this._db.getFactories({ blockHash: block.hash }, { limit: 1 });
|
||||
const [factory] = await this._db.getEntities(Factory, { blockHash: block.hash }, { limit: 1 });
|
||||
|
||||
const token0 = pool.token0;
|
||||
const token1 = pool.token1;
|
||||
@ -501,7 +500,7 @@ export class Indexer {
|
||||
|
||||
// TODO: In subgraph factory is fetched by hardcoded factory address.
|
||||
// Currently fetching first factory in database as only one exists.
|
||||
const [factory] = await this._db.getFactories({ blockHash: block.hash }, { limit: 1 });
|
||||
const [factory] = await this._db.getEntities(Factory, { blockHash: block.hash }, { limit: 1 });
|
||||
|
||||
const token0 = pool.token0;
|
||||
const token1 = pool.token1;
|
||||
@ -619,7 +618,7 @@ export class Indexer {
|
||||
|
||||
// TODO: In subgraph factory is fetched by hardcoded factory address.
|
||||
// Currently fetching first factory in database as only one exists.
|
||||
const [factory] = await this._db.getFactories({ blockHash: block.hash }, { limit: 1 });
|
||||
const [factory] = await this._db.getEntities(Factory, { blockHash: block.hash }, { limit: 1 });
|
||||
|
||||
const pool = await this._db.getPool({ id: contractAddress, blockHash: block.hash });
|
||||
assert(pool);
|
||||
|
@ -2,14 +2,17 @@ import assert from 'assert';
|
||||
import BigInt from 'apollo-type-bigint';
|
||||
import debug from 'debug';
|
||||
|
||||
import { Indexer } from './indexer';
|
||||
import { Indexer, OrderDirection } from './indexer';
|
||||
import { Burn } from './entity/Burn';
|
||||
import { OrderDirection } from './database';
|
||||
import { Bundle } from './entity/Bundle';
|
||||
import { Factory } from './entity/Factory';
|
||||
import { Mint } from './entity/Mint';
|
||||
import { PoolDayData } from './entity/PoolDayData';
|
||||
import { Pool } from './entity/Pool';
|
||||
import { Swap } from './entity/Swap';
|
||||
|
||||
const log = debug('vulcanize:resolver');
|
||||
|
||||
const DEFAULT_LIMIT = 100;
|
||||
|
||||
export interface BlockHeight {
|
||||
number?: number;
|
||||
hash?: string;
|
||||
@ -28,22 +31,54 @@ export const createResolvers = async (indexer: Indexer): Promise<any> => {
|
||||
return indexer.getBundle(id, block);
|
||||
},
|
||||
|
||||
bundles: async (_: any, { block = {}, first = DEFAULT_LIMIT }: { first: number, block: BlockHeight }) => {
|
||||
bundles: async (_: any, { block = {}, first }: { first: number, block: BlockHeight }) => {
|
||||
log('bundles', block, first);
|
||||
|
||||
return indexer.getBundles(block, { limit: first });
|
||||
return indexer.getEntities(Bundle, { blockHash: block.hash, blockNumber: block.number }, { limit: first });
|
||||
},
|
||||
|
||||
burns: async (_: any, { first = DEFAULT_LIMIT, orderBy, orderDirection, where }: { first: number, orderBy: string, orderDirection: OrderDirection, where: Partial<Burn> }) => {
|
||||
burns: async (_: any, { first, orderBy, orderDirection, where }: { first: number, orderBy: string, orderDirection: OrderDirection, where: Partial<Burn> }) => {
|
||||
log('burns', first, orderBy, orderDirection, where);
|
||||
|
||||
return indexer.getBurns(where, { limit: first, orderBy, orderDirection });
|
||||
return indexer.getEntities(Burn, where, { limit: first, orderBy, orderDirection }, ['pool', 'transaction']);
|
||||
},
|
||||
|
||||
factories: async (_: any, { block = {}, first = DEFAULT_LIMIT }: { first: number, block: BlockHeight }) => {
|
||||
factories: async (_: any, { block = {}, first }: { first: number, block: BlockHeight }) => {
|
||||
log('factories', block, first);
|
||||
|
||||
return indexer.getFactories(block, { limit: first });
|
||||
return indexer.getEntities(Factory, { blockHash: block.hash, blockNumber: block.number }, { limit: first });
|
||||
},
|
||||
|
||||
mints: async (_: any, { first, orderBy, orderDirection, where }: { first: number, orderBy: string, orderDirection: OrderDirection, where: Partial<Burn> }) => {
|
||||
log('burns', first, orderBy, orderDirection, where);
|
||||
|
||||
return indexer.getEntities(Mint, where, { limit: first, orderBy, orderDirection }, ['pool', 'transaction']);
|
||||
},
|
||||
|
||||
pool: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
||||
log('bundle', id, block);
|
||||
|
||||
return indexer.getPool(id, block);
|
||||
},
|
||||
|
||||
poolDayDatas: async (_: any, { first, skip, orderBy, orderDirection, where }: { first: number, skip: number, orderBy: string, orderDirection: OrderDirection, where: Partial<Burn> }) => {
|
||||
log('poolDayDatas', first, skip, orderBy, orderDirection, where);
|
||||
|
||||
return indexer.getEntities(PoolDayData, where, { limit: first, skip, orderBy, orderDirection });
|
||||
},
|
||||
|
||||
pools: async (_: any, { block = {}, first, orderBy, orderDirection, where = {} }: { block: BlockHeight, first: number, orderBy: string, orderDirection: OrderDirection, where: Partial<Burn> }) => {
|
||||
log('burns', block, first, orderBy, orderDirection, where);
|
||||
where.blockHash = block.hash;
|
||||
where.blockNumber = block.number;
|
||||
|
||||
return indexer.getEntities(Pool, where, { limit: first, orderBy, orderDirection }, ['token0', 'token1']);
|
||||
},
|
||||
|
||||
swaps: async (_: any, { first, orderBy, orderDirection, where }: { first: number, orderBy: string, orderDirection: OrderDirection, where: Partial<Burn> }) => {
|
||||
log('swaps', first, orderBy, orderDirection, where);
|
||||
|
||||
return indexer.getEntities(Swap, where, { limit: first, orderBy, orderDirection }, ['pool', 'transaction']);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -2,6 +2,7 @@ import assert from 'assert';
|
||||
import { BigNumber } from 'ethers';
|
||||
|
||||
import { Database } from '../database';
|
||||
import { Factory } from '../entity/Factory';
|
||||
import { PoolDayData } from '../entity/PoolDayData';
|
||||
import { PoolHourData } from '../entity/PoolHourData';
|
||||
import { Token } from '../entity/Token';
|
||||
@ -20,7 +21,7 @@ export const updateUniswapDayData = async (db: Database, event: { contractAddres
|
||||
|
||||
// TODO: In subgraph factory is fetched by hardcoded factory address.
|
||||
// Currently fetching first factory in database as only one exists.
|
||||
const [factory] = await db.getFactories({ blockHash: block.hash }, { limit: 1 });
|
||||
const [factory] = await db.getEntities(Factory, { blockHash: block.hash }, { limit: 1 });
|
||||
|
||||
const dayID = Math.floor(block.timestamp / 86400); // Rounded.
|
||||
const dayStartTimestamp = dayID * 86400;
|
||||
|
Loading…
Reference in New Issue
Block a user