mirror of
https://github.com/cerc-io/watcher-ts
synced 2024-11-19 12:26:19 +00:00
Handle mint event (#130)
* Handle mint event and update Factory fields. * Integrate decimal.js with typeorm. * Update transaction in Mint event. * Update day and hour data. Co-authored-by: nabarun <nabarun@deepstacksoft.com>
This commit is contained in:
parent
d71557e963
commit
3a6af9f9cc
@ -11,6 +11,7 @@
|
|||||||
"@vulcanize/util": "^0.1.0",
|
"@vulcanize/util": "^0.1.0",
|
||||||
"apollo-server-express": "^2.25.0",
|
"apollo-server-express": "^2.25.0",
|
||||||
"apollo-type-bigint": "^0.1.3",
|
"apollo-type-bigint": "^0.1.3",
|
||||||
|
"decimal.js": "^10.3.1",
|
||||||
"typeorm": "^0.2.32"
|
"typeorm": "^0.2.32"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -10,6 +10,12 @@ import { Token } from './entity/Token';
|
|||||||
import { Bundle } from './entity/Bundle';
|
import { Bundle } from './entity/Bundle';
|
||||||
import { PoolDayData } from './entity/PoolDayData';
|
import { PoolDayData } from './entity/PoolDayData';
|
||||||
import { PoolHourData } from './entity/PoolHourData';
|
import { PoolHourData } from './entity/PoolHourData';
|
||||||
|
import { Transaction } from './entity/Transaction';
|
||||||
|
import { Mint } from './entity/Mint';
|
||||||
|
import { UniswapDayData } from './entity/UniswapDayData';
|
||||||
|
import { Tick } from './entity/Tick';
|
||||||
|
import { TokenDayData } from './entity/TokenDayData';
|
||||||
|
import { TokenHourData } from './entity/TokenHourData';
|
||||||
|
|
||||||
export class Database {
|
export class Database {
|
||||||
_config: ConnectionOptions
|
_config: ConnectionOptions
|
||||||
@ -72,6 +78,27 @@ export class Database {
|
|||||||
return repo.findOne(findOptions);
|
return repo.findOne(findOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getFactories ({ blockNumber }: DeepPartial<Factory>, queryOptions: { [key: string]: any }): Promise<Array<Factory>> {
|
||||||
|
const repo = this._conn.getRepository(Factory);
|
||||||
|
|
||||||
|
let selectQueryBuilder = repo.createQueryBuilder('factory')
|
||||||
|
.distinctOn(['id'])
|
||||||
|
.orderBy('id')
|
||||||
|
.addOrderBy('block_number', 'DESC');
|
||||||
|
|
||||||
|
if (blockNumber) {
|
||||||
|
selectQueryBuilder = selectQueryBuilder.where('block_number <= :blockNumber', { blockNumber });
|
||||||
|
}
|
||||||
|
|
||||||
|
const { limit } = queryOptions;
|
||||||
|
|
||||||
|
if (limit) {
|
||||||
|
selectQueryBuilder = selectQueryBuilder.limit(limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
return selectQueryBuilder.getMany();
|
||||||
|
}
|
||||||
|
|
||||||
async loadFactory ({ id, blockNumber, ...values }: DeepPartial<Factory>): Promise<Factory> {
|
async loadFactory ({ id, blockNumber, ...values }: DeepPartial<Factory>): Promise<Factory> {
|
||||||
return this._conn.transaction(async (tx) => {
|
return this._conn.transaction(async (tx) => {
|
||||||
const repo = tx.getRepository(Factory);
|
const repo = tx.getRepository(Factory);
|
||||||
@ -83,7 +110,7 @@ export class Database {
|
|||||||
selectQueryBuilder = selectQueryBuilder.andWhere('block_number <= :blockNumber', { blockNumber });
|
selectQueryBuilder = selectQueryBuilder.andWhere('block_number <= :blockNumber', { blockNumber });
|
||||||
}
|
}
|
||||||
|
|
||||||
let entity = await selectQueryBuilder.orderBy('factory.block_number', 'DESC')
|
let entity = await selectQueryBuilder.orderBy('block_number', 'DESC')
|
||||||
.getOne();
|
.getOne();
|
||||||
|
|
||||||
if (!entity) {
|
if (!entity) {
|
||||||
@ -167,7 +194,7 @@ export class Database {
|
|||||||
selectQueryBuilder = selectQueryBuilder.andWhere('block_number <= :blockNumber', { blockNumber });
|
selectQueryBuilder = selectQueryBuilder.andWhere('block_number <= :blockNumber', { blockNumber });
|
||||||
}
|
}
|
||||||
|
|
||||||
let entity = await selectQueryBuilder.orderBy('bundle.block_number', 'DESC')
|
let entity = await selectQueryBuilder.orderBy('block_number', 'DESC')
|
||||||
.getOne();
|
.getOne();
|
||||||
|
|
||||||
if (!entity) {
|
if (!entity) {
|
||||||
@ -190,7 +217,7 @@ export class Database {
|
|||||||
selectQueryBuilder = selectQueryBuilder.andWhere('block_number <= :blockNumber', { blockNumber });
|
selectQueryBuilder = selectQueryBuilder.andWhere('block_number <= :blockNumber', { blockNumber });
|
||||||
}
|
}
|
||||||
|
|
||||||
let entity = await selectQueryBuilder.orderBy('pool_day_data.block_number', 'DESC')
|
let entity = await selectQueryBuilder.orderBy('block_number', 'DESC')
|
||||||
.getOne();
|
.getOne();
|
||||||
|
|
||||||
if (!entity) {
|
if (!entity) {
|
||||||
@ -213,7 +240,145 @@ export class Database {
|
|||||||
selectQueryBuilder = selectQueryBuilder.andWhere('block_number <= :blockNumber', { blockNumber });
|
selectQueryBuilder = selectQueryBuilder.andWhere('block_number <= :blockNumber', { blockNumber });
|
||||||
}
|
}
|
||||||
|
|
||||||
let entity = await selectQueryBuilder.orderBy('pool_hour_data.block_number', 'DESC')
|
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 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();
|
.getOne();
|
||||||
|
|
||||||
if (!entity) {
|
if (!entity) {
|
||||||
@ -273,6 +438,38 @@ export class Database {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async saveTransaction (transaction: Transaction, blockNumber: number): Promise<Transaction> {
|
||||||
|
return this._conn.transaction(async (tx) => {
|
||||||
|
const repo = tx.getRepository(Transaction);
|
||||||
|
transaction.blockNumber = blockNumber;
|
||||||
|
return repo.save(transaction);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveUniswapDayData (uniswapDayData: UniswapDayData, blockNumber: number): Promise<UniswapDayData> {
|
||||||
|
return this._conn.transaction(async (tx) => {
|
||||||
|
const repo = tx.getRepository(UniswapDayData);
|
||||||
|
uniswapDayData.blockNumber = blockNumber;
|
||||||
|
return repo.save(uniswapDayData);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveTokenDayData (tokenDayData: TokenDayData, blockNumber: number): Promise<TokenDayData> {
|
||||||
|
return this._conn.transaction(async (tx) => {
|
||||||
|
const repo = tx.getRepository(TokenDayData);
|
||||||
|
tokenDayData.blockNumber = blockNumber;
|
||||||
|
return repo.save(tokenDayData);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveTokenHourData (tokenHourData: TokenHourData, blockNumber: number): Promise<TokenHourData> {
|
||||||
|
return this._conn.transaction(async (tx) => {
|
||||||
|
const repo = tx.getRepository(TokenHourData);
|
||||||
|
tokenHourData.blockNumber = blockNumber;
|
||||||
|
return repo.save(tokenHourData);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Returns true if events have already been synced for the (block, token) combination.
|
// Returns true if events have already been synced for the (block, token) combination.
|
||||||
async didSyncEvents ({ blockHash, token }: { blockHash: string, token: string }): Promise<boolean> {
|
async didSyncEvents ({ blockHash, token }: { blockHash: string, token: string }): Promise<boolean> {
|
||||||
const numRows = await this._conn.getRepository(EventSyncProgress)
|
const numRows = await this._conn.getRepository(EventSyncProgress)
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import { Entity, PrimaryColumn, Column } from 'typeorm';
|
import { Entity, PrimaryColumn, Column } from 'typeorm';
|
||||||
|
import Decimal from 'decimal.js';
|
||||||
|
import { decimalTransformer } from '@vulcanize/util';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class Bundle {
|
export class Bundle {
|
||||||
@ -8,6 +10,6 @@ export class Bundle {
|
|||||||
@PrimaryColumn('integer')
|
@PrimaryColumn('integer')
|
||||||
blockNumber!: number;
|
blockNumber!: number;
|
||||||
|
|
||||||
@Column('numeric', { default: 0 })
|
@Column('numeric', { default: 0, transformer: decimalTransformer })
|
||||||
ethPriceUSD!: number
|
ethPriceUSD!: Decimal
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
import Decimal from 'decimal.js';
|
||||||
import { Entity, Column, PrimaryColumn } from 'typeorm';
|
import { Entity, Column, PrimaryColumn } from 'typeorm';
|
||||||
|
import { decimalTransformer } from '@vulcanize/util';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class Factory {
|
export class Factory {
|
||||||
@ -8,8 +10,17 @@ export class Factory {
|
|||||||
@PrimaryColumn('integer')
|
@PrimaryColumn('integer')
|
||||||
blockNumber!: number;
|
blockNumber!: number;
|
||||||
|
|
||||||
@Column('numeric', { default: BigInt(0) })
|
@Column('bigint', { default: BigInt(0) })
|
||||||
poolCount!: bigint;
|
poolCount!: bigint;
|
||||||
|
|
||||||
|
@Column('numeric', { default: 0, transformer: decimalTransformer })
|
||||||
|
totalValueLockedETH!: Decimal;
|
||||||
|
|
||||||
|
@Column('bigint', { default: BigInt(0) })
|
||||||
|
txCount!: bigint;
|
||||||
|
|
||||||
|
@Column('numeric', { default: 0, transformer: decimalTransformer })
|
||||||
|
totalValueLockedUSD!: Decimal;
|
||||||
|
|
||||||
// TODO: Add remaining fields when they are used.
|
// TODO: Add remaining fields when they are used.
|
||||||
}
|
}
|
||||||
|
59
packages/uni-info-watcher/src/entity/Mint.ts
Normal file
59
packages/uni-info-watcher/src/entity/Mint.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm';
|
||||||
|
import Decimal from 'decimal.js';
|
||||||
|
import { decimalTransformer } from '@vulcanize/util';
|
||||||
|
|
||||||
|
import { Transaction } from './Transaction';
|
||||||
|
import { Pool } from './Pool';
|
||||||
|
import { Token } from './Token';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class Mint {
|
||||||
|
@PrimaryColumn('varchar')
|
||||||
|
id!: string;
|
||||||
|
|
||||||
|
@PrimaryColumn('integer')
|
||||||
|
blockNumber!: number;
|
||||||
|
|
||||||
|
@ManyToOne(() => Transaction, transaction => transaction.mints)
|
||||||
|
transaction!: Transaction
|
||||||
|
|
||||||
|
@Column('bigint')
|
||||||
|
timestamp!: BigInt;
|
||||||
|
|
||||||
|
@ManyToOne(() => Pool)
|
||||||
|
pool!: Pool
|
||||||
|
|
||||||
|
@ManyToOne(() => Token)
|
||||||
|
token0!: Token
|
||||||
|
|
||||||
|
@ManyToOne(() => Token)
|
||||||
|
token1!: Token
|
||||||
|
|
||||||
|
@Column('varchar', { length: 42 })
|
||||||
|
owner!: string
|
||||||
|
|
||||||
|
@Column('varchar', { length: 42 })
|
||||||
|
sender!: string
|
||||||
|
|
||||||
|
// TODO: Assign origin with Transaction from address.
|
||||||
|
// @Column('varchar', { length: 42 })
|
||||||
|
// origin!: string
|
||||||
|
|
||||||
|
@Column('bigint')
|
||||||
|
amount!: bigint
|
||||||
|
|
||||||
|
@Column('numeric', { transformer: decimalTransformer })
|
||||||
|
amount0!: Decimal
|
||||||
|
|
||||||
|
@Column('numeric', { transformer: decimalTransformer })
|
||||||
|
amount1!: Decimal
|
||||||
|
|
||||||
|
@Column('numeric', { transformer: decimalTransformer })
|
||||||
|
amountUSD!: Decimal
|
||||||
|
|
||||||
|
@Column('bigint')
|
||||||
|
tickLower!: bigint
|
||||||
|
|
||||||
|
@Column('bigint')
|
||||||
|
tickUpper!: bigint
|
||||||
|
}
|
@ -1,4 +1,6 @@
|
|||||||
|
import Decimal from 'decimal.js';
|
||||||
import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm';
|
import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm';
|
||||||
|
import { decimalTransformer } from '@vulcanize/util';
|
||||||
|
|
||||||
import { Token } from './Token';
|
import { Token } from './Token';
|
||||||
|
|
||||||
@ -16,11 +18,11 @@ export class Pool {
|
|||||||
@ManyToOne(() => Token)
|
@ManyToOne(() => Token)
|
||||||
token1!: Token;
|
token1!: Token;
|
||||||
|
|
||||||
@Column('numeric', { default: 0 })
|
@Column('numeric', { default: 0, transformer: decimalTransformer })
|
||||||
token0Price!: number
|
token0Price!: Decimal
|
||||||
|
|
||||||
@Column('numeric', { default: 0 })
|
@Column('numeric', { default: 0, transformer: decimalTransformer })
|
||||||
token1Price!: number
|
token1Price!: Decimal
|
||||||
|
|
||||||
@Column('numeric')
|
@Column('numeric')
|
||||||
feeTier!: bigint
|
feeTier!: bigint
|
||||||
@ -28,8 +30,8 @@ export class Pool {
|
|||||||
@Column('numeric', { default: BigInt(0) })
|
@Column('numeric', { default: BigInt(0) })
|
||||||
sqrtPrice!: bigint
|
sqrtPrice!: bigint
|
||||||
|
|
||||||
@Column('numeric', { default: BigInt(0) })
|
@Column('bigint', { nullable: true })
|
||||||
tick!: bigint
|
tick!: bigint | null
|
||||||
|
|
||||||
@Column('numeric', { default: BigInt(0) })
|
@Column('numeric', { default: BigInt(0) })
|
||||||
liquidity!: bigint
|
liquidity!: bigint
|
||||||
@ -40,14 +42,20 @@ export class Pool {
|
|||||||
@Column('numeric', { default: BigInt(0) })
|
@Column('numeric', { default: BigInt(0) })
|
||||||
feeGrowthGlobal1X128!: bigint
|
feeGrowthGlobal1X128!: bigint
|
||||||
|
|
||||||
@Column('numeric', { default: 0 })
|
@Column('numeric', { default: 0, transformer: decimalTransformer })
|
||||||
totalValueLockedUSD!: number
|
totalValueLockedUSD!: Decimal
|
||||||
|
|
||||||
@Column('numeric', { default: 0 })
|
@Column('numeric', { default: 0, transformer: decimalTransformer })
|
||||||
totalValueLockedToken0!: number
|
totalValueLockedToken0!: Decimal
|
||||||
|
|
||||||
@Column('numeric', { default: 0 })
|
@Column('numeric', { default: 0, transformer: decimalTransformer })
|
||||||
totalValueLockedToken1!: number
|
totalValueLockedToken1!: Decimal
|
||||||
|
|
||||||
|
@Column('numeric', { default: 0, transformer: decimalTransformer })
|
||||||
|
totalValueLockedETH!: Decimal
|
||||||
|
|
||||||
|
@Column('bigint', { default: BigInt(0) })
|
||||||
|
txCount!: bigint;
|
||||||
|
|
||||||
// TODO: Add remaining fields when they are used.
|
// TODO: Add remaining fields when they are used.
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
|
import Decimal from 'decimal.js';
|
||||||
import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm';
|
import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm';
|
||||||
|
import { decimalTransformer } from '@vulcanize/util';
|
||||||
|
|
||||||
import { Pool } from './Pool';
|
import { Pool } from './Pool';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
@ -15,23 +18,23 @@ export class PoolDayData {
|
|||||||
@ManyToOne(() => Pool)
|
@ManyToOne(() => Pool)
|
||||||
pool!: Pool;
|
pool!: Pool;
|
||||||
|
|
||||||
@Column('numeric')
|
@Column('numeric', { transformer: decimalTransformer })
|
||||||
high!: number;
|
high!: Decimal;
|
||||||
|
|
||||||
@Column('numeric')
|
@Column('numeric', { transformer: decimalTransformer })
|
||||||
low!: number;
|
low!: Decimal;
|
||||||
|
|
||||||
@Column('numeric')
|
@Column('numeric', { transformer: decimalTransformer })
|
||||||
open!: number;
|
open!: Decimal;
|
||||||
|
|
||||||
@Column('numeric')
|
@Column('numeric', { transformer: decimalTransformer })
|
||||||
close!: number;
|
close!: Decimal;
|
||||||
|
|
||||||
@Column('numeric', { default: BigInt(0) })
|
@Column('numeric', { default: BigInt(0) })
|
||||||
sqrtPrice!: bigint
|
sqrtPrice!: bigint
|
||||||
|
|
||||||
@Column('numeric', { default: BigInt(0) })
|
@Column('bigint', { nullable: true })
|
||||||
tick!: bigint
|
tick!: bigint | null
|
||||||
|
|
||||||
@Column('numeric', { default: BigInt(0) })
|
@Column('numeric', { default: BigInt(0) })
|
||||||
liquidity!: bigint
|
liquidity!: bigint
|
||||||
@ -42,14 +45,14 @@ export class PoolDayData {
|
|||||||
@Column('numeric', { default: BigInt(0) })
|
@Column('numeric', { default: BigInt(0) })
|
||||||
feeGrowthGlobal1X128!: bigint
|
feeGrowthGlobal1X128!: bigint
|
||||||
|
|
||||||
@Column('numeric', { default: 0 })
|
@Column('numeric', { default: 0, transformer: decimalTransformer })
|
||||||
token0Price!: number
|
token0Price!: Decimal
|
||||||
|
|
||||||
@Column('numeric', { default: 0 })
|
@Column('numeric', { default: 0, transformer: decimalTransformer })
|
||||||
token1Price!: number
|
token1Price!: Decimal
|
||||||
|
|
||||||
@Column('numeric', { default: 0 })
|
@Column('numeric', { default: 0, transformer: decimalTransformer })
|
||||||
tvlUSD!: number
|
tvlUSD!: Decimal
|
||||||
|
|
||||||
@Column('numeric', { default: BigInt(0) })
|
@Column('numeric', { default: BigInt(0) })
|
||||||
txCount!: bigint
|
txCount!: bigint
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
|
import Decimal from 'decimal.js';
|
||||||
import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm';
|
import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm';
|
||||||
|
import { decimalTransformer } from '@vulcanize/util';
|
||||||
|
|
||||||
import { Pool } from './Pool';
|
import { Pool } from './Pool';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
@ -15,23 +18,23 @@ export class PoolHourData {
|
|||||||
@ManyToOne(() => Pool)
|
@ManyToOne(() => Pool)
|
||||||
pool!: Pool;
|
pool!: Pool;
|
||||||
|
|
||||||
@Column('numeric')
|
@Column('numeric', { transformer: decimalTransformer })
|
||||||
high!: number;
|
high!: Decimal;
|
||||||
|
|
||||||
@Column('numeric')
|
@Column('numeric', { transformer: decimalTransformer })
|
||||||
low!: number;
|
low!: Decimal;
|
||||||
|
|
||||||
@Column('numeric')
|
@Column('numeric', { transformer: decimalTransformer })
|
||||||
open!: number;
|
open!: Decimal;
|
||||||
|
|
||||||
@Column('numeric')
|
@Column('numeric', { transformer: decimalTransformer })
|
||||||
close!: number;
|
close!: Decimal;
|
||||||
|
|
||||||
@Column('numeric', { default: BigInt(0) })
|
@Column('numeric', { default: BigInt(0) })
|
||||||
sqrtPrice!: bigint
|
sqrtPrice!: bigint
|
||||||
|
|
||||||
@Column('numeric', { default: BigInt(0) })
|
@Column('bigint', { nullable: true })
|
||||||
tick!: bigint
|
tick!: bigint | null
|
||||||
|
|
||||||
@Column('numeric', { default: BigInt(0) })
|
@Column('numeric', { default: BigInt(0) })
|
||||||
liquidity!: bigint
|
liquidity!: bigint
|
||||||
@ -42,14 +45,14 @@ export class PoolHourData {
|
|||||||
@Column('numeric', { default: BigInt(0) })
|
@Column('numeric', { default: BigInt(0) })
|
||||||
feeGrowthGlobal1X128!: bigint
|
feeGrowthGlobal1X128!: bigint
|
||||||
|
|
||||||
@Column('numeric', { default: 0 })
|
@Column('numeric', { default: 0, transformer: decimalTransformer })
|
||||||
token0Price!: number
|
token0Price!: Decimal
|
||||||
|
|
||||||
@Column('numeric', { default: 0 })
|
@Column('numeric', { default: 0, transformer: decimalTransformer })
|
||||||
token1Price!: number
|
token1Price!: Decimal
|
||||||
|
|
||||||
@Column('numeric', { default: 0 })
|
@Column('numeric', { default: 0, transformer: decimalTransformer })
|
||||||
tvlUSD!: number
|
tvlUSD!: Decimal
|
||||||
|
|
||||||
@Column('numeric', { default: BigInt(0) })
|
@Column('numeric', { default: BigInt(0) })
|
||||||
txCount!: bigint
|
txCount!: bigint
|
||||||
|
35
packages/uni-info-watcher/src/entity/Tick.ts
Normal file
35
packages/uni-info-watcher/src/entity/Tick.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm';
|
||||||
|
import Decimal from 'decimal.js';
|
||||||
|
import { decimalTransformer } from '@vulcanize/util';
|
||||||
|
|
||||||
|
import { Pool } from './Pool';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class Tick {
|
||||||
|
@PrimaryColumn('varchar')
|
||||||
|
id!: string;
|
||||||
|
|
||||||
|
@PrimaryColumn('integer')
|
||||||
|
blockNumber!: number;
|
||||||
|
|
||||||
|
@Column('bigint')
|
||||||
|
tickIdx!: BigInt;
|
||||||
|
|
||||||
|
@ManyToOne(() => Pool)
|
||||||
|
pool!: Pool
|
||||||
|
|
||||||
|
@Column('varchar', { length: 42 })
|
||||||
|
poolAddress!: string
|
||||||
|
|
||||||
|
@Column('numeric', { transformer: decimalTransformer })
|
||||||
|
price0!: Decimal
|
||||||
|
|
||||||
|
@Column('numeric', { transformer: decimalTransformer })
|
||||||
|
price1!: Decimal
|
||||||
|
|
||||||
|
@Column('bigint', { default: 0 })
|
||||||
|
liquidityGross!: bigint
|
||||||
|
|
||||||
|
@Column('bigint', { default: 0 })
|
||||||
|
liquidityNet!: bigint
|
||||||
|
}
|
@ -1,4 +1,7 @@
|
|||||||
|
import Decimal from 'decimal.js';
|
||||||
import { Entity, PrimaryColumn, Column, ManyToMany, JoinTable } from 'typeorm';
|
import { Entity, PrimaryColumn, Column, ManyToMany, JoinTable } from 'typeorm';
|
||||||
|
import { decimalTransformer } from '@vulcanize/util';
|
||||||
|
|
||||||
import { Pool } from './Pool';
|
import { Pool } from './Pool';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
@ -15,11 +18,24 @@ export class Token {
|
|||||||
@Column('varchar')
|
@Column('varchar')
|
||||||
name!: string;
|
name!: string;
|
||||||
|
|
||||||
@Column('numeric')
|
@Column('numeric', { transformer: decimalTransformer })
|
||||||
totalSupply!: number;
|
totalSupply!: Decimal;
|
||||||
|
|
||||||
@Column('numeric', { default: 0 })
|
// TODO: Fetch decimals from contract using erc20-watcher. Currently using hardcoded value.
|
||||||
derivedETH!: number;
|
@Column('bigint', { default: 18 })
|
||||||
|
decimals!: bigint;
|
||||||
|
|
||||||
|
@Column('numeric', { default: 0, transformer: decimalTransformer })
|
||||||
|
derivedETH!: Decimal;
|
||||||
|
|
||||||
|
@Column('bigint', { default: BigInt(0) })
|
||||||
|
txCount!: bigint;
|
||||||
|
|
||||||
|
@Column('numeric', { default: 0, transformer: decimalTransformer })
|
||||||
|
totalValueLocked!: Decimal;
|
||||||
|
|
||||||
|
@Column('numeric', { default: 0, transformer: decimalTransformer })
|
||||||
|
totalValueLockedUSD!: Decimal;
|
||||||
|
|
||||||
@ManyToMany(() => Pool)
|
@ManyToMany(() => Pool)
|
||||||
@JoinTable()
|
@JoinTable()
|
||||||
|
44
packages/uni-info-watcher/src/entity/TokenDayData.ts
Normal file
44
packages/uni-info-watcher/src/entity/TokenDayData.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm';
|
||||||
|
import Decimal from 'decimal.js';
|
||||||
|
import { decimalTransformer } from '@vulcanize/util';
|
||||||
|
|
||||||
|
import { Token } from './Token';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class TokenDayData {
|
||||||
|
@PrimaryColumn('varchar')
|
||||||
|
id!: string;
|
||||||
|
|
||||||
|
@PrimaryColumn('integer')
|
||||||
|
blockNumber!: number;
|
||||||
|
|
||||||
|
@Column('integer')
|
||||||
|
date!: number
|
||||||
|
|
||||||
|
@ManyToOne(() => Token)
|
||||||
|
token!: Token
|
||||||
|
|
||||||
|
@Column('numeric', { transformer: decimalTransformer })
|
||||||
|
high!: Decimal;
|
||||||
|
|
||||||
|
@Column('numeric', { transformer: decimalTransformer })
|
||||||
|
low!: Decimal;
|
||||||
|
|
||||||
|
@Column('numeric', { transformer: decimalTransformer })
|
||||||
|
open!: Decimal;
|
||||||
|
|
||||||
|
@Column('numeric', { transformer: decimalTransformer })
|
||||||
|
close!: Decimal;
|
||||||
|
|
||||||
|
@Column('numeric', { transformer: decimalTransformer })
|
||||||
|
priceUSD!: Decimal
|
||||||
|
|
||||||
|
@Column('numeric', { transformer: decimalTransformer })
|
||||||
|
totalValueLocked!: Decimal
|
||||||
|
|
||||||
|
@Column('numeric', { transformer: decimalTransformer })
|
||||||
|
totalValueLockedUSD!: Decimal
|
||||||
|
|
||||||
|
@Column('numeric', { default: 0, transformer: decimalTransformer })
|
||||||
|
volumeUSD!: Decimal
|
||||||
|
}
|
41
packages/uni-info-watcher/src/entity/TokenHourData.ts
Normal file
41
packages/uni-info-watcher/src/entity/TokenHourData.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm';
|
||||||
|
import Decimal from 'decimal.js';
|
||||||
|
import { decimalTransformer } from '@vulcanize/util';
|
||||||
|
|
||||||
|
import { Token } from './Token';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class TokenHourData {
|
||||||
|
@PrimaryColumn('varchar')
|
||||||
|
id!: string;
|
||||||
|
|
||||||
|
@PrimaryColumn('integer')
|
||||||
|
blockNumber!: number;
|
||||||
|
|
||||||
|
@Column('integer')
|
||||||
|
periodStartUnix!: number
|
||||||
|
|
||||||
|
@ManyToOne(() => Token)
|
||||||
|
token!: Token
|
||||||
|
|
||||||
|
@Column('numeric', { transformer: decimalTransformer })
|
||||||
|
high!: Decimal;
|
||||||
|
|
||||||
|
@Column('numeric', { transformer: decimalTransformer })
|
||||||
|
low!: Decimal;
|
||||||
|
|
||||||
|
@Column('numeric', { transformer: decimalTransformer })
|
||||||
|
open!: Decimal;
|
||||||
|
|
||||||
|
@Column('numeric', { transformer: decimalTransformer })
|
||||||
|
close!: Decimal;
|
||||||
|
|
||||||
|
@Column('numeric', { transformer: decimalTransformer })
|
||||||
|
priceUSD!: Decimal
|
||||||
|
|
||||||
|
@Column('numeric', { transformer: decimalTransformer })
|
||||||
|
totalValueLocked!: Decimal
|
||||||
|
|
||||||
|
@Column('numeric', { transformer: decimalTransformer })
|
||||||
|
totalValueLockedUSD!: Decimal
|
||||||
|
}
|
26
packages/uni-info-watcher/src/entity/Transaction.ts
Normal file
26
packages/uni-info-watcher/src/entity/Transaction.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import Decimal from 'decimal.js';
|
||||||
|
import { Entity, PrimaryColumn, Column, OneToMany } from 'typeorm';
|
||||||
|
import { decimalTransformer } from '@vulcanize/util';
|
||||||
|
|
||||||
|
import { Mint } from './Mint';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class Transaction {
|
||||||
|
@PrimaryColumn('varchar')
|
||||||
|
id!: string;
|
||||||
|
|
||||||
|
@PrimaryColumn('integer')
|
||||||
|
blockNumber!: number;
|
||||||
|
|
||||||
|
@Column('numeric', { default: 0, transformer: decimalTransformer })
|
||||||
|
ethPriceUSD!: Decimal
|
||||||
|
|
||||||
|
@Column('bigint')
|
||||||
|
timestamp!: BigInt;
|
||||||
|
|
||||||
|
@OneToMany(() => Mint, mint => mint.transaction)
|
||||||
|
mints!: Mint[];
|
||||||
|
|
||||||
|
// burns: [Burn]!
|
||||||
|
// swaps: [Swap]!
|
||||||
|
}
|
24
packages/uni-info-watcher/src/entity/UniswapDayData.ts
Normal file
24
packages/uni-info-watcher/src/entity/UniswapDayData.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { Entity, PrimaryColumn, Column } from 'typeorm';
|
||||||
|
import Decimal from 'decimal.js';
|
||||||
|
import { decimalTransformer } from '@vulcanize/util';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class UniswapDayData {
|
||||||
|
@PrimaryColumn('varchar')
|
||||||
|
id!: string;
|
||||||
|
|
||||||
|
@PrimaryColumn('integer')
|
||||||
|
blockNumber!: number;
|
||||||
|
|
||||||
|
@Column('integer')
|
||||||
|
date!: number
|
||||||
|
|
||||||
|
@Column('numeric', { transformer: decimalTransformer })
|
||||||
|
tvlUSD!: Decimal
|
||||||
|
|
||||||
|
@Column('numeric', { default: 0, transformer: decimalTransformer })
|
||||||
|
volumeUSD!: Decimal
|
||||||
|
|
||||||
|
@Column('bigint')
|
||||||
|
txCount!: bigint;
|
||||||
|
}
|
@ -6,8 +6,10 @@ import { BigNumber } from 'ethers';
|
|||||||
|
|
||||||
import { Database } from './database';
|
import { Database } from './database';
|
||||||
import { findEthPerToken, getEthPriceInUSD, WHITELIST_TOKENS } from './utils/pricing';
|
import { findEthPerToken, getEthPriceInUSD, WHITELIST_TOKENS } from './utils/pricing';
|
||||||
import { updatePoolDayData, updatePoolHourData } from './utils/intervalUpdates';
|
import { updatePoolDayData, updatePoolHourData, updateTokenDayData, updateTokenHourData, updateUniswapDayData } from './utils/interval-updates';
|
||||||
import { Token } from './entity/Token';
|
import { Token } from './entity/Token';
|
||||||
|
import { convertTokenToDecimal, loadTransaction } from './utils';
|
||||||
|
import { loadTick } from './utils/tick';
|
||||||
|
|
||||||
const log = debug('vulcanize:events');
|
const log = debug('vulcanize:events');
|
||||||
|
|
||||||
@ -24,6 +26,16 @@ interface InitializeEvent {
|
|||||||
tick: bigint;
|
tick: bigint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface MintEvent {
|
||||||
|
sender: string;
|
||||||
|
owner: string;
|
||||||
|
tickLower: bigint;
|
||||||
|
tickUpper: bigint;
|
||||||
|
amount: bigint;
|
||||||
|
amount0: bigint;
|
||||||
|
amount1: bigint;
|
||||||
|
}
|
||||||
|
|
||||||
interface ResultEvent {
|
interface ResultEvent {
|
||||||
proof: {
|
proof: {
|
||||||
data: string
|
data: string
|
||||||
@ -61,19 +73,24 @@ export class EventWatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async _handleEvents ({ blockHash, blockNumber, contract, event }: { blockHash: string, blockNumber: number, contract: string, event: ResultEvent}): Promise<void> {
|
async _handleEvents ({ blockHash, blockNumber, contract, txHash, event }: { blockHash: string, blockNumber: number, contract: string, txHash: string, event: ResultEvent}): Promise<void> {
|
||||||
// TODO: Process proof (proof.data) in event.
|
// TODO: Process proof (proof.data) in event.
|
||||||
const { event: { __typename: eventType, ...eventValues } } = event;
|
const { event: { __typename: eventType, ...eventValues } } = event;
|
||||||
|
|
||||||
switch (eventType) {
|
switch (eventType) {
|
||||||
case 'PoolCreatedEvent':
|
case 'PoolCreatedEvent':
|
||||||
log('Factory PoolCreated event', contract);
|
log('Factory PoolCreated event', contract);
|
||||||
this._handlePoolCreated(blockHash, blockNumber, contract, eventValues as PoolCreatedEvent);
|
this._handlePoolCreated(blockHash, blockNumber, contract, txHash, eventValues as PoolCreatedEvent);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'InitializeEvent':
|
case 'InitializeEvent':
|
||||||
log('Pool Initialize event', contract);
|
log('Pool Initialize event', contract);
|
||||||
this._handleInitialize(blockHash, blockNumber, contract, eventValues as InitializeEvent);
|
this._handleInitialize(blockHash, blockNumber, contract, txHash, eventValues as InitializeEvent);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'MintEvent':
|
||||||
|
log('Pool Mint event', contract);
|
||||||
|
this._handleMint(blockHash, blockNumber, contract, txHash, eventValues as MintEvent);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -81,7 +98,7 @@ export class EventWatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async _handlePoolCreated (blockHash: string, blockNumber: number, contractAddress: string, poolCreatedEvent: PoolCreatedEvent): Promise<void> {
|
async _handlePoolCreated (blockHash: string, blockNumber: number, contractAddress: string, txHash: string, poolCreatedEvent: PoolCreatedEvent): Promise<void> {
|
||||||
const { token0: token0Address, token1: token1Address, fee, pool: poolAddress } = poolCreatedEvent;
|
const { token0: token0Address, token1: token1Address, fee, pool: poolAddress } = poolCreatedEvent;
|
||||||
|
|
||||||
// Load factory.
|
// Load factory.
|
||||||
@ -153,7 +170,7 @@ export class EventWatcher {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async _handleInitialize (blockHash: string, blockNumber: number, contractAddress: string, initializeEvent: InitializeEvent): Promise<void> {
|
async _handleInitialize (blockHash: string, blockNumber: number, contractAddress: string, txHash: string, initializeEvent: InitializeEvent): Promise<void> {
|
||||||
const { sqrtPriceX96, tick } = initializeEvent;
|
const { sqrtPriceX96, tick } = initializeEvent;
|
||||||
const pool = await this._db.getPool({ id: contractAddress, blockNumber });
|
const pool = await this._db.getPool({ id: contractAddress, blockNumber });
|
||||||
assert(pool, `Pool ${contractAddress} not found.`);
|
assert(pool, `Pool ${contractAddress} not found.`);
|
||||||
@ -187,4 +204,126 @@ export class EventWatcher {
|
|||||||
this._db.saveToken(token1, blockNumber)
|
this._db.saveToken(token1, blockNumber)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _handleMint (blockHash: string, blockNumber: number, contractAddress: string, txHash: string, mintEvent: MintEvent): Promise<void> {
|
||||||
|
const bundle = await this._db.loadBundle({ id: '1', blockNumber });
|
||||||
|
const poolAddress = contractAddress;
|
||||||
|
const pool = await this._db.loadPool({ id: poolAddress, blockNumber });
|
||||||
|
|
||||||
|
// 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 token0 = pool.token0;
|
||||||
|
const token1 = pool.token1;
|
||||||
|
const amount0 = convertTokenToDecimal(mintEvent.amount0, BigInt(token0.decimals));
|
||||||
|
const amount1 = convertTokenToDecimal(mintEvent.amount1, BigInt(token1.decimals));
|
||||||
|
|
||||||
|
const amountUSD = amount0
|
||||||
|
.times(token0.derivedETH.times(bundle.ethPriceUSD))
|
||||||
|
.plus(amount1.times(token1.derivedETH.times(bundle.ethPriceUSD)));
|
||||||
|
|
||||||
|
// Reset tvl aggregates until new amounts calculated.
|
||||||
|
factory.totalValueLockedETH = factory.totalValueLockedETH.minus(pool.totalValueLockedETH);
|
||||||
|
|
||||||
|
// Update globals.
|
||||||
|
factory.txCount = BigInt(factory.txCount) + BigInt(1);
|
||||||
|
|
||||||
|
// Update token0 data.
|
||||||
|
token0.txCount = BigInt(token0.txCount) + BigInt(1);
|
||||||
|
token0.totalValueLocked = token0.totalValueLocked.plus(amount0);
|
||||||
|
token0.totalValueLockedUSD = token0.totalValueLocked.times(token0.derivedETH.times(bundle.ethPriceUSD));
|
||||||
|
|
||||||
|
// Update token1 data.
|
||||||
|
token1.txCount = BigInt(token1.txCount) + BigInt(1);
|
||||||
|
token1.totalValueLocked = token1.totalValueLocked.plus(amount1);
|
||||||
|
token1.totalValueLockedUSD = token1.totalValueLocked.times(token1.derivedETH.times(bundle.ethPriceUSD));
|
||||||
|
|
||||||
|
// Pool data.
|
||||||
|
pool.txCount = BigInt(pool.txCount) + BigInt(1);
|
||||||
|
|
||||||
|
// Pools liquidity tracks the currently active liquidity given pools current tick.
|
||||||
|
// We only want to update it on mint if the new position includes the current tick.
|
||||||
|
if (pool.tick !== null) {
|
||||||
|
if (
|
||||||
|
BigInt(mintEvent.tickLower) <= BigInt(pool.tick) &&
|
||||||
|
BigInt(mintEvent.tickUpper) > BigInt(pool.tick)
|
||||||
|
) {
|
||||||
|
pool.liquidity = BigInt(pool.liquidity) + mintEvent.amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pool.totalValueLockedToken0 = pool.totalValueLockedToken0.plus(amount0);
|
||||||
|
pool.totalValueLockedToken1 = pool.totalValueLockedToken1.plus(amount1);
|
||||||
|
|
||||||
|
pool.totalValueLockedETH = pool.totalValueLockedToken0.times(token0.derivedETH)
|
||||||
|
.plus(pool.totalValueLockedToken1.times(token1.derivedETH));
|
||||||
|
|
||||||
|
pool.totalValueLockedUSD = pool.totalValueLockedETH.times(bundle.ethPriceUSD);
|
||||||
|
|
||||||
|
// Reset aggregates with new amounts.
|
||||||
|
factory.totalValueLockedETH = factory.totalValueLockedETH.plus(pool.totalValueLockedETH);
|
||||||
|
factory.totalValueLockedUSD = factory.totalValueLockedETH.times(bundle.ethPriceUSD);
|
||||||
|
|
||||||
|
const transaction = await loadTransaction(this._db, { txHash, blockNumber });
|
||||||
|
|
||||||
|
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,
|
||||||
|
|
||||||
|
// 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
|
||||||
|
});
|
||||||
|
|
||||||
|
// Tick entities.
|
||||||
|
const lowerTickIdx = mintEvent.tickLower;
|
||||||
|
const upperTickIdx = mintEvent.tickUpper;
|
||||||
|
|
||||||
|
const lowerTickId = poolAddress + '#' + mintEvent.tickLower.toString();
|
||||||
|
const upperTickId = poolAddress + '#' + mintEvent.tickUpper.toString();
|
||||||
|
|
||||||
|
const lowerTick = await loadTick(this._db, lowerTickId, lowerTickIdx, pool, blockNumber);
|
||||||
|
const upperTick = await loadTick(this._db, upperTickId, upperTickIdx, pool, blockNumber);
|
||||||
|
|
||||||
|
const amount = mintEvent.amount;
|
||||||
|
lowerTick.liquidityGross = lowerTick.liquidityGross + amount;
|
||||||
|
lowerTick.liquidityNet = lowerTick.liquidityNet + amount;
|
||||||
|
upperTick.liquidityGross = upperTick.liquidityGross + amount;
|
||||||
|
upperTick.liquidityNet = upperTick.liquidityNet + amount;
|
||||||
|
|
||||||
|
// 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 });
|
||||||
|
await updatePoolDayData(this._db, { blockNumber, contractAddress });
|
||||||
|
await updatePoolHourData(this._db, { blockNumber, contractAddress });
|
||||||
|
await updateTokenDayData(this._db, token0, { blockNumber });
|
||||||
|
await updateTokenDayData(this._db, token1, { blockNumber });
|
||||||
|
await updateTokenHourData(this._db, token0, { blockNumber });
|
||||||
|
await updateTokenHourData(this._db, token1, { blockNumber });
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
this._db.saveToken(token0, blockNumber),
|
||||||
|
this._db.saveToken(token1, blockNumber)
|
||||||
|
]);
|
||||||
|
|
||||||
|
await this._db.savePool(pool, blockNumber);
|
||||||
|
await this._db.saveFactory(factory, blockNumber);
|
||||||
|
|
||||||
|
// Skipping update inner tick vars and tick day data as they are not queried.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
71
packages/uni-info-watcher/src/utils/index.ts
Normal file
71
packages/uni-info-watcher/src/utils/index.ts
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import Decimal from 'decimal.js';
|
||||||
|
import { BigNumber } from 'ethers';
|
||||||
|
|
||||||
|
import { Transaction } from '../entity/Transaction';
|
||||||
|
import { Database } from '../database';
|
||||||
|
|
||||||
|
export const exponentToBigDecimal = (decimals: bigint): Decimal => {
|
||||||
|
let bd = new Decimal(1);
|
||||||
|
|
||||||
|
for (let i = 0; BigNumber.from(decimals).gte(i); i++) {
|
||||||
|
bd = bd.times(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bd;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const convertTokenToDecimal = (tokenAmount: bigint, exchangeDecimals: bigint): Decimal => {
|
||||||
|
if (exchangeDecimals === BigInt(0)) {
|
||||||
|
return new Decimal(tokenAmount.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return (new Decimal(tokenAmount.toString())).div(exponentToBigDecimal(exchangeDecimals));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const loadTransaction = async (db: Database, event: { txHash: string, blockNumber: number }): Promise<Transaction> => {
|
||||||
|
const { txHash, blockNumber } = event;
|
||||||
|
|
||||||
|
// TODO: Get block timestamp from event.
|
||||||
|
// transaction.timestamp = event.block.timestamp
|
||||||
|
const timestamp = BigInt(Math.floor(Date.now() / 1000)); // Unix timestamp.
|
||||||
|
|
||||||
|
const transaction = await db.loadTransaction({
|
||||||
|
id: txHash,
|
||||||
|
blockNumber,
|
||||||
|
timestamp
|
||||||
|
});
|
||||||
|
|
||||||
|
transaction.blockNumber = blockNumber;
|
||||||
|
transaction.timestamp = timestamp;
|
||||||
|
|
||||||
|
return db.saveTransaction(transaction, blockNumber);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return 0 if denominator is 0 in division.
|
||||||
|
export const safeDiv = (amount0: Decimal, amount1: Decimal): Decimal => {
|
||||||
|
if (amount1.isZero()) {
|
||||||
|
return new Decimal(0);
|
||||||
|
} else {
|
||||||
|
return amount0.div(amount1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const bigDecimalExponated = (value: Decimal, power: bigint): Decimal => {
|
||||||
|
if (power === BigInt(0)) {
|
||||||
|
return new Decimal(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const negativePower = power > BigInt(0);
|
||||||
|
let result = (new Decimal(0)).plus(value);
|
||||||
|
const powerAbs = BigNumber.from(power).abs();
|
||||||
|
|
||||||
|
for (let i = BigNumber.from(1); i.lt(powerAbs); i = i.add(1)) {
|
||||||
|
result = result.times(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (negativePower) {
|
||||||
|
result = safeDiv(new Decimal(1), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
230
packages/uni-info-watcher/src/utils/interval-updates.ts
Normal file
230
packages/uni-info-watcher/src/utils/interval-updates.ts
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
import { BigNumber } from 'ethers';
|
||||||
|
|
||||||
|
import { Database } from '../database';
|
||||||
|
import { PoolDayData } from '../entity/PoolDayData';
|
||||||
|
import { PoolHourData } from '../entity/PoolHourData';
|
||||||
|
import { Token } from '../entity/Token';
|
||||||
|
import { TokenDayData } from '../entity/TokenDayData';
|
||||||
|
import { TokenHourData } from '../entity/TokenHourData';
|
||||||
|
import { UniswapDayData } from '../entity/UniswapDayData';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tracks global aggregate data over daily windows.
|
||||||
|
* @param db
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
export const updateUniswapDayData = async (db: Database, event: { contractAddress: string, blockNumber: number }): Promise<UniswapDayData> => {
|
||||||
|
const { blockNumber } = 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 });
|
||||||
|
|
||||||
|
// TODO: Get block timestamp from event.
|
||||||
|
// let timestamp = event.block.timestamp.toI32()
|
||||||
|
const timestamp = Math.floor(Date.now() / 1000); // Unix timestamp.
|
||||||
|
|
||||||
|
const dayID = Math.floor(timestamp / 86400); // Rounded.
|
||||||
|
const dayStartTimestamp = dayID * 86400;
|
||||||
|
|
||||||
|
const uniswapDayData = await db.loadUniswapDayData({
|
||||||
|
id: dayID.toString(),
|
||||||
|
blockNumber,
|
||||||
|
date: dayStartTimestamp,
|
||||||
|
tvlUSD: factory.totalValueLockedUSD,
|
||||||
|
txCount: factory.txCount
|
||||||
|
});
|
||||||
|
|
||||||
|
uniswapDayData.tvlUSD = factory.totalValueLockedUSD;
|
||||||
|
uniswapDayData.txCount = factory.txCount;
|
||||||
|
return db.saveUniswapDayData(uniswapDayData, blockNumber);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updatePoolDayData = async (db: Database, event: { contractAddress: string, blockNumber: number }): Promise<PoolDayData> => {
|
||||||
|
const { contractAddress, blockNumber } = event;
|
||||||
|
|
||||||
|
// TODO: Get block timestamp from event.
|
||||||
|
// let timestamp = event.block.timestamp.toI32()
|
||||||
|
const timestamp = Math.floor(Date.now() / 1000); // Unix timestamp.
|
||||||
|
|
||||||
|
const dayID = Math.floor(timestamp / 86400);
|
||||||
|
const dayStartTimestamp = dayID * 86400;
|
||||||
|
|
||||||
|
const dayPoolID = contractAddress
|
||||||
|
.concat('-')
|
||||||
|
.concat(dayID.toString());
|
||||||
|
|
||||||
|
const pool = await db.loadPool({ id: contractAddress, blockNumber });
|
||||||
|
|
||||||
|
let poolDayData = await db.loadPoolDayData({
|
||||||
|
id: dayPoolID,
|
||||||
|
blockNumber,
|
||||||
|
date: dayStartTimestamp,
|
||||||
|
pool: pool,
|
||||||
|
open: pool.token0Price,
|
||||||
|
high: pool.token0Price,
|
||||||
|
low: pool.token0Price,
|
||||||
|
close: pool.token0Price
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Number(pool.token0Price) > Number(poolDayData.high)) {
|
||||||
|
poolDayData.high = pool.token0Price;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Number(pool.token0Price) < Number(poolDayData.low)) {
|
||||||
|
poolDayData.low = pool.token0Price;
|
||||||
|
}
|
||||||
|
|
||||||
|
poolDayData.liquidity = pool.liquidity;
|
||||||
|
poolDayData.sqrtPrice = pool.sqrtPrice;
|
||||||
|
poolDayData.feeGrowthGlobal0X128 = pool.feeGrowthGlobal0X128;
|
||||||
|
poolDayData.feeGrowthGlobal1X128 = pool.feeGrowthGlobal1X128;
|
||||||
|
poolDayData.token0Price = pool.token0Price;
|
||||||
|
poolDayData.token1Price = pool.token1Price;
|
||||||
|
poolDayData.tick = pool.tick;
|
||||||
|
poolDayData.tvlUSD = pool.totalValueLockedUSD;
|
||||||
|
poolDayData.txCount = BigInt(BigNumber.from(poolDayData.txCount).add(1).toHexString());
|
||||||
|
poolDayData = await db.savePoolDayData(poolDayData, blockNumber);
|
||||||
|
|
||||||
|
return poolDayData;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updatePoolHourData = async (db: Database, event: { contractAddress: string, blockNumber: number }): Promise<PoolHourData> => {
|
||||||
|
const { contractAddress, blockNumber } = event;
|
||||||
|
|
||||||
|
// TODO: Get block timestamp from event.
|
||||||
|
// let timestamp = event.block.timestamp.toI32()
|
||||||
|
const timestamp = Math.floor(Date.now() / 1000); // Unix timestamp.
|
||||||
|
|
||||||
|
const hourIndex = Math.floor(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 });
|
||||||
|
|
||||||
|
let poolHourData = await db.loadPoolHourData({
|
||||||
|
id: hourPoolID,
|
||||||
|
blockNumber,
|
||||||
|
periodStartUnix: hourStartUnix,
|
||||||
|
pool: pool,
|
||||||
|
open: pool.token0Price,
|
||||||
|
high: pool.token0Price,
|
||||||
|
low: pool.token0Price,
|
||||||
|
close: pool.token0Price
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Number(pool.token0Price) > Number(poolHourData.high)) {
|
||||||
|
poolHourData.high = pool.token0Price;
|
||||||
|
}
|
||||||
|
if (Number(pool.token0Price) < Number(poolHourData.low)) {
|
||||||
|
poolHourData.low = pool.token0Price;
|
||||||
|
}
|
||||||
|
|
||||||
|
poolHourData.liquidity = pool.liquidity;
|
||||||
|
poolHourData.sqrtPrice = pool.sqrtPrice;
|
||||||
|
poolHourData.token0Price = pool.token0Price;
|
||||||
|
poolHourData.token1Price = pool.token1Price;
|
||||||
|
poolHourData.feeGrowthGlobal0X128 = pool.feeGrowthGlobal0X128;
|
||||||
|
poolHourData.feeGrowthGlobal1X128 = pool.feeGrowthGlobal1X128;
|
||||||
|
poolHourData.close = pool.token0Price;
|
||||||
|
poolHourData.tick = pool.tick;
|
||||||
|
poolHourData.tvlUSD = pool.totalValueLockedUSD;
|
||||||
|
poolHourData.txCount = BigInt(BigNumber.from(poolHourData.txCount).add(1).toHexString());
|
||||||
|
poolHourData = await db.savePoolHourData(poolHourData, blockNumber);
|
||||||
|
|
||||||
|
return poolHourData;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateTokenDayData = async (db: Database, token: Token, event: { blockNumber: number }): Promise<TokenDayData> => {
|
||||||
|
const { blockNumber } = event;
|
||||||
|
const bundle = await db.loadBundle({ id: '1', blockNumber });
|
||||||
|
|
||||||
|
// TODO: Get block timestamp from event.
|
||||||
|
// let timestamp = event.block.timestamp.toI32()
|
||||||
|
const timestamp = Math.floor(Date.now() / 1000); // Unix timestamp.
|
||||||
|
|
||||||
|
const dayID = Math.floor(timestamp / 86400);
|
||||||
|
const dayStartTimestamp = dayID * 86400;
|
||||||
|
|
||||||
|
const tokenDayID = token.id
|
||||||
|
.concat('-')
|
||||||
|
.concat(dayID.toString());
|
||||||
|
|
||||||
|
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
|
||||||
|
});
|
||||||
|
|
||||||
|
if (tokenPrice.gt(tokenDayData.high)) {
|
||||||
|
tokenDayData.high = tokenPrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokenPrice.lt(tokenDayData.low)) {
|
||||||
|
tokenDayData.low = tokenPrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenDayData.close = tokenPrice;
|
||||||
|
tokenDayData.priceUSD = token.derivedETH.times(bundle.ethPriceUSD);
|
||||||
|
tokenDayData.totalValueLocked = token.totalValueLocked;
|
||||||
|
tokenDayData.totalValueLockedUSD = token.totalValueLockedUSD;
|
||||||
|
return db.saveTokenDayData(tokenDayData, blockNumber);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateTokenHourData = async (db: Database, token: Token, event: { blockNumber: number }): Promise<TokenHourData> => {
|
||||||
|
const { blockNumber } = event;
|
||||||
|
const bundle = await db.loadBundle({ id: '1', blockNumber });
|
||||||
|
|
||||||
|
// TODO: Get block timestamp from event.
|
||||||
|
// let timestamp = event.block.timestamp.toI32()
|
||||||
|
const timestamp = Math.floor(Date.now() / 1000); // Unix timestamp.
|
||||||
|
|
||||||
|
const hourIndex = Math.floor(timestamp / 3600); // Get unique hour within unix history.
|
||||||
|
const hourStartUnix = hourIndex * 3600; // Want the rounded effect.
|
||||||
|
|
||||||
|
const tokenHourID = token.id
|
||||||
|
.concat('-')
|
||||||
|
.concat(hourIndex.toString());
|
||||||
|
|
||||||
|
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
|
||||||
|
});
|
||||||
|
|
||||||
|
if (tokenPrice.gt(tokenHourData.high)) {
|
||||||
|
tokenHourData.high = tokenPrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokenPrice.lt(tokenHourData.low)) {
|
||||||
|
tokenHourData.low = tokenPrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenHourData.close = tokenPrice;
|
||||||
|
tokenHourData.priceUSD = tokenPrice;
|
||||||
|
tokenHourData.totalValueLocked = token.totalValueLocked;
|
||||||
|
tokenHourData.totalValueLockedUSD = token.totalValueLockedUSD;
|
||||||
|
return db.saveTokenHourData(tokenHourData, blockNumber);
|
||||||
|
};
|
@ -1,103 +0,0 @@
|
|||||||
import { BigNumber } from 'ethers';
|
|
||||||
|
|
||||||
import { Database } from '../database';
|
|
||||||
import { PoolDayData } from '../entity/PoolDayData';
|
|
||||||
import { PoolHourData } from '../entity/PoolHourData';
|
|
||||||
|
|
||||||
export const updatePoolDayData = async (db: Database, event: { contractAddress: string, blockNumber: number }): Promise<PoolDayData> => {
|
|
||||||
const { contractAddress, blockNumber } = event;
|
|
||||||
|
|
||||||
// TODO: Get block timestamp from event.
|
|
||||||
// let timestamp = event.block.timestamp.toI32()
|
|
||||||
const timestamp = Math.floor(Date.now() / 1000); // Unix timestamp.
|
|
||||||
|
|
||||||
const dayID = Math.floor(timestamp / 86400);
|
|
||||||
const dayStartTimestamp = dayID * 86400;
|
|
||||||
|
|
||||||
const dayPoolID = contractAddress
|
|
||||||
.concat('-')
|
|
||||||
.concat(dayID.toString());
|
|
||||||
|
|
||||||
const pool = await db.loadPool({ id: contractAddress, blockNumber });
|
|
||||||
|
|
||||||
let poolDayData = await db.loadPoolDayData({
|
|
||||||
id: dayPoolID,
|
|
||||||
blockNumber,
|
|
||||||
date: dayStartTimestamp,
|
|
||||||
pool: pool,
|
|
||||||
open: pool.token0Price,
|
|
||||||
high: pool.token0Price,
|
|
||||||
low: pool.token0Price,
|
|
||||||
close: pool.token0Price
|
|
||||||
});
|
|
||||||
|
|
||||||
if (Number(pool.token0Price) > Number(poolDayData.high)) {
|
|
||||||
poolDayData.high = pool.token0Price;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Number(pool.token0Price) < Number(poolDayData.low)) {
|
|
||||||
poolDayData.low = pool.token0Price;
|
|
||||||
}
|
|
||||||
|
|
||||||
poolDayData.liquidity = pool.liquidity;
|
|
||||||
poolDayData.sqrtPrice = pool.sqrtPrice;
|
|
||||||
poolDayData.feeGrowthGlobal0X128 = pool.feeGrowthGlobal0X128;
|
|
||||||
poolDayData.feeGrowthGlobal1X128 = pool.feeGrowthGlobal1X128;
|
|
||||||
poolDayData.token0Price = pool.token0Price;
|
|
||||||
poolDayData.token1Price = pool.token1Price;
|
|
||||||
poolDayData.tick = pool.tick;
|
|
||||||
poolDayData.tvlUSD = pool.totalValueLockedUSD;
|
|
||||||
poolDayData.txCount = BigInt(BigNumber.from(poolDayData.txCount).add(1).toHexString());
|
|
||||||
poolDayData = await db.savePoolDayData(poolDayData, blockNumber);
|
|
||||||
|
|
||||||
return poolDayData;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const updatePoolHourData = async (db: Database, event: { contractAddress: string, blockNumber: number }): Promise<PoolHourData> => {
|
|
||||||
const { contractAddress, blockNumber } = event;
|
|
||||||
|
|
||||||
// TODO: Get block timestamp from event.
|
|
||||||
// let timestamp = event.block.timestamp.toI32()
|
|
||||||
const timestamp = Math.floor(Date.now() / 1000); // Unix timestamp.
|
|
||||||
|
|
||||||
const hourIndex = Math.floor(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 });
|
|
||||||
|
|
||||||
let poolHourData = await db.loadPoolHourData({
|
|
||||||
id: hourPoolID,
|
|
||||||
blockNumber,
|
|
||||||
periodStartUnix: hourStartUnix,
|
|
||||||
pool: pool,
|
|
||||||
open: pool.token0Price,
|
|
||||||
high: pool.token0Price,
|
|
||||||
low: pool.token0Price,
|
|
||||||
close: pool.token0Price
|
|
||||||
});
|
|
||||||
|
|
||||||
if (Number(pool.token0Price) > Number(poolHourData.high)) {
|
|
||||||
poolHourData.high = pool.token0Price;
|
|
||||||
}
|
|
||||||
if (Number(pool.token0Price) < Number(poolHourData.low)) {
|
|
||||||
poolHourData.low = pool.token0Price;
|
|
||||||
}
|
|
||||||
|
|
||||||
poolHourData.liquidity = pool.liquidity;
|
|
||||||
poolHourData.sqrtPrice = pool.sqrtPrice;
|
|
||||||
poolHourData.token0Price = pool.token0Price;
|
|
||||||
poolHourData.token1Price = pool.token1Price;
|
|
||||||
poolHourData.feeGrowthGlobal0X128 = pool.feeGrowthGlobal0X128;
|
|
||||||
poolHourData.feeGrowthGlobal1X128 = pool.feeGrowthGlobal1X128;
|
|
||||||
poolHourData.close = pool.token0Price;
|
|
||||||
poolHourData.tick = pool.tick;
|
|
||||||
poolHourData.tvlUSD = pool.totalValueLockedUSD;
|
|
||||||
poolHourData.txCount = BigInt(BigNumber.from(poolHourData.txCount).add(1).toHexString());
|
|
||||||
poolHourData = await db.savePoolHourData(poolHourData, blockNumber);
|
|
||||||
|
|
||||||
return poolHourData;
|
|
||||||
};
|
|
@ -1,3 +1,4 @@
|
|||||||
|
import Decimal from 'decimal.js';
|
||||||
import { BigNumber } from 'ethers';
|
import { BigNumber } from 'ethers';
|
||||||
|
|
||||||
import { Database } from '../database';
|
import { Database } from '../database';
|
||||||
@ -34,16 +35,16 @@ export const WHITELIST_TOKENS: string[] = [
|
|||||||
'0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9' // AAVE
|
'0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9' // AAVE
|
||||||
];
|
];
|
||||||
|
|
||||||
const MINIMUM_ETH_LOCKED = 52;
|
const MINIMUM_ETH_LOCKED = new Decimal(52);
|
||||||
|
|
||||||
export const getEthPriceInUSD = async (db: Database): Promise<number> => {
|
export const getEthPriceInUSD = async (db: Database): Promise<Decimal> => {
|
||||||
// Fetch eth prices for each stablecoin.
|
// Fetch eth prices for each stablecoin.
|
||||||
const usdcPool = await db.getPool({ id: USDC_WETH_03_POOL }); // DAI is token0.
|
const usdcPool = await db.getPool({ id: USDC_WETH_03_POOL }); // DAI is token0.
|
||||||
|
|
||||||
if (usdcPool) {
|
if (usdcPool) {
|
||||||
return usdcPool.token0Price;
|
return usdcPool.token0Price;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return new Decimal(0);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -51,16 +52,16 @@ export const getEthPriceInUSD = async (db: Database): Promise<number> => {
|
|||||||
* Search through graph to find derived Eth per token.
|
* Search through graph to find derived Eth per token.
|
||||||
* @todo update to be derived ETH (add stablecoin estimates)
|
* @todo update to be derived ETH (add stablecoin estimates)
|
||||||
**/
|
**/
|
||||||
export const findEthPerToken = async (token: Token): Promise<number> => {
|
export const findEthPerToken = async (token: Token): Promise<Decimal> => {
|
||||||
if (token.id === WETH_ADDRESS) {
|
if (token.id === WETH_ADDRESS) {
|
||||||
return 1;
|
return new Decimal(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const whiteList = token.whitelistPools;
|
const whiteList = token.whitelistPools;
|
||||||
// For now just take USD from pool with greatest TVL.
|
// For now just take USD from pool with greatest TVL.
|
||||||
// Need to update this to actually detect best rate based on liquidity distribution.
|
// Need to update this to actually detect best rate based on liquidity distribution.
|
||||||
let largestLiquidityETH = 0;
|
let largestLiquidityETH = new Decimal(0);
|
||||||
let priceSoFar = 0;
|
let priceSoFar = new Decimal(0);
|
||||||
|
|
||||||
for (let i = 0; i < whiteList.length; ++i) {
|
for (let i = 0; i < whiteList.length; ++i) {
|
||||||
const pool = whiteList[i];
|
const pool = whiteList[i];
|
||||||
@ -70,23 +71,23 @@ export const findEthPerToken = async (token: Token): Promise<number> => {
|
|||||||
// Whitelist token is token1.
|
// Whitelist token is token1.
|
||||||
const token1 = pool.token1;
|
const token1 = pool.token1;
|
||||||
// Get the derived ETH in pool.
|
// Get the derived ETH in pool.
|
||||||
const ethLocked = Number(pool.totalValueLockedToken1) * Number(token1.derivedETH);
|
const ethLocked = pool.totalValueLockedToken1.times(token1.derivedETH);
|
||||||
|
|
||||||
if (ethLocked > largestLiquidityETH && ethLocked > MINIMUM_ETH_LOCKED) {
|
if (ethLocked.gt(largestLiquidityETH) && ethLocked.gt(MINIMUM_ETH_LOCKED)) {
|
||||||
largestLiquidityETH = ethLocked;
|
largestLiquidityETH = ethLocked;
|
||||||
// token1 per our token * Eth per token1
|
// token1 per our token * Eth per token1
|
||||||
priceSoFar = Number(pool.token1Price) * Number(token1.derivedETH);
|
priceSoFar = pool.token1Price.times(token1.derivedETH);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (pool.token1.id === token.id) {
|
if (pool.token1.id === token.id) {
|
||||||
const token0 = pool.token0;
|
const token0 = pool.token0;
|
||||||
// Get the derived ETH in pool.
|
// Get the derived ETH in pool.
|
||||||
const ethLocked = Number(pool.totalValueLockedToken0) * Number(token0.derivedETH);
|
const ethLocked = pool.totalValueLockedToken0.times(token0.derivedETH);
|
||||||
|
|
||||||
if (ethLocked > largestLiquidityETH && ethLocked > MINIMUM_ETH_LOCKED) {
|
if (ethLocked.gt(largestLiquidityETH) && ethLocked.gt(MINIMUM_ETH_LOCKED)) {
|
||||||
largestLiquidityETH = ethLocked;
|
largestLiquidityETH = ethLocked;
|
||||||
// token0 per our token * ETH per token0
|
// token0 per our token * ETH per token0
|
||||||
priceSoFar = Number(pool.token0Price) * Number(token0.derivedETH);
|
priceSoFar = pool.token0Price.times(token0.derivedETH);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
21
packages/uni-info-watcher/src/utils/tick.ts
Normal file
21
packages/uni-info-watcher/src/utils/tick.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import Decimal from 'decimal.js';
|
||||||
|
|
||||||
|
import { Pool } from '../entity/Pool';
|
||||||
|
import { Database } from '../database';
|
||||||
|
import { bigDecimalExponated, safeDiv } from '.';
|
||||||
|
import { Tick } from '../entity/Tick';
|
||||||
|
|
||||||
|
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)
|
||||||
|
});
|
||||||
|
};
|
@ -19,6 +19,7 @@ export class Client {
|
|||||||
blockHash
|
blockHash
|
||||||
blockNumber
|
blockNumber
|
||||||
contract
|
contract
|
||||||
|
txHash
|
||||||
event {
|
event {
|
||||||
proof {
|
proof {
|
||||||
data
|
data
|
||||||
@ -38,6 +39,16 @@ export class Client {
|
|||||||
sqrtPriceX96
|
sqrtPriceX96
|
||||||
tick
|
tick
|
||||||
}
|
}
|
||||||
|
|
||||||
|
... on MintEvent {
|
||||||
|
sender
|
||||||
|
owner
|
||||||
|
tickLower
|
||||||
|
tickUpper
|
||||||
|
amount
|
||||||
|
amount0
|
||||||
|
amount1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1 +1,2 @@
|
|||||||
export * from './src/config';
|
export * from './src/config';
|
||||||
|
export * from './src/database';
|
||||||
|
19
packages/util/src/database.ts
Normal file
19
packages/util/src/database.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import Decimal from 'decimal.js';
|
||||||
|
import { ValueTransformer } from 'typeorm';
|
||||||
|
|
||||||
|
export const decimalTransformer: ValueTransformer = {
|
||||||
|
to: (value?: Decimal) => {
|
||||||
|
if (value) {
|
||||||
|
return value.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
from: (value?: string) => {
|
||||||
|
if (value) {
|
||||||
|
return new Decimal(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
};
|
@ -5021,6 +5021,11 @@ decamelize@^4.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837"
|
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837"
|
||||||
integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==
|
integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==
|
||||||
|
|
||||||
|
decimal.js@^10.3.1:
|
||||||
|
version "10.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783"
|
||||||
|
integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==
|
||||||
|
|
||||||
decode-uri-component@^0.2.0:
|
decode-uri-component@^0.2.0:
|
||||||
version "0.2.0"
|
version "0.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
|
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
|
||||||
|
Loading…
Reference in New Issue
Block a user