mirror of
https://github.com/cerc-io/watcher-ts
synced 2025-01-09 21:08:06 +00:00
Handle pool created event (#124)
* Update Token entities. * Complete handlePoolCreated in events. Co-authored-by: nabarun <nabarun@deepstacksoft.com>
This commit is contained in:
parent
194d079d5e
commit
f7df56bb75
@ -22,7 +22,7 @@
|
|||||||
subscribersDir = "src/subscriber"
|
subscribersDir = "src/subscriber"
|
||||||
|
|
||||||
[upstream]
|
[upstream]
|
||||||
gqlEndpoint = "http://127.0.0.1:8083/graphql"
|
gqlEndpoint = "http://127.0.0.1:8082/graphql"
|
||||||
gqlSubscriptionEndpoint = "http://127.0.0.1:5000/graphql"
|
gqlSubscriptionEndpoint = "http://127.0.0.1:5000/graphql"
|
||||||
|
|
||||||
[upstream.cache]
|
[upstream.cache]
|
||||||
|
@ -1,24 +1,19 @@
|
|||||||
import { gql } from 'apollo-server-express';
|
import { gql } from '@apollo/client/core';
|
||||||
import { GraphQLClient } from '@vulcanize/ipld-eth-client';
|
import { GraphQLClient, GraphQLConfig } from '@vulcanize/ipld-eth-client';
|
||||||
|
|
||||||
import { querySymbol } from './queries';
|
import { queryName, queryDecimals, queryTotalSupply, querySymbol } from './queries';
|
||||||
|
|
||||||
interface Config {
|
|
||||||
gqlEndpoint: string;
|
|
||||||
gqlSubscriptionEndpoint: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Client {
|
export class Client {
|
||||||
_config: Config;
|
_config: GraphQLConfig;
|
||||||
_client: GraphQLClient;
|
_client: GraphQLClient;
|
||||||
|
|
||||||
constructor (config: Config) {
|
constructor (config: GraphQLConfig) {
|
||||||
this._config = config;
|
this._config = config;
|
||||||
|
|
||||||
this._client = new GraphQLClient(config);
|
this._client = new GraphQLClient(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSymbol (blockHash: string | undefined, token: string): Promise<any> {
|
async getSymbol (blockHash: string, token: string): Promise<any> {
|
||||||
const { symbol } = await this._client.query(
|
const { symbol } = await this._client.query(
|
||||||
gql(querySymbol),
|
gql(querySymbol),
|
||||||
{ blockHash, token }
|
{ blockHash, token }
|
||||||
@ -26,4 +21,31 @@ export class Client {
|
|||||||
|
|
||||||
return symbol;
|
return symbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getName (blockHash: string, token: string): Promise<any> {
|
||||||
|
const { name } = await this._client.query(
|
||||||
|
gql(queryName),
|
||||||
|
{ blockHash, token }
|
||||||
|
);
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTotalSupply (blockHash: string, token: string): Promise<any> {
|
||||||
|
const { totalSupply } = await this._client.query(
|
||||||
|
gql(queryTotalSupply),
|
||||||
|
{ blockHash, token }
|
||||||
|
);
|
||||||
|
|
||||||
|
return totalSupply;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getDecimals (blockHash: string, token: string): Promise<any> {
|
||||||
|
const { decimals } = await this._client.query(
|
||||||
|
gql(queryDecimals),
|
||||||
|
{ blockHash, token }
|
||||||
|
);
|
||||||
|
|
||||||
|
return decimals;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,9 @@ import { Cache } from '@vulcanize/cache';
|
|||||||
|
|
||||||
import ethQueries from './eth-queries';
|
import ethQueries from './eth-queries';
|
||||||
import { padKey } from './utils';
|
import { padKey } from './utils';
|
||||||
import { GraphQLClient } from './graphql-client';
|
import { GraphQLClient, GraphQLConfig } from './graphql-client';
|
||||||
|
|
||||||
interface Config {
|
interface Config extends GraphQLConfig {
|
||||||
gqlEndpoint: string;
|
|
||||||
gqlSubscriptionEndpoint: string;
|
|
||||||
cache: Cache | undefined;
|
cache: Cache | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,16 +10,16 @@ import { WebSocketLink } from '@apollo/client/link/ws';
|
|||||||
|
|
||||||
const log = debug('vulcanize:client');
|
const log = debug('vulcanize:client');
|
||||||
|
|
||||||
interface Config {
|
export interface GraphQLConfig {
|
||||||
gqlEndpoint: string;
|
gqlEndpoint: string;
|
||||||
gqlSubscriptionEndpoint: string;
|
gqlSubscriptionEndpoint: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GraphQLClient {
|
export class GraphQLClient {
|
||||||
_config: Config;
|
_config: GraphQLConfig;
|
||||||
_client: ApolloClient<NormalizedCacheObject>;
|
_client: ApolloClient<NormalizedCacheObject>;
|
||||||
|
|
||||||
constructor (config: Config) {
|
constructor (config: GraphQLConfig) {
|
||||||
this._config = config;
|
this._config = config;
|
||||||
|
|
||||||
const { gqlEndpoint, gqlSubscriptionEndpoint } = config;
|
const { gqlEndpoint, gqlSubscriptionEndpoint } = config;
|
||||||
|
@ -30,7 +30,19 @@ export class Database {
|
|||||||
return this._conn.close();
|
return this._conn.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadFactory ({ id, blockNumber }: DeepPartial<Factory>): Promise<Factory> {
|
async getToken ({ id, blockNumber }: DeepPartial<Token>): Promise<Token | undefined> {
|
||||||
|
const repo = this._conn.getRepository(Token);
|
||||||
|
|
||||||
|
return repo.createQueryBuilder('token')
|
||||||
|
.where('id = :id AND block_number <= :blockNumber', {
|
||||||
|
id,
|
||||||
|
blockNumber
|
||||||
|
})
|
||||||
|
.orderBy('token.block_number', 'DESC')
|
||||||
|
.getOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
@ -39,10 +51,11 @@ export class Database {
|
|||||||
id,
|
id,
|
||||||
blockNumber
|
blockNumber
|
||||||
})
|
})
|
||||||
|
.orderBy('factory.block_number', 'DESC')
|
||||||
.getOne();
|
.getOne();
|
||||||
|
|
||||||
if (!entity) {
|
if (!entity) {
|
||||||
entity = repo.create({ blockNumber, id });
|
entity = repo.create({ blockNumber, id, ...values });
|
||||||
entity = await repo.save(entity);
|
entity = await repo.save(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,7 +63,7 @@ export class Database {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadPool ({ id, blockNumber }: DeepPartial<Pool>): Promise<Pool> {
|
async loadPool ({ id, blockNumber, ...values }: DeepPartial<Pool>): Promise<Pool> {
|
||||||
return this._conn.transaction(async (tx) => {
|
return this._conn.transaction(async (tx) => {
|
||||||
const repo = tx.getRepository(Pool);
|
const repo = tx.getRepository(Pool);
|
||||||
|
|
||||||
@ -59,10 +72,11 @@ export class Database {
|
|||||||
id,
|
id,
|
||||||
blockNumber
|
blockNumber
|
||||||
})
|
})
|
||||||
|
.orderBy('pool.block_number', 'DESC')
|
||||||
.getOne();
|
.getOne();
|
||||||
|
|
||||||
if (!entity) {
|
if (!entity) {
|
||||||
entity = repo.create({ blockNumber, id });
|
entity = repo.create({ blockNumber, id, ...values });
|
||||||
entity = await repo.save(entity);
|
entity = await repo.save(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +84,7 @@ export class Database {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadToken ({ id, blockNumber }: DeepPartial<Token>, getValues: () => Promise<DeepPartial<Token>>): Promise<Token> {
|
async loadToken ({ id, blockNumber, ...values }: DeepPartial<Token>): Promise<Token> {
|
||||||
return this._conn.transaction(async (tx) => {
|
return this._conn.transaction(async (tx) => {
|
||||||
const repo = tx.getRepository(Token);
|
const repo = tx.getRepository(Token);
|
||||||
|
|
||||||
@ -79,11 +93,11 @@ export class Database {
|
|||||||
id,
|
id,
|
||||||
blockNumber
|
blockNumber
|
||||||
})
|
})
|
||||||
|
.orderBy('token.block_number', 'DESC')
|
||||||
.getOne();
|
.getOne();
|
||||||
|
|
||||||
if (!entity) {
|
if (!entity) {
|
||||||
const tokenValues = await getValues();
|
entity = repo.create({ blockNumber, id, ...values });
|
||||||
entity = repo.create({ blockNumber, id, ...tokenValues });
|
|
||||||
entity = await repo.save(entity);
|
entity = await repo.save(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,6 +105,14 @@ export class Database {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async saveFactory (factory: Factory, blockNumber: number): Promise<Factory> {
|
||||||
|
return this._conn.transaction(async (tx) => {
|
||||||
|
const repo = tx.getRepository(Factory);
|
||||||
|
factory.blockNumber = blockNumber;
|
||||||
|
return repo.save(factory);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 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)
|
||||||
|
@ -6,9 +6,11 @@ export class Factory {
|
|||||||
@PrimaryColumn('varchar', { length: 42 })
|
@PrimaryColumn('varchar', { length: 42 })
|
||||||
id!: string;
|
id!: string;
|
||||||
|
|
||||||
@Column('numeric')
|
@PrimaryColumn('numeric')
|
||||||
blockNumber!: number;
|
blockNumber!: number;
|
||||||
|
|
||||||
@Column('numeric', { default: 0 })
|
@Column('numeric', { default: BigInt(0) })
|
||||||
poolCount!: number;
|
poolCount!: bigint;
|
||||||
|
|
||||||
|
// TODO: Add remaining fields when they are used.
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import { Entity, PrimaryColumn, Column, Index } from 'typeorm';
|
import { Entity, PrimaryColumn, Column, Index, ManyToOne } from 'typeorm';
|
||||||
|
|
||||||
|
import { Token } from './Token';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
@Index(['blockNumber', 'id'])
|
@Index(['blockNumber', 'id'])
|
||||||
@ -6,6 +8,17 @@ export class Pool {
|
|||||||
@PrimaryColumn('varchar', { length: 42 })
|
@PrimaryColumn('varchar', { length: 42 })
|
||||||
id!: string;
|
id!: string;
|
||||||
|
|
||||||
@Column('numeric')
|
@PrimaryColumn('numeric')
|
||||||
blockNumber!: number;
|
blockNumber!: number;
|
||||||
|
|
||||||
|
@ManyToOne(() => Token)
|
||||||
|
token0!: Token;
|
||||||
|
|
||||||
|
@ManyToOne(() => Token)
|
||||||
|
token1!: Token;
|
||||||
|
|
||||||
|
@Column('numeric')
|
||||||
|
feeTier!: bigint
|
||||||
|
|
||||||
|
// TODO: Add remaining fields when they are used.
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,17 @@ export class Token {
|
|||||||
@PrimaryColumn('varchar', { length: 42 })
|
@PrimaryColumn('varchar', { length: 42 })
|
||||||
id!: string;
|
id!: string;
|
||||||
|
|
||||||
@Column('numeric')
|
@PrimaryColumn('numeric')
|
||||||
blockNumber!: number;
|
blockNumber!: number;
|
||||||
|
|
||||||
@Column('varchar')
|
@Column('varchar')
|
||||||
symbol!: string;
|
symbol!: string;
|
||||||
|
|
||||||
|
@Column('varchar')
|
||||||
|
name!: string;
|
||||||
|
|
||||||
|
@Column('numeric')
|
||||||
|
totalSupply!: number;
|
||||||
|
|
||||||
|
// TODO: Add remaining fields when they are used.
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import assert from 'assert';
|
|||||||
import debug from 'debug';
|
import debug from 'debug';
|
||||||
import { Client as UniClient } from '@vulcanize/uni-watcher';
|
import { Client as UniClient } from '@vulcanize/uni-watcher';
|
||||||
import { Client as ERC20Client } from '@vulcanize/erc20-watcher';
|
import { Client as ERC20Client } from '@vulcanize/erc20-watcher';
|
||||||
|
import { BigNumber } from 'ethers';
|
||||||
|
|
||||||
import { Database } from './database';
|
import { Database } from './database';
|
||||||
|
|
||||||
@ -58,6 +59,7 @@ export class EventWatcher {
|
|||||||
|
|
||||||
switch (eventType) {
|
switch (eventType) {
|
||||||
case 'PoolCreatedEvent':
|
case 'PoolCreatedEvent':
|
||||||
|
log('PoolCreated event', contract);
|
||||||
this._handlePoolCreated(blockHash, blockNumber, contract, eventValues as PoolCreatedEvent);
|
this._handlePoolCreated(blockHash, blockNumber, contract, eventValues as PoolCreatedEvent);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -67,28 +69,62 @@ export class EventWatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async _handlePoolCreated (blockHash: string, blockNumber: number, contractAddress: string, poolCreatedEvent: PoolCreatedEvent): Promise<void> {
|
async _handlePoolCreated (blockHash: string, blockNumber: number, contractAddress: string, poolCreatedEvent: PoolCreatedEvent): Promise<void> {
|
||||||
const { token0: token0Address, token1: token1Address, fee, tickSpacing, pool: poolAddress } = poolCreatedEvent;
|
const { token0: token0Address, token1: token1Address, fee, pool: poolAddress } = poolCreatedEvent;
|
||||||
|
|
||||||
// Load factory.
|
// Load factory.
|
||||||
const factory = await this._db.loadFactory({ blockNumber, id: contractAddress });
|
const factory = await this._db.loadFactory({ blockNumber, id: contractAddress });
|
||||||
factory.poolCount = factory.poolCount + 1;
|
|
||||||
|
|
||||||
// Create new Pool entity.
|
// Update Factory.
|
||||||
const pool = this._db.loadPool({ blockNumber, id: poolAddress });
|
let factoryPoolCount = BigNumber.from(factory.poolCount);
|
||||||
|
factoryPoolCount = factoryPoolCount.add(1);
|
||||||
|
factory.poolCount = BigInt(factoryPoolCount.toHexString());
|
||||||
|
|
||||||
// TODO: Load Token entities.
|
// Get Tokens.
|
||||||
const getTokenValues = async (tokenAddress: string) => {
|
let [token0, token1] = await Promise.all([
|
||||||
|
this._db.getToken({ blockNumber, id: token0Address }),
|
||||||
|
this._db.getToken({ blockNumber, id: token1Address })
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Create Token.
|
||||||
|
const createToken = async (tokenAddress: string) => {
|
||||||
const { value: symbol } = await this._erc20Client.getSymbol(blockHash, tokenAddress);
|
const { value: symbol } = await this._erc20Client.getSymbol(blockHash, tokenAddress);
|
||||||
return { symbol };
|
const { value: name } = await this._erc20Client.getName(blockHash, tokenAddress);
|
||||||
|
const { value: totalSupply } = await this._erc20Client.getTotalSupply(blockHash, tokenAddress);
|
||||||
|
|
||||||
|
// TODO: decimals not implemented by erc20-watcher.
|
||||||
|
// const { value: decimals } = await this._erc20Client.getDecimals(blockHash, tokenAddress);
|
||||||
|
|
||||||
|
return this._db.loadToken({
|
||||||
|
blockNumber,
|
||||||
|
id: token1Address,
|
||||||
|
symbol,
|
||||||
|
name,
|
||||||
|
totalSupply
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const token0 = this._db.loadToken({ blockNumber, id: token0Address }, () => getTokenValues(token0Address));
|
// Create Tokens if not present.
|
||||||
const token1 = this._db.loadToken({ blockNumber, id: token1Address }, () => getTokenValues(token1Address));
|
if (!token0) {
|
||||||
|
token0 = await createToken(token0Address);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Update Token entities.
|
if (!token1) {
|
||||||
|
token1 = await createToken(token1Address);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Update Pool entity.
|
// Create new Pool entity.
|
||||||
|
// Skipping adding createdAtTimestamp field as it is not queried in frontend subgraph.
|
||||||
|
await this._db.loadPool({
|
||||||
|
blockNumber,
|
||||||
|
id: poolAddress,
|
||||||
|
token0: token0,
|
||||||
|
token1: token1,
|
||||||
|
feeTier: BigInt(fee)
|
||||||
|
});
|
||||||
|
|
||||||
// TODO: Save entities to DB.
|
// Skipping updating token whitelistPools field as it is not queried in frontend subgraph.
|
||||||
|
|
||||||
|
// Save entities to DB.
|
||||||
|
await this._db.saveFactory(factory, blockNumber);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,24 @@ Start watching the factory contract:
|
|||||||
Example:
|
Example:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ npx ts-node src/cli/watch-contract.ts --configFile environments/local.toml --address 0xfE0034a874c2707c23F91D7409E9036F5e08ac34 --kind factory --startingBlock 100
|
$ yarn watch:contract --address 0xfE0034a874c2707c23F91D7409E9036F5e08ac34 --kind factory --startingBlock 100
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Scripts
|
||||||
|
|
||||||
|
* `yarn server`
|
||||||
|
|
||||||
|
Start the GraphQL server.
|
||||||
|
|
||||||
|
* `yarn watch:contract`
|
||||||
|
|
||||||
|
Add contract to watch.
|
||||||
|
|
||||||
|
* `yarn lint`
|
||||||
|
|
||||||
|
Lint files.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Lint fix.
|
||||||
|
$ yarn lint --fix
|
||||||
|
```
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
"server:mock": "MOCK=1 nodemon src/server.ts -f environments/local.toml",
|
"server:mock": "MOCK=1 nodemon src/server.ts -f environments/local.toml",
|
||||||
"test": "mocha -r ts-node/register src/**/*.spec.ts",
|
"test": "mocha -r ts-node/register src/**/*.spec.ts",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"build": "tsc"
|
"build": "tsc",
|
||||||
|
"watch:contract": "ts-node src/cli/watch-contract.ts --configFile environments/local.toml"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
import { gql } from 'apollo-server-express';
|
import { gql } from '@apollo/client/core';
|
||||||
import { GraphQLClient } from '@vulcanize/ipld-eth-client';
|
import { GraphQLClient, GraphQLConfig } from '@vulcanize/ipld-eth-client';
|
||||||
|
|
||||||
interface Config {
|
|
||||||
gqlEndpoint: string;
|
|
||||||
gqlSubscriptionEndpoint: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Client {
|
export class Client {
|
||||||
_config: Config;
|
_config: GraphQLConfig;
|
||||||
_client: GraphQLClient;
|
_client: GraphQLClient;
|
||||||
|
|
||||||
constructor (config: Config) {
|
constructor (config: GraphQLConfig) {
|
||||||
this._config = config;
|
this._config = config;
|
||||||
|
|
||||||
this._client = new GraphQLClient(config);
|
this._client = new GraphQLClient(config);
|
||||||
|
Loading…
Reference in New Issue
Block a user