Add block hash to entities, split get and set methods (#161)

* Split get and set entity for PoolCreated and Initialize events.

* Split get/set entity for Mint, Burn and Swap events.

* Split get/set entity for IncreaseLiquidity and DecreaseLiquidity events.

* Split get/set for NFPM Transfer and Collect events.

* Add blockHash to entities.

Co-authored-by: nabarun <nabarun@deepstacksoft.com>
This commit is contained in:
Ashwin Phatak 2021-07-23 11:00:40 +05:30 committed by GitHub
parent 26965f372f
commit f8335aad03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 643 additions and 729 deletions

View File

@ -45,6 +45,44 @@ export class Database {
return this._conn.close();
}
async getFactory ({ id, blockNumber }: DeepPartial<Factory>): Promise<Factory | undefined> {
const repo = this._conn.getRepository(Factory);
const whereOptions: FindConditions<Factory> = { id };
if (blockNumber) {
whereOptions.blockNumber = LessThanOrEqual(blockNumber);
}
const findOptions: FindOneOptions<Factory> = {
where: whereOptions,
order: {
blockNumber: 'DESC'
}
};
return repo.findOne(findOptions);
}
async getBundle ({ id, blockNumber }: DeepPartial<Bundle>): Promise<Bundle | undefined> {
const repo = this._conn.getRepository(Bundle);
const whereOptions: FindConditions<Bundle> = { id };
if (blockNumber) {
whereOptions.blockNumber = LessThanOrEqual(blockNumber);
}
const findOptions: FindOneOptions<Bundle> = {
where: whereOptions,
order: {
blockNumber: 'DESC'
}
};
return repo.findOne(findOptions);
}
async getToken ({ id, blockNumber }: DeepPartial<Token>): Promise<Token | undefined> {
const repo = this._conn.getRepository(Token);
@ -122,6 +160,114 @@ export class Database {
return repo.findOne(findOptions);
}
async getPoolDayData ({ id, blockNumber }: DeepPartial<PoolDayData>): Promise<PoolDayData | undefined> {
const repo = this._conn.getRepository(PoolDayData);
const whereOptions: FindConditions<PoolDayData> = { id };
if (blockNumber) {
whereOptions.blockNumber = LessThanOrEqual(blockNumber);
}
const findOptions: FindOneOptions<PoolDayData> = {
where: whereOptions,
order: {
blockNumber: 'DESC'
}
};
return repo.findOne(findOptions);
}
async getPoolHourData ({ id, blockNumber }: DeepPartial<PoolHourData>): Promise<PoolHourData | undefined> {
const repo = this._conn.getRepository(PoolHourData);
const whereOptions: FindConditions<PoolHourData> = { id };
if (blockNumber) {
whereOptions.blockNumber = LessThanOrEqual(blockNumber);
}
const findOptions: FindOneOptions<PoolHourData> = {
where: whereOptions,
order: {
blockNumber: 'DESC'
}
};
return repo.findOne(findOptions);
}
async getUniswapDayData ({ id, blockNumber }: DeepPartial<UniswapDayData>): Promise<UniswapDayData | undefined> {
const repo = this._conn.getRepository(UniswapDayData);
const whereOptions: FindConditions<UniswapDayData> = { id };
if (blockNumber) {
whereOptions.blockNumber = LessThanOrEqual(blockNumber);
}
const findOptions: FindOneOptions<UniswapDayData> = {
where: whereOptions,
order: {
blockNumber: 'DESC'
}
};
return repo.findOne(findOptions);
}
async getTokenDayData ({ id, blockNumber }: DeepPartial<TokenDayData>): Promise<TokenDayData | undefined> {
const repo = this._conn.getRepository(TokenDayData);
const whereOptions: FindConditions<TokenDayData> = { id };
if (blockNumber) {
whereOptions.blockNumber = LessThanOrEqual(blockNumber);
}
const findOptions: FindOneOptions<TokenDayData> = {
where: whereOptions,
order: {
blockNumber: 'DESC'
}
};
return repo.findOne(findOptions);
}
async getTokenHourData ({ id, blockNumber }: DeepPartial<TokenHourData>): Promise<TokenHourData | undefined> {
const repo = this._conn.getRepository(TokenHourData);
const whereOptions: FindConditions<TokenHourData> = { id };
if (blockNumber) {
whereOptions.blockNumber = LessThanOrEqual(blockNumber);
}
const findOptions: FindOneOptions<TokenHourData> = {
where: whereOptions,
order: {
blockNumber: 'DESC'
}
};
return repo.findOne(findOptions);
}
async getTransaction ({ id, blockNumber }: DeepPartial<Transaction>): Promise<Transaction | undefined> {
const repo = this._conn.getRepository(Transaction);
const whereOptions: FindConditions<Transaction> = { id };
if (blockNumber) {
whereOptions.blockNumber = LessThanOrEqual(blockNumber);
}
const findOptions: FindOneOptions<Transaction> = {
where: whereOptions,
order: {
blockNumber: 'DESC'
}
};
return repo.findOne(findOptions);
}
async getFactories ({ blockNumber }: DeepPartial<Factory>, queryOptions: { [key: string]: any }): Promise<Array<Factory>> {
const repo = this._conn.getRepository(Factory);
@ -143,485 +289,150 @@ export class Database {
return selectQueryBuilder.getMany();
}
async loadFactory ({ id, blockNumber, ...values }: DeepPartial<Factory>): Promise<Factory> {
async saveFactory (factory: Factory, block: Block): Promise<Factory> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(Factory);
let selectQueryBuilder = repo.createQueryBuilder('factory')
.where('id = :id', { id });
if (blockNumber) {
selectQueryBuilder = selectQueryBuilder.andWhere('block_number <= :blockNumber', { blockNumber });
}
let entity = await selectQueryBuilder.orderBy('block_number', 'DESC')
.getOne();
if (!entity) {
entity = repo.create({ blockNumber, id, ...values });
entity = await repo.save(entity);
}
return entity;
});
}
async loadPool ({ id, blockNumber, ...values }: DeepPartial<Pool>): Promise<Pool> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(Pool);
const whereOptions: FindConditions<Pool> = { id };
if (blockNumber) {
whereOptions.blockNumber = LessThanOrEqual(blockNumber);
}
const findOptions: FindOneOptions<Pool> = {
where: whereOptions,
relations: ['token0', 'token1'],
order: {
blockNumber: 'DESC'
}
};
let entity = await repo.findOne(findOptions);
if (!entity) {
entity = repo.create({ blockNumber, id, ...values });
entity = await repo.save(entity);
}
return entity;
});
}
async loadToken ({ id, blockNumber, ...values }: DeepPartial<Token>): Promise<Token> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(Token);
const whereOptions: FindConditions<Token> = { id };
if (blockNumber) {
whereOptions.blockNumber = LessThanOrEqual(blockNumber);
}
const findOptions: FindOneOptions<Token> = {
where: whereOptions,
relations: ['whitelistPools', 'whitelistPools.token0', 'whitelistPools.token1'],
order: {
blockNumber: 'DESC'
}
};
let entity = await repo.findOne(findOptions);
if (!entity) {
entity = repo.create({ blockNumber, id, ...values });
entity = await repo.save(entity);
// TODO: Find way to preload relations during create.
entity.whitelistPools = [];
}
return entity;
});
}
async loadBundle ({ id, blockNumber, ...values }: DeepPartial<Bundle>): Promise<Bundle> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(Bundle);
let selectQueryBuilder = repo.createQueryBuilder('bundle')
.where('id = :id', { id });
if (blockNumber) {
selectQueryBuilder = selectQueryBuilder.andWhere('block_number <= :blockNumber', { blockNumber });
}
let entity = await selectQueryBuilder.orderBy('block_number', 'DESC')
.getOne();
if (!entity) {
entity = repo.create({ blockNumber, id, ...values });
entity = await repo.save(entity);
}
return entity;
});
}
async loadPoolDayData ({ id, blockNumber, ...values }: DeepPartial<PoolDayData>): Promise<PoolDayData> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(PoolDayData);
let selectQueryBuilder = repo.createQueryBuilder('pool_day_data')
.where('id = :id', { id });
if (blockNumber) {
selectQueryBuilder = selectQueryBuilder.andWhere('block_number <= :blockNumber', { blockNumber });
}
let entity = await selectQueryBuilder.orderBy('block_number', 'DESC')
.getOne();
if (!entity) {
entity = repo.create({ blockNumber, id, ...values });
entity = await repo.save(entity);
}
return entity;
});
}
async loadPoolHourData ({ id, blockNumber, ...values }: DeepPartial<PoolHourData>): Promise<PoolHourData> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(PoolHourData);
let selectQueryBuilder = repo.createQueryBuilder('pool_hour_data')
.where('id = :id', { id });
if (blockNumber) {
selectQueryBuilder = selectQueryBuilder.andWhere('block_number <= :blockNumber', { blockNumber });
}
let entity = await selectQueryBuilder.orderBy('block_number', 'DESC')
.getOne();
if (!entity) {
entity = repo.create({ blockNumber, id, ...values });
entity = await repo.save(entity);
}
return entity;
});
}
async loadTransaction ({ id, blockNumber, ...values }: DeepPartial<Transaction>): Promise<Transaction> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(Transaction);
let selectQueryBuilder = repo.createQueryBuilder('transaction')
.where('id = :id', { id });
if (blockNumber) {
selectQueryBuilder = selectQueryBuilder.andWhere('block_number <= :blockNumber', { blockNumber });
}
let entity = await selectQueryBuilder.orderBy('block_number', 'DESC')
.getOne();
if (!entity) {
entity = repo.create({ blockNumber, id, ...values });
entity = await repo.save(entity);
}
return entity;
});
}
async loadMint ({ id, blockNumber, ...values }:DeepPartial<Mint>): Promise<Mint> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(Mint);
let selectQueryBuilder = repo.createQueryBuilder('mint')
.where('id = :id', { id });
if (blockNumber) {
selectQueryBuilder = selectQueryBuilder.andWhere('block_number <= :blockNumber', { blockNumber });
}
let entity = await selectQueryBuilder.orderBy('block_number', 'DESC')
.getOne();
if (!entity) {
entity = repo.create({ blockNumber, id, ...values });
entity = await repo.save(entity);
}
return entity;
});
}
async loadBurn ({ id, blockNumber, ...values }:DeepPartial<Burn>): Promise<Burn> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(Burn);
let selectQueryBuilder = repo.createQueryBuilder('burn')
.where('id = :id', { id });
if (blockNumber) {
selectQueryBuilder = selectQueryBuilder.andWhere('block_number <= :blockNumber', { blockNumber });
}
let entity = await selectQueryBuilder.orderBy('block_number', 'DESC')
.getOne();
if (!entity) {
entity = repo.create({ blockNumber, id, ...values });
entity = await repo.save(entity);
}
return entity;
});
}
async loadSwap ({ id, blockNumber, ...values }:DeepPartial<Swap>): Promise<Swap> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(Swap);
let selectQueryBuilder = repo.createQueryBuilder('swap')
.where('id = :id', { id });
if (blockNumber) {
selectQueryBuilder = selectQueryBuilder.andWhere('block_number <= :blockNumber', { blockNumber });
}
let entity = await selectQueryBuilder.orderBy('block_number', 'DESC')
.getOne();
if (!entity) {
entity = repo.create({ blockNumber, id, ...values });
entity = await repo.save(entity);
}
return entity;
});
}
async loadTick ({ id, blockNumber, ...values }: DeepPartial<Tick>): Promise<Tick> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(Tick);
let selectQueryBuilder = repo.createQueryBuilder('tick')
.where('id = :id', { id });
if (blockNumber) {
selectQueryBuilder = selectQueryBuilder.andWhere('block_number <= :blockNumber', { blockNumber });
}
let entity = await selectQueryBuilder.orderBy('block_number', 'DESC')
.getOne();
if (!entity) {
entity = repo.create({ blockNumber, id, ...values });
entity = await repo.save(entity);
}
return entity;
});
}
async loadUniswapDayData ({ id, blockNumber, ...values }: DeepPartial<UniswapDayData>): Promise<UniswapDayData> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(UniswapDayData);
let selectQueryBuilder = repo.createQueryBuilder('uniswap_day_data')
.where('id = :id', { id });
if (blockNumber) {
selectQueryBuilder = selectQueryBuilder.andWhere('block_number <= :blockNumber', { blockNumber });
}
let entity = await selectQueryBuilder.orderBy('block_number', 'DESC')
.getOne();
if (!entity) {
entity = repo.create({ blockNumber, id, ...values });
entity = await repo.save(entity);
}
return entity;
});
}
async loadTokenDayData ({ id, blockNumber, ...values }: DeepPartial<TokenDayData>): Promise<TokenDayData> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(TokenDayData);
let selectQueryBuilder = repo.createQueryBuilder('token_day_data')
.where('id = :id', { id });
if (blockNumber) {
selectQueryBuilder = selectQueryBuilder.andWhere('block_number <= :blockNumber', { blockNumber });
}
let entity = await selectQueryBuilder.orderBy('block_number', 'DESC')
.getOne();
if (!entity) {
entity = repo.create({ blockNumber, id, ...values });
entity = await repo.save(entity);
}
return entity;
});
}
async loadTokenHourData ({ id, blockNumber, ...values }: DeepPartial<TokenHourData>): Promise<TokenHourData> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(TokenHourData);
let selectQueryBuilder = repo.createQueryBuilder('token_hour_data')
.where('id = :id', { id });
if (blockNumber) {
selectQueryBuilder = selectQueryBuilder.andWhere('block_number <= :blockNumber', { blockNumber });
}
let entity = await selectQueryBuilder.orderBy('block_number', 'DESC')
.getOne();
if (!entity) {
entity = repo.create({ blockNumber, id, ...values });
entity = await repo.save(entity);
}
return entity;
});
}
async loadPosition ({ id, blockNumber, ...values }: DeepPartial<Position>): Promise<Position> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(Position);
let selectQueryBuilder = repo.createQueryBuilder('position')
.where('id = :id', { id });
if (blockNumber) {
selectQueryBuilder = selectQueryBuilder.andWhere('block_number <= :blockNumber', { blockNumber });
}
let entity = await selectQueryBuilder.orderBy('block_number', 'DESC')
.getOne();
if (!entity) {
entity = repo.create({ blockNumber, id, ...values });
entity = await repo.save(entity);
}
return entity;
});
}
async loadPositionSnapshot ({ id, blockNumber, ...values }: DeepPartial<PositionSnapshot>): Promise<PositionSnapshot> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(PositionSnapshot);
let selectQueryBuilder = repo.createQueryBuilder('positionSnapshot')
.where('id = :id', { id });
if (blockNumber) {
selectQueryBuilder = selectQueryBuilder.andWhere('block_number <= :blockNumber', { blockNumber });
}
let entity = await selectQueryBuilder.orderBy('block_number', 'DESC')
.getOne();
if (!entity) {
entity = repo.create({ blockNumber, id, ...values });
entity = await repo.save(entity);
}
return entity;
});
}
async saveFactory (factory: Factory, blockNumber: number): Promise<Factory> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(Factory);
factory.blockNumber = blockNumber;
factory.blockNumber = block.number;
factory.blockHash = block.hash;
return repo.save(factory);
});
}
async saveBundle (bundle: Bundle, blockNumber: number): Promise<Bundle> {
async saveBundle (bundle: Bundle, block: Block): Promise<Bundle> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(Bundle);
bundle.blockNumber = blockNumber;
bundle.blockNumber = block.number;
bundle.blockHash = block.hash;
return repo.save(bundle);
});
}
async savePool (pool: Pool, blockNumber: number): Promise<Pool> {
async savePool (pool: Pool, block: Block): Promise<Pool> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(Pool);
pool.blockNumber = blockNumber;
pool.blockNumber = block.number;
pool.blockHash = block.hash;
return repo.save(pool);
});
}
async savePoolDayData (poolDayData: PoolDayData, blockNumber: number): Promise<PoolDayData> {
async savePoolDayData (poolDayData: PoolDayData, block: Block): Promise<PoolDayData> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(PoolDayData);
poolDayData.blockNumber = blockNumber;
poolDayData.blockNumber = block.number;
poolDayData.blockHash = block.hash;
return repo.save(poolDayData);
});
}
async savePoolHourData (poolHourData: PoolHourData, blockNumber: number): Promise<PoolHourData> {
async savePoolHourData (poolHourData: PoolHourData, block: Block): Promise<PoolHourData> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(PoolHourData);
poolHourData.blockNumber = blockNumber;
poolHourData.blockNumber = block.number;
poolHourData.blockHash = block.hash;
return repo.save(poolHourData);
});
}
async saveToken (token: Token, blockNumber: number): Promise<Token> {
async saveToken (token: Token, block: Block): Promise<Token> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(Token);
token.blockNumber = blockNumber;
token.blockNumber = block.number;
token.blockHash = block.hash;
return repo.save(token);
});
}
async saveTransaction (transaction: Transaction, blockNumber: number): Promise<Transaction> {
async saveTransaction (transaction: Transaction, block: Block): Promise<Transaction> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(Transaction);
transaction.blockNumber = blockNumber;
transaction.blockNumber = block.number;
transaction.blockHash = block.hash;
return repo.save(transaction);
});
}
async saveUniswapDayData (uniswapDayData: UniswapDayData, blockNumber: number): Promise<UniswapDayData> {
async saveUniswapDayData (uniswapDayData: UniswapDayData, block: Block): Promise<UniswapDayData> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(UniswapDayData);
uniswapDayData.blockNumber = blockNumber;
uniswapDayData.blockNumber = block.number;
uniswapDayData.blockHash = block.hash;
return repo.save(uniswapDayData);
});
}
async saveTokenDayData (tokenDayData: TokenDayData, blockNumber: number): Promise<TokenDayData> {
async saveTokenDayData (tokenDayData: TokenDayData, block: Block): Promise<TokenDayData> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(TokenDayData);
tokenDayData.blockNumber = blockNumber;
tokenDayData.blockNumber = block.number;
tokenDayData.blockHash = block.hash;
return repo.save(tokenDayData);
});
}
async saveTokenHourData (tokenHourData: TokenHourData, blockNumber: number): Promise<TokenHourData> {
async saveTokenHourData (tokenHourData: TokenHourData, block: Block): Promise<TokenHourData> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(TokenHourData);
tokenHourData.blockNumber = blockNumber;
tokenHourData.blockNumber = block.number;
tokenHourData.blockHash = block.hash;
return repo.save(tokenHourData);
});
}
async saveTick (tick: Tick, blockNumber: number): Promise<Tick> {
async saveTick (tick: Tick, block: Block): Promise<Tick> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(Tick);
tick.blockNumber = blockNumber;
tick.blockNumber = block.number;
tick.blockHash = block.hash;
return repo.save(tick);
});
}
async savePosition (position: Position, blockNumber: number): Promise<Position> {
async savePosition (position: Position, block: Block): Promise<Position> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(Position);
position.blockNumber = blockNumber;
position.blockNumber = block.number;
position.blockHash = block.hash;
return repo.save(position);
});
}
async savePositionSnapshot (positionSnapshot: PositionSnapshot, block: Block): Promise<PositionSnapshot> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(PositionSnapshot);
positionSnapshot.blockNumber = block.number;
positionSnapshot.blockHash = block.hash;
return repo.save(positionSnapshot);
});
}
async saveMint (mint: Mint, block: Block): Promise<Mint> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(Mint);
mint.blockNumber = block.number;
mint.blockHash = block.hash;
return repo.save(mint);
});
}
async saveBurn (burn: Burn, block: Block): Promise<Burn> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(Burn);
burn.blockNumber = block.number;
burn.blockHash = block.hash;
return repo.save(burn);
});
}
async saveSwap (swap: Swap, block: Block): Promise<Swap> {
return this._conn.transaction(async (tx) => {
const repo = tx.getRepository(Swap);
swap.blockNumber = block.number;
swap.blockHash = block.hash;
return repo.save(swap);
});
}
// Returns true if events have already been synced for the (block, token) combination.
async didSyncEvents ({ blockHash, token }: { blockHash: string, token: string }): Promise<boolean> {
const numRows = await this._conn.getRepository(EventSyncProgress)

View File

@ -7,7 +7,10 @@ export class Bundle {
@PrimaryColumn('varchar', { length: 1 })
id!: string;
@PrimaryColumn('integer')
@PrimaryColumn('varchar', { length: 66 })
blockHash!: string
@Column('integer')
blockNumber!: number;
@Column('numeric', { default: 0, transformer: decimalTransformer })

View File

@ -11,7 +11,10 @@ export class Burn {
@PrimaryColumn('varchar')
id!: string;
@PrimaryColumn('integer')
@PrimaryColumn('varchar', { length: 66 })
blockHash!: string
@Column('integer')
blockNumber!: number;
@ManyToOne(() => Transaction, transaction => transaction.burns)

View File

@ -7,7 +7,10 @@ export class Factory {
@PrimaryColumn('varchar', { length: 42 })
id!: string;
@PrimaryColumn('integer')
@PrimaryColumn('varchar', { length: 66 })
blockHash!: string
@Column('integer')
blockNumber!: number;
@Column('bigint', { default: BigInt(0) })

View File

@ -11,7 +11,10 @@ export class Mint {
@PrimaryColumn('varchar')
id!: string;
@PrimaryColumn('integer')
@PrimaryColumn('varchar', { length: 66 })
blockHash!: string
@Column('integer')
blockNumber!: number;
@ManyToOne(() => Transaction, transaction => transaction.mints)

View File

@ -9,7 +9,10 @@ export class Pool {
@PrimaryColumn('varchar', { length: 42 })
id!: string;
@PrimaryColumn('integer')
@PrimaryColumn('varchar', { length: 66 })
blockHash!: string
@Column('integer')
blockNumber!: number;
@ManyToOne(() => Token)

View File

@ -9,7 +9,10 @@ export class PoolDayData {
@PrimaryColumn('varchar')
id!: string;
@PrimaryColumn('integer')
@PrimaryColumn('varchar', { length: 66 })
blockHash!: string
@Column('integer')
blockNumber!: number;
@Column('integer')

View File

@ -9,7 +9,10 @@ export class PoolHourData {
@PrimaryColumn('varchar')
id!: string;
@PrimaryColumn('integer')
@PrimaryColumn('varchar', { length: 66 })
blockHash!: string
@Column('integer')
blockNumber!: number;
@Column('integer')

View File

@ -13,7 +13,10 @@ export class Position {
@PrimaryColumn('varchar')
id!: string;
@PrimaryColumn('integer')
@PrimaryColumn('varchar', { length: 66 })
blockHash!: string
@Column('integer')
blockNumber!: number;
@Column('bigint')

View File

@ -12,7 +12,10 @@ export class PositionSnapshot {
@PrimaryColumn('varchar')
id!: string;
@PrimaryColumn('integer')
@PrimaryColumn('varchar', { length: 66 })
blockHash!: string
@Column('integer')
blockNumber!: number;
@Column('bigint')

View File

@ -11,7 +11,10 @@ export class Swap {
@PrimaryColumn('varchar')
id!: string;
@PrimaryColumn('integer')
@PrimaryColumn('varchar', { length: 66 })
blockHash!: string
@Column('integer')
blockNumber!: number;
@ManyToOne(() => Transaction, transaction => transaction.swaps)

View File

@ -9,7 +9,10 @@ export class Tick {
@PrimaryColumn('varchar')
id!: string;
@PrimaryColumn('integer')
@PrimaryColumn('varchar', { length: 66 })
blockHash!: string
@Column('integer')
blockNumber!: number;
@Column('bigint')

View File

@ -9,7 +9,10 @@ export class Token {
@PrimaryColumn('varchar', { length: 42 })
id!: string;
@PrimaryColumn('integer')
@PrimaryColumn('varchar', { length: 66 })
blockHash!: string
@Column('integer')
blockNumber!: number;
@Column('varchar')

View File

@ -9,7 +9,10 @@ export class TokenDayData {
@PrimaryColumn('varchar')
id!: string;
@PrimaryColumn('integer')
@PrimaryColumn('varchar', { length: 66 })
blockHash!: string
@Column('integer')
blockNumber!: number;
@Column('integer')

View File

@ -9,7 +9,10 @@ export class TokenHourData {
@PrimaryColumn('varchar')
id!: string;
@PrimaryColumn('integer')
@PrimaryColumn('varchar', { length: 66 })
blockHash!: string
@Column('integer')
blockNumber!: number;
@Column('integer')

View File

@ -11,7 +11,10 @@ export class Transaction {
@PrimaryColumn('varchar')
id!: string;
@PrimaryColumn('integer')
@PrimaryColumn('varchar', { length: 66 })
blockHash!: string
@Column('integer')
blockNumber!: number;
@Column('numeric', { default: 0, transformer: decimalTransformer })

View File

@ -7,7 +7,10 @@ export class UniswapDayData {
@PrimaryColumn('varchar')
id!: string;
@PrimaryColumn('integer')
@PrimaryColumn('varchar', { length: 66 })
blockHash!: string
@Column('integer')
blockNumber!: number;
@Column('integer')

View File

@ -2,7 +2,7 @@ import assert from 'assert';
import debug from 'debug';
import { DeepPartial } from 'typeorm';
import JSONbig from 'json-bigint';
import { BigNumber, utils } from 'ethers';
import { utils } from 'ethers';
import { Client as UniClient } from '@vulcanize/uni-watcher';
import { Client as ERC20Client } from '@vulcanize/erc20-watcher';
@ -10,12 +10,19 @@ import { findEthPerToken, getEthPriceInUSD, getTrackedAmountUSD, sqrtPriceX96ToT
import { updatePoolDayData, updatePoolHourData, updateTokenDayData, updateTokenHourData, updateUniswapDayData } from './utils/interval-updates';
import { Token } from './entity/Token';
import { convertTokenToDecimal, loadTransaction, safeDiv } from './utils';
import { loadTick } from './utils/tick';
import { createTick } from './utils/tick';
import Decimal from 'decimal.js';
import { Position } from './entity/Position';
import { Database } 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';
import { Bundle } from './entity/Bundle';
import { Pool } from './entity/Pool';
import { Mint } from './entity/Mint';
import { Burn } from './entity/Burn';
import { Swap } from './entity/Swap';
import { PositionSnapshot } from './entity/PositionSnapshot';
const log = debug('vulcanize:indexer');
@ -88,7 +95,7 @@ export class Indexer {
const resultEvent = this.getResultEvent(dbEvent);
// TODO: Process proof (proof.data) in event.
const { contract, block, tx, event } = resultEvent;
const { contract, tx, block, event } = resultEvent;
const { __typename: eventType } = event;
switch (eventType) {
@ -179,16 +186,33 @@ export class Indexer {
}
async _handlePoolCreated (block: Block, contractAddress: string, tx: Transaction, poolCreatedEvent: PoolCreatedEvent): Promise<void> {
const { number: blockNumber, hash: blockHash } = block;
const { number: blockNumber } = block;
const { token0: token0Address, token1: token1Address, fee, pool: poolAddress } = poolCreatedEvent;
// Temp fix from Subgraph mapping code.
if (utils.getAddress(poolAddress) === utils.getAddress('0x8fe8d9bb8eeba3ed688069c3d6b556c9ca258248')) {
return;
}
// Load factory.
const factory = await this._db.loadFactory({ blockNumber, id: contractAddress });
let factory = await this._db.getFactory({ blockNumber, id: contractAddress });
if (!factory) {
factory = new Factory();
factory.id = contractAddress;
factory = await this._db.saveFactory(factory, block);
// Create new bundle for tracking eth price.
const bundle = new Bundle();
bundle.id = '1';
await this._db.saveBundle(bundle, block);
}
// Update Factory.
let factoryPoolCount = BigNumber.from(factory.poolCount);
factoryPoolCount = factoryPoolCount.add(1);
factory.poolCount = BigInt(factoryPoolCount.toHexString());
factory.poolCount = BigInt(factory.poolCount) + BigInt(1);
let pool = new Pool();
pool.id = poolAddress;
// Get Tokens.
let [token0, token1] = await Promise.all([
@ -198,61 +222,59 @@ export class Indexer {
// Create Tokens if not present.
if (!token0) {
token0 = await this._createToken(blockHash, blockNumber, token0Address);
token0 = await this._createToken(block, token0Address);
}
if (!token1) {
token1 = await this._createToken(blockHash, blockNumber, token1Address);
token1 = await this._createToken(block, token1Address);
}
// Create new Pool entity.
// Skipping adding createdAtTimestamp field as it is not queried in frontend subgraph.
const pool = await this._db.loadPool({
blockNumber,
id: poolAddress,
token0: token0,
token1: token1,
feeTier: BigInt(fee)
});
pool.token0 = token0;
pool.token1 = token1;
pool.feeTier = BigInt(fee);
pool = await this._db.savePool(pool, block);
// Update white listed pools.
if (WHITELIST_TOKENS.includes(token0.id)) {
token1.whitelistPools.push(pool);
await this._db.saveToken(token1, blockNumber);
}
if (WHITELIST_TOKENS.includes(token1.id)) {
token0.whitelistPools.push(pool);
await this._db.saveToken(token0, blockNumber);
}
// Skipping adding createdAtTimestamp field as it is not queried in frontend subgraph.
// Save entities to DB.
await this._db.saveFactory(factory, blockNumber);
await this._db.saveToken(token0, block);
await this._db.saveToken(token1, block);
await this._db.saveFactory(factory, block);
}
/**
* Create new Token.
* @param tokenAddress
*/
async _createToken (blockHash: string, blockNumber: number, tokenAddress: string): Promise<Token> {
const { value: symbol } = await this._erc20Client.getSymbol(blockHash, tokenAddress);
const { value: name } = await this._erc20Client.getName(blockHash, tokenAddress);
const { value: totalSupply } = await this._erc20Client.getTotalSupply(blockHash, tokenAddress);
async _createToken (block: Block, tokenAddress: string): Promise<Token> {
const token = new Token();
token.id = tokenAddress;
const { value: symbol } = await this._erc20Client.getSymbol(block.hash, tokenAddress);
const { value: name } = await this._erc20Client.getName(block.hash, tokenAddress);
const { value: totalSupply } = await this._erc20Client.getTotalSupply(block.hash, tokenAddress);
// TODO: Decimals not implemented by erc20-watcher.
// const { value: decimals } = await this._erc20Client.getDecimals(blockHash, tokenAddress);
return this._db.loadToken({
blockNumber,
id: tokenAddress,
symbol,
name,
totalSupply
});
token.symbol = symbol;
token.name = name;
token.totalSupply = totalSupply;
return this._db.saveToken(token, block);
}
async _handleInitialize (block: Block, contractAddress: string, tx: Transaction, initializeEvent: InitializeEvent): Promise<void> {
const { number: blockNumber, timestamp: blockTimestamp } = block;
const { number: blockNumber } = block;
const { sqrtPriceX96, tick } = initializeEvent;
const pool = await this._db.getPool({ id: contractAddress, blockNumber });
assert(pool, `Pool ${contractAddress} not found.`);
@ -260,39 +282,41 @@ export class Indexer {
// Update Pool.
pool.sqrtPrice = BigInt(sqrtPriceX96);
pool.tick = BigInt(tick);
this._db.savePool(pool, blockNumber);
// Update ETH price now that prices could have changed.
const bundle = await this._db.loadBundle({ id: '1', blockNumber });
bundle.ethPriceUSD = await getEthPriceInUSD(this._db);
this._db.saveBundle(bundle, blockNumber);
await updatePoolDayData(this._db, { contractAddress, blockNumber, blockTimestamp });
await updatePoolHourData(this._db, { contractAddress, blockNumber, blockTimestamp });
this._db.savePool(pool, block);
// Update token prices.
const [token0, token1] = await Promise.all([
this._db.getToken({ id: pool.token0.id, blockNumber }),
this._db.getToken({ id: pool.token1.id, blockNumber })
]);
// Update ETH price now that prices could have changed.
const bundle = await this._db.getBundle({ id: '1', blockNumber });
assert(bundle);
bundle.ethPriceUSD = await getEthPriceInUSD(this._db);
this._db.saveBundle(bundle, block);
await updatePoolDayData(this._db, { contractAddress, block });
await updatePoolHourData(this._db, { contractAddress, block });
assert(token0 && token1, 'Pool tokens not found.');
// Update token prices.
token0.derivedETH = await findEthPerToken(token0);
token1.derivedETH = await findEthPerToken(token1);
await Promise.all([
this._db.saveToken(token0, blockNumber),
this._db.saveToken(token1, blockNumber)
this._db.saveToken(token0, block),
this._db.saveToken(token1, block)
]);
}
async _handleMint (block: Block, contractAddress: string, tx: Transaction, mintEvent: MintEvent): Promise<void> {
const { number: blockNumber, timestamp: blockTimestamp } = block;
const { hash: txHash } = tx;
const bundle = await this._db.loadBundle({ id: '1', blockNumber });
const { number: blockNumber } = block;
const bundle = await this._db.getBundle({ id: '1', blockNumber });
assert(bundle);
const poolAddress = contractAddress;
const pool = await this._db.loadPool({ id: poolAddress, blockNumber });
const pool = await this._db.getPool({ id: poolAddress, blockNumber });
assert(pool);
// TODO: In subgraph factory is fetched by hardcoded factory address.
// Currently fetching first factory in database as only one exists.
@ -349,29 +373,27 @@ export class Indexer {
factory.totalValueLockedETH = factory.totalValueLockedETH.plus(pool.totalValueLockedETH);
factory.totalValueLockedUSD = factory.totalValueLockedETH.times(bundle.ethPriceUSD);
const transaction = await loadTransaction(this._db, { txHash, blockNumber, blockTimestamp });
const transaction = await loadTransaction(this._db, { block, tx });
await this._db.loadMint({
id: transaction.id + '#' + pool.txCount.toString(),
blockNumber,
transaction,
timestamp: transaction.timestamp,
pool,
token0: pool.token0,
token1: pool.token1,
owner: mintEvent.owner,
sender: mintEvent.sender,
const mint = new Mint();
mint.id = transaction.id + '#' + pool.txCount.toString();
mint.transaction = transaction;
mint.timestamp = transaction.timestamp;
mint.pool = pool;
mint.token0 = pool.token0;
mint.token1 = pool.token1;
mint.owner = mintEvent.owner;
mint.sender = mintEvent.sender;
// TODO: Assign origin with Transaction from address.
// origin: event.transaction.from
// TODO: Assign origin with Transaction from address.
// origin: event.transaction.from
amount: mintEvent.amount,
amount0: amount0,
amount1: amount1,
amountUSD: amountUSD,
tickLower: mintEvent.tickLower,
tickUpper: mintEvent.tickUpper
});
mint.amount = mintEvent.amount;
mint.amount0 = amount0;
mint.amount1 = amount1;
mint.amountUSD = amountUSD;
mint.tickLower = mintEvent.tickLower;
mint.tickUpper = mintEvent.tickUpper;
// Tick entities.
const lowerTickIdx = mintEvent.tickLower;
@ -380,8 +402,16 @@ export class Indexer {
const lowerTickId = poolAddress + '#' + mintEvent.tickLower.toString();
const upperTickId = poolAddress + '#' + mintEvent.tickUpper.toString();
const lowerTick = await loadTick(this._db, lowerTickId, BigInt(lowerTickIdx), pool, blockNumber);
const upperTick = await loadTick(this._db, upperTickId, BigInt(upperTickIdx), pool, blockNumber);
let lowerTick = await this._db.getTick({ id: lowerTickId, blockNumber });
let upperTick = await this._db.getTick({ id: upperTickId, blockNumber });
if (!lowerTick) {
lowerTick = await createTick(this._db, lowerTickId, BigInt(lowerTickIdx), pool, block);
}
if (!upperTick) {
upperTick = await createTick(this._db, upperTickId, BigInt(upperTickIdx), pool, block);
}
const amount = BigInt(mintEvent.amount);
lowerTick.liquidityGross = BigInt(lowerTick.liquidityGross) + amount;
@ -392,36 +422,38 @@ export class Indexer {
// TODO: Update Tick's volume, fees, and liquidity provider count.
// Computing these on the tick level requires reimplementing some of the swapping code from v3-core.
await updateUniswapDayData(this._db, { blockNumber, contractAddress, blockTimestamp });
await updatePoolDayData(this._db, { blockNumber, contractAddress, blockTimestamp });
await updatePoolHourData(this._db, { blockNumber, contractAddress, blockTimestamp });
await updateTokenDayData(this._db, token0, { blockNumber, blockTimestamp });
await updateTokenDayData(this._db, token1, { blockNumber, blockTimestamp });
await updateTokenHourData(this._db, token0, { blockNumber, blockTimestamp });
await updateTokenHourData(this._db, token1, { blockNumber, blockTimestamp });
await updateUniswapDayData(this._db, { block, contractAddress });
await updatePoolDayData(this._db, { block, contractAddress });
await updatePoolHourData(this._db, { block, contractAddress });
await updateTokenDayData(this._db, token0, { block });
await updateTokenDayData(this._db, token1, { block });
await updateTokenHourData(this._db, token0, { block });
await updateTokenHourData(this._db, token1, { block });
await Promise.all([
this._db.saveToken(token0, blockNumber),
this._db.saveToken(token1, blockNumber)
this._db.saveToken(token0, block),
this._db.saveToken(token1, block)
]);
await this._db.savePool(pool, blockNumber);
await this._db.saveFactory(factory, blockNumber);
await this._db.savePool(pool, block);
await this._db.saveFactory(factory, block);
await this._db.saveMint(mint, block);
await Promise.all([
await this._db.saveTick(lowerTick, blockNumber),
await this._db.saveTick(upperTick, blockNumber)
await this._db.saveTick(lowerTick, block),
await this._db.saveTick(upperTick, block)
]);
// Skipping update inner tick vars and tick day data as they are not queried.
}
async _handleBurn (block: Block, contractAddress: string, tx: Transaction, burnEvent: BurnEvent): Promise<void> {
const { number: blockNumber, timestamp: blockTimestamp } = block;
const { hash: txHash } = tx;
const bundle = await this._db.loadBundle({ id: '1', blockNumber });
const { number: blockNumber } = block;
const bundle = await this._db.getBundle({ id: '1', blockNumber });
assert(bundle);
const poolAddress = contractAddress;
const pool = await this._db.loadPool({ id: poolAddress, blockNumber });
const pool = await this._db.getPool({ id: poolAddress, blockNumber });
assert(pool);
// TODO: In subgraph factory is fetched by hardcoded factory address.
// Currently fetching first factory in database as only one exists.
@ -479,74 +511,75 @@ export class Indexer {
factory.totalValueLockedUSD = factory.totalValueLockedETH.times(bundle.ethPriceUSD);
// Burn entity.
const transaction = await loadTransaction(this._db, { txHash, blockNumber, blockTimestamp });
const transaction = await loadTransaction(this._db, { block, tx });
await this._db.loadBurn({
id: transaction.id + '#' + pool.txCount.toString(),
blockNumber,
transaction,
timestamp: transaction.timestamp,
pool,
token0: pool.token0,
token1: pool.token1,
owner: burnEvent.owner,
const burn = new Burn();
burn.id = transaction.id + '#' + pool.txCount.toString();
burn.transaction = transaction;
burn.timestamp = transaction.timestamp;
burn.pool = pool;
burn.token0 = pool.token0;
burn.token1 = pool.token1;
burn.owner = burnEvent.owner;
// TODO: Assign origin with Transaction from address.
// origin: event.transaction.from
// TODO: Assign origin with Transaction from address.
// origin: event.transaction.from
amount: burnEvent.amount,
amount0,
amount1,
amountUSD,
tickLower: burnEvent.tickLower,
tickUpper: burnEvent.tickUpper
});
burn.amount = burnEvent.amount;
burn.amount0 = amount0;
burn.amount1 = amount1;
burn.amountUSD = amountUSD;
burn.tickLower = burnEvent.tickLower;
burn.tickUpper = burnEvent.tickUpper;
// Tick entities.
const lowerTickId = poolAddress + '#' + (burnEvent.tickLower).toString();
const upperTickId = poolAddress + '#' + (burnEvent.tickUpper).toString();
const lowerTick = await this._db.loadTick({ id: lowerTickId, blockNumber });
const upperTick = await this._db.loadTick({ id: upperTickId, blockNumber });
const lowerTick = await this._db.getTick({ id: lowerTickId, blockNumber });
const upperTick = await this._db.getTick({ id: upperTickId, blockNumber });
assert(lowerTick && upperTick);
const amount = BigInt(burnEvent.amount);
lowerTick.liquidityGross = BigInt(lowerTick.liquidityGross) - amount;
lowerTick.liquidityNet = BigInt(lowerTick.liquidityNet) - amount;
upperTick.liquidityGross = BigInt(upperTick.liquidityGross) - amount;
upperTick.liquidityNet = BigInt(upperTick.liquidityNet) + amount;
await updateUniswapDayData(this._db, { blockNumber, contractAddress, blockTimestamp });
await updatePoolDayData(this._db, { blockNumber, contractAddress, blockTimestamp });
await updatePoolHourData(this._db, { blockNumber, contractAddress, blockTimestamp });
await updateTokenDayData(this._db, token0, { blockNumber, blockTimestamp });
await updateTokenDayData(this._db, token0, { blockNumber, blockTimestamp });
await updateTokenHourData(this._db, token0, { blockNumber, blockTimestamp });
await updateTokenHourData(this._db, token0, { blockNumber, blockTimestamp });
await updateUniswapDayData(this._db, { block, contractAddress });
await updatePoolDayData(this._db, { block, contractAddress });
await updatePoolHourData(this._db, { block, contractAddress });
await updateTokenDayData(this._db, token0, { block });
await updateTokenDayData(this._db, token0, { block });
await updateTokenHourData(this._db, token0, { block });
await updateTokenHourData(this._db, token0, { block });
// Skipping update Tick fee and Tick day data as they are not queried.
await Promise.all([
await this._db.saveTick(lowerTick, blockNumber),
await this._db.saveTick(upperTick, blockNumber)
await this._db.saveTick(lowerTick, block),
await this._db.saveTick(upperTick, block)
]);
await Promise.all([
this._db.saveToken(token0, blockNumber),
this._db.saveToken(token1, blockNumber)
this._db.saveToken(token0, block),
this._db.saveToken(token1, block)
]);
await this._db.savePool(pool, blockNumber);
await this._db.saveFactory(factory, blockNumber);
await this._db.savePool(pool, block);
await this._db.saveFactory(factory, block);
await this._db.saveBurn(burn, block);
}
async _handleSwap (block: Block, contractAddress: string, tx: Transaction, swapEvent: SwapEvent): Promise<void> {
const { number: blockNumber, timestamp: blockTimestamp } = block;
const { hash: txHash } = tx;
const bundle = await this._db.loadBundle({ id: '1', blockNumber });
const { number: blockNumber } = block;
const bundle = await this._db.getBundle({ id: '1', blockNumber });
assert(bundle);
// 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({ blockNumber }, { limit: 1 });
const pool = await this._db.loadPool({ id: contractAddress, blockNumber });
const pool = await this._db.getPool({ id: contractAddress, blockNumber });
assert(pool);
// Hot fix for bad pricing.
if (pool.id === '0x9663f2ca0454accad3e094448ea6f77443880454') {
@ -637,11 +670,11 @@ export class Indexer {
const prices = sqrtPriceX96ToTokenPrices(pool.sqrtPrice, token0 as Token, token1 as Token);
pool.token0Price = prices[0];
pool.token1Price = prices[1];
this._db.savePool(pool, blockNumber);
this._db.savePool(pool, block);
// Update USD pricing.
bundle.ethPriceUSD = await getEthPriceInUSD(this._db);
this._db.saveBundle(bundle, blockNumber);
this._db.saveBundle(bundle, block);
token0.derivedETH = await findEthPerToken(token0);
token1.derivedETH = await findEthPerToken(token1);
@ -661,39 +694,37 @@ export class Indexer {
token1.totalValueLockedUSD = token1.totalValueLocked.times(token1.derivedETH).times(bundle.ethPriceUSD);
// Create Swap event
const transaction = await loadTransaction(this._db, { txHash, blockNumber, blockTimestamp });
const transaction = await loadTransaction(this._db, { block, tx });
await this._db.loadSwap({
id: transaction.id + '#' + pool.txCount.toString(),
blockNumber,
transaction,
timestamp: transaction.timestamp,
pool,
token0: pool.token0,
token1: pool.token1,
sender: swapEvent.sender,
const swap = new Swap();
swap.id = transaction.id + '#' + pool.txCount.toString();
swap.transaction = transaction;
swap.timestamp = transaction.timestamp;
swap.pool = pool;
swap.token0 = pool.token0;
swap.token1 = pool.token1;
swap.sender = swapEvent.sender;
// TODO: Assign origin with Transaction from address.
// origin: event.transaction.from
// TODO: Assign origin with Transaction from address.
// origin: event.transaction.from
recipient: swapEvent.recipient,
amount0: amount0,
amount1: amount1,
amountUSD: amountTotalUSDTracked,
tick: BigInt(swapEvent.tick),
sqrtPriceX96: swapEvent.sqrtPriceX96
});
swap.recipient = swapEvent.recipient;
swap.amount0 = amount0;
swap.amount1 = amount1;
swap.amountUSD = amountTotalUSDTracked;
swap.tick = BigInt(swapEvent.tick);
swap.sqrtPriceX96 = swapEvent.sqrtPriceX96;
// Skipping update pool fee growth as they are not queried.
// Interval data.
const uniswapDayData = await updateUniswapDayData(this._db, { blockNumber, contractAddress, blockTimestamp });
const poolDayData = await updatePoolDayData(this._db, { blockNumber, contractAddress, blockTimestamp });
const poolHourData = await updatePoolHourData(this._db, { blockNumber, contractAddress, blockTimestamp });
const token0DayData = await updateTokenDayData(this._db, token0, { blockNumber, blockTimestamp });
const token1DayData = await updateTokenDayData(this._db, token0, { blockNumber, blockTimestamp });
const token0HourData = await updateTokenHourData(this._db, token0, { blockNumber, blockTimestamp });
const token1HourData = await updateTokenHourData(this._db, token0, { blockNumber, blockTimestamp });
const uniswapDayData = await updateUniswapDayData(this._db, { block, contractAddress });
const poolDayData = await updatePoolDayData(this._db, { block, contractAddress });
const poolHourData = await updatePoolHourData(this._db, { block, contractAddress });
const token0DayData = await updateTokenDayData(this._db, token0, { block });
const token1DayData = await updateTokenDayData(this._db, token0, { block });
const token0HourData = await updateTokenHourData(this._db, token0, { block });
const token1HourData = await updateTokenHourData(this._db, token0, { block });
// Update volume metrics.
uniswapDayData.volumeETH = uniswapDayData.volumeETH.plus(amountTotalETHTracked);
@ -730,20 +761,20 @@ export class Indexer {
token1HourData.untrackedVolumeUSD = token1HourData.untrackedVolumeUSD.plus(amountTotalUSDTracked);
token1HourData.feesUSD = token1HourData.feesUSD.plus(feesUSD);
this._db.saveTokenDayData(token0DayData, blockNumber);
this._db.saveTokenDayData(token1DayData, blockNumber);
this._db.saveUniswapDayData(uniswapDayData, blockNumber);
this._db.savePoolDayData(poolDayData, blockNumber);
this._db.saveFactory(factory, blockNumber);
this._db.savePool(pool, blockNumber);
this._db.saveToken(token0, blockNumber);
this._db.saveToken(token1, blockNumber);
await this._db.saveSwap(swap, block);
await this._db.saveTokenDayData(token0DayData, block);
await this._db.saveTokenDayData(token1DayData, block);
await this._db.saveUniswapDayData(uniswapDayData, block);
await this._db.savePoolDayData(poolDayData, block);
await this._db.saveFactory(factory, block);
await this._db.savePool(pool, block);
await this._db.saveToken(token0, block);
await this._db.saveToken(token1, block);
// Skipping update of inner vars of current or crossed ticks as they are not queried.
}
async _handleIncreaseLiquidity (block: Block, contractAddress: string, tx: Transaction, event: IncreaseLiquidityEvent): Promise<void> {
const { number: blockNumber } = block;
const position = await this._getPosition(block, contractAddress, tx, BigInt(event.tokenId));
// position was not able to be fetched.
@ -768,14 +799,13 @@ export class Indexer {
await this._updateFeeVars(position, block, contractAddress, BigInt(event.tokenId));
await this._db.savePosition(position, blockNumber);
await this._db.savePosition(position, block);
await this._savePositionSnapshot(position, block, tx);
}
async _handleDecreaseLiquidity (block: Block, contractAddress: string, tx: Transaction, event: DecreaseLiquidityEvent): Promise<void> {
const { number: blockNumber } = block;
const position = await this._getPosition(block, contractAddress, tx, BigInt(event.tokenId));
let position = await this._getPosition(block, contractAddress, tx, BigInt(event.tokenId));
// Position was not able to be fetched.
if (position == null) {
@ -796,15 +826,14 @@ export class Indexer {
position.depositedToken0 = position.depositedToken0.plus(amount0);
position.depositedToken1 = position.depositedToken1.plus(amount1);
await this._updateFeeVars(position, block, contractAddress, BigInt(event.tokenId));
position = await this._updateFeeVars(position, block, contractAddress, BigInt(event.tokenId));
await this._db.savePosition(position, blockNumber);
await this._db.savePosition(position, block);
await this._savePositionSnapshot(position, block, tx);
}
async _handleCollect (block: Block, contractAddress: string, tx: Transaction, event: CollectEvent): Promise<void> {
const { number: blockNumber } = block;
let position = await this._getPosition(block, contractAddress, tx, BigInt(event.tokenId));
// Position was not able to be fetched.
@ -826,7 +855,7 @@ export class Indexer {
position = await this._updateFeeVars(position, block, contractAddress, BigInt(event.tokenId));
await this._db.savePosition(position, blockNumber);
await this._db.savePosition(position, block);
await this._savePositionSnapshot(position, block, tx);
}
@ -839,14 +868,13 @@ export class Indexer {
}
position.owner = event.to;
await this._db.savePosition(position, block.number);
await this._db.savePosition(position, block);
await this._savePositionSnapshot(position, block, tx);
}
async _getPosition (block: Block, contractAddress: string, tx: Transaction, tokenId: bigint): Promise<Position | null> {
const { number: blockNumber, hash: blockHash, timestamp: blockTimestamp } = block;
const { hash: txHash } = tx;
const { number: blockNumber, hash: blockHash } = block;
let position = await this._db.getPosition({ id: tokenId.toString(), blockNumber });
if (!position) {
@ -861,31 +889,36 @@ export class Indexer {
const { pool: poolAddress } = await this._uniClient.getPool(blockHash, token0Address, token1Address, fee);
const transaction = await loadTransaction(this._db, { txHash, blockNumber, blockTimestamp });
position = new Position();
position.id = tokenId.toString();
const pool = await this._db.getPool({ id: poolAddress, blockNumber });
assert(pool);
position.pool = pool;
const [token0, token1] = await Promise.all([
this._db.getToken({ id: token0Address, blockNumber }),
this._db.getToken({ id: token0Address, blockNumber })
]);
assert(token0 && token1);
position.token0 = token0;
position.token1 = token1;
const [tickLower, tickUpper] = await Promise.all([
this._db.getTick({ id: poolAddress.concat('#').concat(nfpmPosition.tickLower.toString()), blockNumber }),
this._db.getTick({ id: poolAddress.concat('#').concat(nfpmPosition.tickUpper.toString()), blockNumber })
]);
assert(tickLower && tickUpper);
position.tickLower = tickLower;
position.tickUpper = tickUpper;
position = await this._db.loadPosition({
id: tokenId.toString(),
blockNumber,
pool,
token0,
token1,
tickLower,
tickUpper,
transaction,
feeGrowthInside0LastX128: BigInt(nfpmPosition.feeGrowthInside0LastX128.toString()),
feeGrowthInside1LastX128: BigInt(nfpmPosition.feeGrowthInside1LastX128.toString())
});
const transaction = await loadTransaction(this._db, { block, tx });
position.transaction = transaction;
position.feeGrowthInside0LastX128 = BigInt(nfpmPosition.feeGrowthInside0LastX128.toString());
position.feeGrowthInside1LastX128 = BigInt(nfpmPosition.feeGrowthInside1LastX128.toString());
position = await this._db.savePosition(position, block);
}
}
@ -904,25 +937,24 @@ export class Indexer {
}
async _savePositionSnapshot (position: Position, block: Block, tx: Transaction): Promise<void> {
const transaction = await loadTransaction(this._db, { txHash: tx.hash, blockNumber: block.number, blockTimestamp: block.timestamp });
const positionSnapshot = new PositionSnapshot();
positionSnapshot.id = position.id.concat('#').concat(block.number.toString());
positionSnapshot.blockNumber = block.number;
positionSnapshot.owner = position.owner;
positionSnapshot.pool = position.pool;
positionSnapshot.position = position;
positionSnapshot.timestamp = BigInt(block.timestamp);
positionSnapshot.liquidity = position.liquidity;
positionSnapshot.depositedToken0 = position.depositedToken0;
positionSnapshot.depositedToken1 = position.depositedToken1;
positionSnapshot.withdrawnToken0 = position.withdrawnToken0;
positionSnapshot.withdrawnToken1 = position.withdrawnToken1;
positionSnapshot.collectedFeesToken0 = position.collectedFeesToken0;
positionSnapshot.collectedFeesToken1 = position.collectedFeesToken1;
positionSnapshot.transaction = await loadTransaction(this._db, { block, tx });
positionSnapshot.feeGrowthInside0LastX128 = position.feeGrowthInside0LastX128;
positionSnapshot.feeGrowthInside1LastX128 = position.feeGrowthInside1LastX128;
await this._db.loadPositionSnapshot({
id: position.id.concat('#').concat(block.number.toString()),
blockNumber: block.number,
owner: position.owner,
pool: position.pool,
position: position,
timestamp: block.timestamp,
liquidity: position.liquidity,
depositedToken0: position.depositedToken0,
depositedToken1: position.depositedToken1,
withdrawnToken0: position.withdrawnToken0,
withdrawnToken1: position.withdrawnToken1,
collectedFeesToken0: position.collectedFeesToken0,
collectedFeesToken1: position.collectedFeesToken1,
transaction,
feeGrowthInside0LastX128: position.feeGrowthInside0LastX128,
feeGrowthInside1LastX128: position.feeGrowthInside1LastX128
});
await this._db.savePositionSnapshot(positionSnapshot, block);
}
}

View File

@ -1,8 +1,9 @@
import Decimal from 'decimal.js';
import { BigNumber } from 'ethers';
import { Transaction } from '../entity/Transaction';
import { Transaction as TransactionEntity } from '../entity/Transaction';
import { Database } from '../database';
import { Block, Transaction } from '../events';
export const exponentToBigDecimal = (decimals: bigint): Decimal => {
let bd = new Decimal(1);
@ -22,19 +23,19 @@ export const convertTokenToDecimal = (tokenAmount: bigint, exchangeDecimals: big
return (new Decimal(tokenAmount.toString())).div(exponentToBigDecimal(exchangeDecimals));
};
export const loadTransaction = async (db: Database, event: { txHash: string, blockNumber: number, blockTimestamp: number }): Promise<Transaction> => {
const { txHash, blockNumber, blockTimestamp } = event;
export const loadTransaction = async (db: Database, event: { block: Block, tx: Transaction }): Promise<TransactionEntity> => {
const { tx, block } = event;
let transaction = await db.getTransaction({ id: tx.hash, blockNumber: block.number });
const transaction = await db.loadTransaction({
id: txHash,
blockNumber,
timestamp: BigInt(blockTimestamp)
});
if (!transaction) {
transaction = new TransactionEntity();
transaction.id = tx.hash;
}
transaction.blockNumber = blockNumber;
transaction.timestamp = BigInt(blockTimestamp);
transaction.blockNumber = block.number;
transaction.timestamp = BigInt(block.timestamp);
return db.saveTransaction(transaction, blockNumber);
return db.saveTransaction(transaction, block);
};
// Return 0 if denominator is 0 in division.

View File

@ -1,3 +1,4 @@
import assert from 'assert';
import { BigNumber } from 'ethers';
import { Database } from '../database';
@ -7,56 +8,63 @@ import { Token } from '../entity/Token';
import { TokenDayData } from '../entity/TokenDayData';
import { TokenHourData } from '../entity/TokenHourData';
import { UniswapDayData } from '../entity/UniswapDayData';
import { Block } from '../events';
/**
* Tracks global aggregate data over daily windows.
* @param db
* @param event
*/
export const updateUniswapDayData = async (db: Database, event: { contractAddress: string, blockNumber: number, blockTimestamp: number }): Promise<UniswapDayData> => {
const { blockNumber, blockTimestamp } = event;
export const updateUniswapDayData = async (db: Database, event: { contractAddress: string, block: Block }): Promise<UniswapDayData> => {
const { block } = event;
// 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({ blockNumber }, { limit: 1 });
const [factory] = await db.getFactories({ blockNumber: block.number }, { limit: 1 });
const dayID = Math.floor(blockTimestamp / 86400); // Rounded.
const dayID = Math.floor(block.timestamp / 86400); // Rounded.
const dayStartTimestamp = dayID * 86400;
const uniswapDayData = await db.loadUniswapDayData({
id: dayID.toString(),
blockNumber,
date: dayStartTimestamp,
tvlUSD: factory.totalValueLockedUSD,
txCount: factory.txCount
});
let uniswapDayData = await db.getUniswapDayData({ id: dayID.toString(), blockNumber: block.number });
if (!uniswapDayData) {
uniswapDayData = new UniswapDayData();
uniswapDayData.id = dayID.toString();
uniswapDayData.date = dayStartTimestamp;
uniswapDayData.tvlUSD = factory.totalValueLockedUSD;
uniswapDayData.txCount = factory.txCount;
}
uniswapDayData.tvlUSD = factory.totalValueLockedUSD;
uniswapDayData.txCount = factory.txCount;
return db.saveUniswapDayData(uniswapDayData, blockNumber);
return db.saveUniswapDayData(uniswapDayData, block);
};
export const updatePoolDayData = async (db: Database, event: { contractAddress: string, blockNumber: number, blockTimestamp: number }): Promise<PoolDayData> => {
const { contractAddress, blockNumber, blockTimestamp } = event;
const dayID = Math.floor(blockTimestamp / 86400);
export const updatePoolDayData = async (db: Database, event: { contractAddress: string, block: Block }): Promise<PoolDayData> => {
const { contractAddress, block } = event;
const dayID = Math.floor(block.timestamp / 86400);
const dayStartTimestamp = dayID * 86400;
const dayPoolID = contractAddress
.concat('-')
.concat(dayID.toString());
const pool = await db.loadPool({ id: contractAddress, blockNumber });
const pool = await db.getPool({ id: contractAddress, blockNumber: block.number });
assert(pool);
let poolDayData = await db.loadPoolDayData({
id: dayPoolID,
blockNumber,
date: dayStartTimestamp,
pool: pool,
open: pool.token0Price,
high: pool.token0Price,
low: pool.token0Price,
close: pool.token0Price
});
let poolDayData = await db.getPoolDayData({ id: dayPoolID, blockNumber: block.number });
if (!poolDayData) {
poolDayData = new PoolDayData();
poolDayData.id = dayPoolID;
poolDayData.date = dayStartTimestamp;
poolDayData.pool = pool;
poolDayData.open = pool.token0Price;
poolDayData.high = pool.token0Price;
poolDayData.low = pool.token0Price;
poolDayData.close = pool.token0Price;
poolDayData = await db.savePoolDayData(poolDayData, block);
}
if (Number(pool.token0Price) > Number(poolDayData.high)) {
poolDayData.high = pool.token0Price;
@ -75,32 +83,36 @@ export const updatePoolDayData = async (db: Database, event: { contractAddress:
poolDayData.tick = pool.tick;
poolDayData.tvlUSD = pool.totalValueLockedUSD;
poolDayData.txCount = BigInt(BigNumber.from(poolDayData.txCount).add(1).toHexString());
poolDayData = await db.savePoolDayData(poolDayData, blockNumber);
poolDayData = await db.savePoolDayData(poolDayData, block);
return poolDayData;
};
export const updatePoolHourData = async (db: Database, event: { contractAddress: string, blockNumber: number, blockTimestamp: number }): Promise<PoolHourData> => {
const { contractAddress, blockNumber, blockTimestamp } = event;
const hourIndex = Math.floor(blockTimestamp / 3600); // Get unique hour within unix history.
export const updatePoolHourData = async (db: Database, event: { contractAddress: string, block: Block }): Promise<PoolHourData> => {
const { contractAddress, block } = event;
const hourIndex = Math.floor(block.timestamp / 3600); // Get unique hour within unix history.
const hourStartUnix = hourIndex * 3600; // Want the rounded effect.
const hourPoolID = contractAddress
.concat('-')
.concat(hourIndex.toString());
const pool = await db.loadPool({ id: contractAddress, blockNumber });
const pool = await db.getPool({ id: contractAddress, blockNumber: block.number });
assert(pool);
let poolHourData = await db.loadPoolHourData({
id: hourPoolID,
blockNumber,
periodStartUnix: hourStartUnix,
pool: pool,
open: pool.token0Price,
high: pool.token0Price,
low: pool.token0Price,
close: pool.token0Price
});
let poolHourData = await db.getPoolHourData({ id: hourPoolID, blockNumber: block.number });
if (!poolHourData) {
poolHourData = new PoolHourData();
poolHourData.id = hourPoolID;
poolHourData.periodStartUnix = hourStartUnix;
poolHourData.pool = pool;
poolHourData.open = pool.token0Price;
poolHourData.high = pool.token0Price;
poolHourData.low = pool.token0Price;
poolHourData.close = pool.token0Price;
poolHourData = await db.savePoolHourData(poolHourData, block);
}
if (Number(pool.token0Price) > Number(poolHourData.high)) {
poolHourData.high = pool.token0Price;
@ -119,15 +131,16 @@ export const updatePoolHourData = async (db: Database, event: { contractAddress:
poolHourData.tick = pool.tick;
poolHourData.tvlUSD = pool.totalValueLockedUSD;
poolHourData.txCount = BigInt(BigNumber.from(poolHourData.txCount).add(1).toHexString());
poolHourData = await db.savePoolHourData(poolHourData, blockNumber);
poolHourData = await db.savePoolHourData(poolHourData, block);
return poolHourData;
};
export const updateTokenDayData = async (db: Database, token: Token, event: { blockNumber: number, blockTimestamp: number }): Promise<TokenDayData> => {
const { blockNumber, blockTimestamp } = event;
const bundle = await db.loadBundle({ id: '1', blockNumber });
const dayID = Math.floor(blockTimestamp / 86400);
export const updateTokenDayData = async (db: Database, token: Token, event: { block: Block }): Promise<TokenDayData> => {
const { block } = event;
const bundle = await db.getBundle({ id: '1', blockNumber: block.number });
assert(bundle);
const dayID = Math.floor(block.timestamp / 86400);
const dayStartTimestamp = dayID * 86400;
const tokenDayID = token.id
@ -136,19 +149,21 @@ export const updateTokenDayData = async (db: Database, token: Token, event: { bl
const tokenPrice = token.derivedETH.times(bundle.ethPriceUSD);
const tokenDayData = await db.loadTokenDayData({
id: tokenDayID,
blockNumber,
date: dayStartTimestamp,
token,
open: tokenPrice,
high: tokenPrice,
low: tokenPrice,
close: tokenPrice,
priceUSD: token.derivedETH.times(bundle.ethPriceUSD),
totalValueLocked: token.totalValueLocked,
totalValueLockedUSD: token.totalValueLockedUSD
});
let tokenDayData = await db.getTokenDayData({ id: tokenDayID, blockNumber: block.number });
if (!tokenDayData) {
tokenDayData = new TokenDayData();
tokenDayData.id = tokenDayID;
tokenDayData.date = dayStartTimestamp;
tokenDayData.token = token;
tokenDayData.open = tokenPrice;
tokenDayData.high = tokenPrice;
tokenDayData.low = tokenPrice;
tokenDayData.close = tokenPrice;
tokenDayData.priceUSD = token.derivedETH.times(bundle.ethPriceUSD);
tokenDayData.totalValueLocked = token.totalValueLocked;
tokenDayData.totalValueLockedUSD = token.totalValueLockedUSD;
}
if (tokenPrice.gt(tokenDayData.high)) {
tokenDayData.high = tokenPrice;
@ -162,13 +177,14 @@ export const updateTokenDayData = async (db: Database, token: Token, event: { bl
tokenDayData.priceUSD = token.derivedETH.times(bundle.ethPriceUSD);
tokenDayData.totalValueLocked = token.totalValueLocked;
tokenDayData.totalValueLockedUSD = token.totalValueLockedUSD;
return db.saveTokenDayData(tokenDayData, blockNumber);
return db.saveTokenDayData(tokenDayData, block);
};
export const updateTokenHourData = async (db: Database, token: Token, event: { blockNumber: number, blockTimestamp: number }): Promise<TokenHourData> => {
const { blockNumber, blockTimestamp } = event;
const bundle = await db.loadBundle({ id: '1', blockNumber });
const hourIndex = Math.floor(blockTimestamp / 3600); // Get unique hour within unix history.
export const updateTokenHourData = async (db: Database, token: Token, event: { block: Block }): Promise<TokenHourData> => {
const { block } = event;
const bundle = await db.getBundle({ id: '1', blockNumber: block.number });
assert(bundle);
const hourIndex = Math.floor(block.timestamp / 3600); // Get unique hour within unix history.
const hourStartUnix = hourIndex * 3600; // Want the rounded effect.
const tokenHourID = token.id
@ -177,19 +193,21 @@ export const updateTokenHourData = async (db: Database, token: Token, event: { b
const tokenPrice = token.derivedETH.times(bundle.ethPriceUSD);
const tokenHourData = await db.loadTokenHourData({
id: tokenHourID,
blockNumber,
periodStartUnix: hourStartUnix,
token: token,
open: tokenPrice,
high: tokenPrice,
low: tokenPrice,
close: tokenPrice,
priceUSD: tokenPrice,
totalValueLocked: token.totalValueLocked,
totalValueLockedUSD: token.totalValueLockedUSD
});
let tokenHourData = await db.getTokenHourData({ id: tokenHourID, blockNumber: block.number });
if (!tokenHourData) {
tokenHourData = new TokenHourData();
tokenHourData.id = tokenHourID;
tokenHourData.periodStartUnix = hourStartUnix;
tokenHourData.token = token;
tokenHourData.open = tokenPrice;
tokenHourData.high = tokenPrice;
tokenHourData.low = tokenPrice;
tokenHourData.close = tokenPrice;
tokenHourData.priceUSD = tokenPrice;
tokenHourData.totalValueLocked = token.totalValueLocked;
tokenHourData.totalValueLockedUSD = token.totalValueLockedUSD;
}
if (tokenPrice.gt(tokenHourData.high)) {
tokenHourData.high = tokenPrice;
@ -203,5 +221,5 @@ export const updateTokenHourData = async (db: Database, token: Token, event: { b
tokenHourData.priceUSD = tokenPrice;
tokenHourData.totalValueLocked = token.totalValueLocked;
tokenHourData.totalValueLockedUSD = token.totalValueLockedUSD;
return db.saveTokenHourData(tokenHourData, blockNumber);
return db.saveTokenHourData(tokenHourData, block);
};

View File

@ -1,3 +1,4 @@
import assert from 'assert';
import Decimal from 'decimal.js';
import { BigNumber } from 'ethers';
@ -125,7 +126,8 @@ export const getTrackedAmountUSD = async (
tokenAmount1: Decimal,
token1: Token
): Promise<Decimal> => {
const bundle = await db.loadBundle({ id: '1' });
const bundle = await db.getBundle({ id: '1' });
assert(bundle);
const price0USD = token0.derivedETH.times(bundle.ethPriceUSD);
const price1USD = token1.derivedETH.times(bundle.ethPriceUSD);

View File

@ -4,18 +4,20 @@ import { Pool } from '../entity/Pool';
import { Database } from '../database';
import { bigDecimalExponated, safeDiv } from '.';
import { Tick } from '../entity/Tick';
import { Block } from '../events';
export const createTick = async (db: Database, tickId: string, tickIdx: bigint, pool: Pool, block: Block): Promise<Tick> => {
const tick = new Tick();
tick.id = tickId;
tick.tickIdx = tickIdx;
tick.pool = pool;
tick.poolAddress = pool.id;
export const loadTick = async (db: Database, tickId: string, tickIdx: bigint, pool: Pool, blockNumber: number): Promise<Tick> => {
// 1.0001^tick is token1/token0.
const price0 = bigDecimalExponated(new Decimal('1.0001'), tickIdx);
return db.loadTick({
id: tickId,
blockNumber,
tickIdx: tickIdx,
pool,
poolAddress: pool.id,
price0,
price1: safeDiv(new Decimal(1), price0)
});
tick.price0 = price0;
tick.price1 = safeDiv(new Decimal(1), price0);
return db.saveTick(tick, block);
};