mirror of
https://github.com/cerc-io/watcher-ts
synced 2025-01-09 12:58:06 +00:00
Replace storage calls in uni-info-watcher with eth_calls (#281)
* Replace storage calls in uni-info-watcher with eth_calls * Handle eth_call reverts and catch them in uni-info-watcher
This commit is contained in:
parent
5cbcd455d2
commit
7953ba2949
@ -424,6 +424,20 @@ export class Database implements DatabaseInterface {
|
|||||||
return this._baseDatabase.getModelEntities(queryRunner, entity, block, where, queryOptions, relations);
|
return this._baseDatabase.getModelEntities(queryRunner, entity, block, where, queryOptions, relations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getModelEntitiesNoTx<Entity> (entity: new () => Entity, block: BlockHeight, where: Where = {}, queryOptions: QueryOptions = {}, relations: Relation[] = []): Promise<Entity[]> {
|
||||||
|
const queryRunner = this._conn.createQueryRunner();
|
||||||
|
let res;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await queryRunner.connect();
|
||||||
|
res = await this.getModelEntities(queryRunner, entity, block, where, queryOptions, relations);
|
||||||
|
} finally {
|
||||||
|
await queryRunner.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
async saveFactory (queryRunner: QueryRunner, factory: Factory, block: Block): Promise<Factory> {
|
async saveFactory (queryRunner: QueryRunner, factory: Factory, block: Block): Promise<Factory> {
|
||||||
const repo = queryRunner.manager.getRepository(Factory);
|
const repo = queryRunner.manager.getRepository(Factory);
|
||||||
factory.blockNumber = block.number;
|
factory.blockNumber = block.number;
|
||||||
|
@ -1231,16 +1231,26 @@ export class Indexer implements IndexerInterface {
|
|||||||
let position = await this._db.getPosition({ id: tokenId.toString(), blockHash });
|
let position = await this._db.getPosition({ id: tokenId.toString(), blockHash });
|
||||||
|
|
||||||
if (!position) {
|
if (!position) {
|
||||||
const nfpmPosition = await this._uniClient.getPosition(blockHash, tokenId);
|
let positionResult;
|
||||||
|
|
||||||
|
try {
|
||||||
|
({ value: positionResult } = await this._uniClient.positions(blockHash, contractAddress, tokenId));
|
||||||
|
} catch (error: any) {
|
||||||
// The contract call reverts in situations where the position is minted and deleted in the same block.
|
// The contract call reverts in situations where the position is minted and deleted in the same block.
|
||||||
// From my investigation this happens in calls from BancorSwap.
|
// From my investigation this happens in calls from BancorSwap.
|
||||||
// (e.g. 0xf7867fa19aa65298fadb8d4f72d0daed5e836f3ba01f0b9b9631cdc6c36bed40)
|
// (e.g. 0xf7867fa19aa65298fadb8d4f72d0daed5e836f3ba01f0b9b9631cdc6c36bed40)
|
||||||
|
|
||||||
if (nfpmPosition) {
|
if (error.message !== utils.Logger.errors.CALL_EXCEPTION) {
|
||||||
const { token0: token0Address, token1: token1Address, fee } = await this._uniClient.poolIdToPoolKey(blockHash, nfpmPosition.poolId);
|
log('nfpm positions eth_call failed');
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const { pool: poolAddress } = await this._uniClient.getPool(blockHash, token0Address, token1Address, fee);
|
if (positionResult) {
|
||||||
|
// 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.getModelEntitiesNoTx(Factory, { hash: blockHash }, {}, { limit: 1 });
|
||||||
|
const { value: poolAddress } = await this._uniClient.callGetPool(blockHash, factory.id, positionResult.token0, positionResult.token1, positionResult.fee);
|
||||||
|
|
||||||
position = new Position();
|
position = new Position();
|
||||||
position.id = tokenId.toString();
|
position.id = tokenId.toString();
|
||||||
@ -1250,23 +1260,23 @@ export class Indexer implements IndexerInterface {
|
|||||||
position.pool = pool;
|
position.pool = pool;
|
||||||
|
|
||||||
const [token0, token1] = await Promise.all([
|
const [token0, token1] = await Promise.all([
|
||||||
this._db.getTokenNoTx({ id: token0Address, blockHash }),
|
this._db.getTokenNoTx({ id: positionResult.token0, blockHash }),
|
||||||
this._db.getTokenNoTx({ id: token1Address, blockHash })
|
this._db.getTokenNoTx({ id: positionResult.token1, blockHash })
|
||||||
]);
|
]);
|
||||||
assert(token0 && token1);
|
assert(token0 && token1);
|
||||||
position.token0 = token0;
|
position.token0 = token0;
|
||||||
position.token1 = token1;
|
position.token1 = token1;
|
||||||
|
|
||||||
const [tickLower, tickUpper] = await Promise.all([
|
const [tickLower, tickUpper] = await Promise.all([
|
||||||
this._db.getTickNoTx({ id: poolAddress.concat('#').concat(nfpmPosition.tickLower.toString()), blockHash }),
|
this._db.getTickNoTx({ id: poolAddress.concat('#').concat(positionResult.tickLower.toString()), blockHash }),
|
||||||
this._db.getTickNoTx({ id: poolAddress.concat('#').concat(nfpmPosition.tickUpper.toString()), blockHash })
|
this._db.getTickNoTx({ id: poolAddress.concat('#').concat(positionResult.tickUpper.toString()), blockHash })
|
||||||
]);
|
]);
|
||||||
assert(tickLower && tickUpper);
|
assert(tickLower && tickUpper);
|
||||||
position.tickLower = tickLower;
|
position.tickLower = tickLower;
|
||||||
position.tickUpper = tickUpper;
|
position.tickUpper = tickUpper;
|
||||||
|
|
||||||
position.feeGrowthInside0LastX128 = BigInt(nfpmPosition.feeGrowthInside0LastX128.toString());
|
position.feeGrowthInside0LastX128 = BigInt(positionResult.feeGrowthInside0LastX128.toString());
|
||||||
position.feeGrowthInside1LastX128 = BigInt(nfpmPosition.feeGrowthInside1LastX128.toString());
|
position.feeGrowthInside1LastX128 = BigInt(positionResult.feeGrowthInside1LastX128.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1274,11 +1284,16 @@ export class Indexer implements IndexerInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async _updateFeeVars (position: Position, block: Block, contractAddress: string, tokenId: bigint): Promise<Position> {
|
async _updateFeeVars (position: Position, block: Block, contractAddress: string, tokenId: bigint): Promise<Position> {
|
||||||
const nfpmPosition = await this._uniClient.getPosition(block.hash, tokenId);
|
try {
|
||||||
|
const { value: positionResult } = await this._uniClient.positions(block.hash, contractAddress, tokenId);
|
||||||
|
|
||||||
if (nfpmPosition) {
|
if (positionResult) {
|
||||||
position.feeGrowthInside0LastX128 = BigInt(nfpmPosition.feeGrowthInside0LastX128.toString());
|
position.feeGrowthInside0LastX128 = BigInt(positionResult.feeGrowthInside0LastX128.toString());
|
||||||
position.feeGrowthInside1LastX128 = BigInt(nfpmPosition.feeGrowthInside1LastX128.toString());
|
position.feeGrowthInside1LastX128 = BigInt(positionResult.feeGrowthInside1LastX128.toString());
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
log('nfpm positions eth_call failed');
|
||||||
|
log(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
return position;
|
return position;
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
[upstream.ethServer]
|
[upstream.ethServer]
|
||||||
gqlApiEndpoint = "http://127.0.0.1:8082/graphql"
|
gqlApiEndpoint = "http://127.0.0.1:8082/graphql"
|
||||||
gqlPostgraphileEndpoint = "http://127.0.0.1:5000/graphql"
|
gqlPostgraphileEndpoint = "http://127.0.0.1:5000/graphql"
|
||||||
rpcProviderEndpoint = "http://127.0.0.1:8545"
|
rpcProviderEndpoint = "http://127.0.0.1:8081"
|
||||||
|
|
||||||
[upstream.cache]
|
[upstream.cache]
|
||||||
name = "requests"
|
name = "requests"
|
||||||
|
29
packages/uni-watcher/environments/test.toml
Normal file
29
packages/uni-watcher/environments/test.toml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
[server]
|
||||||
|
host = "127.0.0.1"
|
||||||
|
port = 3003
|
||||||
|
|
||||||
|
[database]
|
||||||
|
type = "postgres"
|
||||||
|
host = "localhost"
|
||||||
|
port = 5432
|
||||||
|
database = "uni-watcher"
|
||||||
|
username = "postgres"
|
||||||
|
password = "postgres"
|
||||||
|
synchronize = true
|
||||||
|
logging = false
|
||||||
|
|
||||||
|
[upstream]
|
||||||
|
[upstream.ethServer]
|
||||||
|
gqlApiEndpoint = "http://127.0.0.1:8082/graphql"
|
||||||
|
gqlPostgraphileEndpoint = "http://127.0.0.1:5000/graphql"
|
||||||
|
rpcProviderEndpoint = "http://127.0.0.1:8545"
|
||||||
|
|
||||||
|
[upstream.cache]
|
||||||
|
name = "requests"
|
||||||
|
enabled = false
|
||||||
|
deleteOnStart = false
|
||||||
|
|
||||||
|
[jobQueue]
|
||||||
|
dbConnectionString = "postgres://postgres:postgres@localhost/uni-watcher-job-queue"
|
||||||
|
maxCompletionLagInSecs = 300
|
||||||
|
jobDelayInMilliSecs = 100
|
@ -6,6 +6,7 @@ import { expect, assert } from 'chai';
|
|||||||
import { AssertionError } from 'assert';
|
import { AssertionError } from 'assert';
|
||||||
import 'mocha';
|
import 'mocha';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import { getDefaultProvider } from 'ethers';
|
||||||
|
|
||||||
import { getConfig, JobQueue, JobRunner, JOB_KIND_PRUNE } from '@vulcanize/util';
|
import { getConfig, JobQueue, JobRunner, JOB_KIND_PRUNE } from '@vulcanize/util';
|
||||||
import { getCache } from '@vulcanize/cache';
|
import { getCache } from '@vulcanize/cache';
|
||||||
@ -17,7 +18,7 @@ import { Database } from './database';
|
|||||||
import { BlockProgress } from './entity/BlockProgress';
|
import { BlockProgress } from './entity/BlockProgress';
|
||||||
import { SyncStatus } from './entity/SyncStatus';
|
import { SyncStatus } from './entity/SyncStatus';
|
||||||
|
|
||||||
const CONFIG_FILE = './environments/local.toml';
|
const CONFIG_FILE = './environments/test.toml';
|
||||||
|
|
||||||
describe('chain pruning', () => {
|
describe('chain pruning', () => {
|
||||||
let db: Database;
|
let db: Database;
|
||||||
@ -45,7 +46,7 @@ describe('chain pruning', () => {
|
|||||||
|
|
||||||
// Create an Indexer object.
|
// Create an Indexer object.
|
||||||
assert(upstream, 'Missing upstream config');
|
assert(upstream, 'Missing upstream config');
|
||||||
const { ethServer: { gqlApiEndpoint, gqlPostgraphileEndpoint }, cache: cacheConfig } = upstream;
|
const { ethServer: { gqlApiEndpoint, gqlPostgraphileEndpoint, rpcProviderEndpoint }, cache: cacheConfig } = upstream;
|
||||||
assert(gqlApiEndpoint, 'Missing upstream ethServer.gqlApiEndpoint');
|
assert(gqlApiEndpoint, 'Missing upstream ethServer.gqlApiEndpoint');
|
||||||
assert(gqlPostgraphileEndpoint, 'Missing upstream ethServer.gqlPostgraphileEndpoint');
|
assert(gqlPostgraphileEndpoint, 'Missing upstream ethServer.gqlPostgraphileEndpoint');
|
||||||
|
|
||||||
@ -61,7 +62,9 @@ describe('chain pruning', () => {
|
|||||||
cache
|
cache
|
||||||
});
|
});
|
||||||
|
|
||||||
indexer = new Indexer(db, ethClient, postgraphileClient);
|
const ethProvider = getDefaultProvider(rpcProviderEndpoint);
|
||||||
|
|
||||||
|
indexer = new Indexer(db, ethClient, postgraphileClient, ethProvider);
|
||||||
assert(indexer, 'Could not create indexer object.');
|
assert(indexer, 'Could not create indexer object.');
|
||||||
|
|
||||||
const { dbConnectionString, maxCompletionLagInSecs } = jobQueueConfig;
|
const { dbConnectionString, maxCompletionLagInSecs } = jobQueueConfig;
|
||||||
|
@ -27,13 +27,13 @@ export const builder = {
|
|||||||
export const handler = async (argv: any): Promise<void> => {
|
export const handler = async (argv: any): Promise<void> => {
|
||||||
const config = await getConfig(argv.configFile);
|
const config = await getConfig(argv.configFile);
|
||||||
await resetJobs(config);
|
await resetJobs(config);
|
||||||
const { dbConfig, ethClient, postgraphileClient } = await getResetConfig(config);
|
const { dbConfig, ethClient, postgraphileClient, ethProvider } = await getResetConfig(config);
|
||||||
|
|
||||||
// Initialize database.
|
// Initialize database.
|
||||||
const db = new Database(dbConfig);
|
const db = new Database(dbConfig);
|
||||||
await db.init();
|
await db.init();
|
||||||
|
|
||||||
const indexer = new Indexer(db, ethClient, postgraphileClient);
|
const indexer = new Indexer(db, ethClient, postgraphileClient, ethProvider);
|
||||||
|
|
||||||
const syncStatus = await indexer.getSyncStatus();
|
const syncStatus = await indexer.getSyncStatus();
|
||||||
assert(syncStatus, 'Missing syncStatus');
|
assert(syncStatus, 'Missing syncStatus');
|
||||||
|
@ -12,7 +12,9 @@ import {
|
|||||||
queryEvents,
|
queryEvents,
|
||||||
subscribeEvents,
|
subscribeEvents,
|
||||||
queryGetContract,
|
queryGetContract,
|
||||||
queryEventsInRange
|
queryEventsInRange,
|
||||||
|
queryCallGetPool,
|
||||||
|
queryPositions
|
||||||
} from './queries';
|
} from './queries';
|
||||||
|
|
||||||
export class Client {
|
export class Client {
|
||||||
@ -84,6 +86,34 @@ export class Client {
|
|||||||
return getPool;
|
return getPool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async callGetPool (blockHash: string, contractAddress: string, key0: string, key1: string, key2: number): Promise<any> {
|
||||||
|
const { callGetPool } = await this._client.query(
|
||||||
|
gql(queryCallGetPool),
|
||||||
|
{
|
||||||
|
blockHash,
|
||||||
|
contractAddress,
|
||||||
|
key0,
|
||||||
|
key1,
|
||||||
|
key2
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return callGetPool;
|
||||||
|
}
|
||||||
|
|
||||||
|
async positions (blockHash: string, contractAddress: string, tokenId: bigint): Promise<any> {
|
||||||
|
const { positions } = await this._client.query(
|
||||||
|
gql(queryPositions),
|
||||||
|
{
|
||||||
|
blockHash,
|
||||||
|
contractAddress,
|
||||||
|
tokenId: tokenId.toString()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return positions;
|
||||||
|
}
|
||||||
|
|
||||||
async getContract (type: string): Promise<any> {
|
async getContract (type: string): Promise<any> {
|
||||||
const { getContract } = await this._client.query(
|
const { getContract } = await this._client.query(
|
||||||
gql(queryGetContract),
|
gql(queryGetContract),
|
||||||
|
@ -8,6 +8,7 @@ import yargs from 'yargs';
|
|||||||
import { hideBin } from 'yargs/helpers';
|
import { hideBin } from 'yargs/helpers';
|
||||||
import debug from 'debug';
|
import debug from 'debug';
|
||||||
import { PubSub } from 'apollo-server-express';
|
import { PubSub } from 'apollo-server-express';
|
||||||
|
import { getDefaultProvider } from 'ethers';
|
||||||
|
|
||||||
import { getCache } from '@vulcanize/cache';
|
import { getCache } from '@vulcanize/cache';
|
||||||
import { EthClient } from '@vulcanize/ipld-eth-client';
|
import { EthClient } from '@vulcanize/ipld-eth-client';
|
||||||
@ -57,7 +58,7 @@ export const main = async (): Promise<any> => {
|
|||||||
await db.init();
|
await db.init();
|
||||||
|
|
||||||
assert(upstream, 'Missing upstream config');
|
assert(upstream, 'Missing upstream config');
|
||||||
const { ethServer: { gqlPostgraphileEndpoint }, cache: cacheConfig } = upstream;
|
const { ethServer: { gqlPostgraphileEndpoint, rpcProviderEndpoint }, cache: cacheConfig } = upstream;
|
||||||
assert(gqlPostgraphileEndpoint, 'Missing upstream ethServer.gqlPostgraphileEndpoint');
|
assert(gqlPostgraphileEndpoint, 'Missing upstream ethServer.gqlPostgraphileEndpoint');
|
||||||
|
|
||||||
const cache = await getCache(cacheConfig);
|
const cache = await getCache(cacheConfig);
|
||||||
@ -72,10 +73,12 @@ export const main = async (): Promise<any> => {
|
|||||||
cache
|
cache
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const ethProvider = getDefaultProvider(rpcProviderEndpoint);
|
||||||
|
|
||||||
// Note: In-memory pubsub works fine for now, as each watcher is a single process anyway.
|
// Note: In-memory pubsub works fine for now, as each watcher is a single process anyway.
|
||||||
// Later: https://www.apollographql.com/docs/apollo-server/data/subscriptions/#production-pubsub-libraries
|
// Later: https://www.apollographql.com/docs/apollo-server/data/subscriptions/#production-pubsub-libraries
|
||||||
const pubsub = new PubSub();
|
const pubsub = new PubSub();
|
||||||
const indexer = new Indexer(db, ethClient, postgraphileClient);
|
const indexer = new Indexer(db, ethClient, postgraphileClient, ethProvider);
|
||||||
|
|
||||||
const { dbConnectionString, maxCompletionLagInSecs } = jobQueueConfig;
|
const { dbConnectionString, maxCompletionLagInSecs } = jobQueueConfig;
|
||||||
assert(dbConnectionString, 'Missing job queue db connection string');
|
assert(dbConnectionString, 'Missing job queue db connection string');
|
||||||
|
@ -9,7 +9,7 @@ import { ethers } from 'ethers';
|
|||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
|
|
||||||
import { EthClient } from '@vulcanize/ipld-eth-client';
|
import { EthClient } from '@vulcanize/ipld-eth-client';
|
||||||
import { IndexerInterface, Indexer as BaseIndexer } from '@vulcanize/util';
|
import { IndexerInterface, Indexer as BaseIndexer, ValueResult } from '@vulcanize/util';
|
||||||
|
|
||||||
import { Database } from './database';
|
import { Database } from './database';
|
||||||
import { Event, UNKNOWN_EVENT_NAME } from './entity/Event';
|
import { Event, UNKNOWN_EVENT_NAME } from './entity/Event';
|
||||||
@ -40,16 +40,18 @@ export class Indexer implements IndexerInterface {
|
|||||||
_ethClient: EthClient
|
_ethClient: EthClient
|
||||||
_postgraphileClient: EthClient
|
_postgraphileClient: EthClient
|
||||||
_baseIndexer: BaseIndexer
|
_baseIndexer: BaseIndexer
|
||||||
|
_ethProvider: ethers.providers.BaseProvider
|
||||||
|
|
||||||
_factoryContract: ethers.utils.Interface
|
_factoryContract: ethers.utils.Interface
|
||||||
_poolContract: ethers.utils.Interface
|
_poolContract: ethers.utils.Interface
|
||||||
_nfpmContract: ethers.utils.Interface
|
_nfpmContract: ethers.utils.Interface
|
||||||
|
|
||||||
constructor (db: Database, ethClient: EthClient, postgraphileClient: EthClient) {
|
constructor (db: Database, ethClient: EthClient, postgraphileClient: EthClient, ethProvider: ethers.providers.BaseProvider) {
|
||||||
this._db = db;
|
this._db = db;
|
||||||
this._ethClient = ethClient;
|
this._ethClient = ethClient;
|
||||||
this._postgraphileClient = postgraphileClient;
|
this._postgraphileClient = postgraphileClient;
|
||||||
this._baseIndexer = new BaseIndexer(this._db, this._ethClient);
|
this._baseIndexer = new BaseIndexer(this._db, this._ethClient);
|
||||||
|
this._ethProvider = ethProvider;
|
||||||
|
|
||||||
this._factoryContract = new ethers.utils.Interface(factoryABI);
|
this._factoryContract = new ethers.utils.Interface(factoryABI);
|
||||||
this._poolContract = new ethers.utils.Interface(poolABI);
|
this._poolContract = new ethers.utils.Interface(poolABI);
|
||||||
@ -290,6 +292,50 @@ export class Indexer implements IndexerInterface {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async callGetPool (blockHash: string, contractAddress: string, key0: string, key1: string, key2: number): Promise<ValueResult> {
|
||||||
|
const contract = new ethers.Contract(contractAddress, factoryABI, this._ethProvider);
|
||||||
|
|
||||||
|
const { block: { number } } = await this._ethClient.getBlockByHash(blockHash);
|
||||||
|
const blockNumber = ethers.BigNumber.from(number).toNumber();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const value = await contract.getPool(key0, key1, key2, { blockTag: blockNumber });
|
||||||
|
|
||||||
|
return { value };
|
||||||
|
} catch (error: any) {
|
||||||
|
if (error.code === ethers.utils.Logger.errors.CALL_EXCEPTION) {
|
||||||
|
log('eth_call error');
|
||||||
|
log(error);
|
||||||
|
|
||||||
|
throw new Error(error.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async positions (blockHash: string, contractAddress: string, tokenId: string): Promise<ValueResult> {
|
||||||
|
const contract = new ethers.Contract(contractAddress, nfpmABI, this._ethProvider);
|
||||||
|
|
||||||
|
const { block: { number } } = await this._ethClient.getBlockByHash(blockHash);
|
||||||
|
const blockNumber = ethers.BigNumber.from(number).toNumber();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const value = await contract.positions(tokenId, { blockTag: blockNumber });
|
||||||
|
|
||||||
|
return { value };
|
||||||
|
} catch (error: any) {
|
||||||
|
if (error.code === ethers.utils.Logger.errors.CALL_EXCEPTION) {
|
||||||
|
log('eth_call error');
|
||||||
|
log(error);
|
||||||
|
|
||||||
|
throw new Error(error.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async getContract (type: string): Promise<any> {
|
async getContract (type: string): Promise<any> {
|
||||||
const contract = await this._db.getLatestContract(type);
|
const contract = await this._db.getLatestContract(type);
|
||||||
return contract;
|
return contract;
|
||||||
|
@ -7,6 +7,7 @@ import 'reflect-metadata';
|
|||||||
import yargs from 'yargs';
|
import yargs from 'yargs';
|
||||||
import { hideBin } from 'yargs/helpers';
|
import { hideBin } from 'yargs/helpers';
|
||||||
import debug from 'debug';
|
import debug from 'debug';
|
||||||
|
import { getDefaultProvider } from 'ethers';
|
||||||
|
|
||||||
import { getCache } from '@vulcanize/cache';
|
import { getCache } from '@vulcanize/cache';
|
||||||
import { EthClient } from '@vulcanize/ipld-eth-client';
|
import { EthClient } from '@vulcanize/ipld-eth-client';
|
||||||
@ -105,7 +106,7 @@ export const main = async (): Promise<any> => {
|
|||||||
await db.init();
|
await db.init();
|
||||||
|
|
||||||
assert(upstream, 'Missing upstream config');
|
assert(upstream, 'Missing upstream config');
|
||||||
const { ethServer: { gqlApiEndpoint, gqlPostgraphileEndpoint }, cache: cacheConfig } = upstream;
|
const { ethServer: { gqlApiEndpoint, gqlPostgraphileEndpoint, rpcProviderEndpoint }, cache: cacheConfig } = upstream;
|
||||||
assert(gqlApiEndpoint, 'Missing upstream ethServer.gqlApiEndpoint');
|
assert(gqlApiEndpoint, 'Missing upstream ethServer.gqlApiEndpoint');
|
||||||
assert(gqlPostgraphileEndpoint, 'Missing upstream ethServer.gqlPostgraphileEndpoint');
|
assert(gqlPostgraphileEndpoint, 'Missing upstream ethServer.gqlPostgraphileEndpoint');
|
||||||
|
|
||||||
@ -121,7 +122,9 @@ export const main = async (): Promise<any> => {
|
|||||||
cache
|
cache
|
||||||
});
|
});
|
||||||
|
|
||||||
const indexer = new Indexer(db, ethClient, postgraphileClient);
|
const ethProvider = getDefaultProvider(rpcProviderEndpoint);
|
||||||
|
|
||||||
|
const indexer = new Indexer(db, ethClient, postgraphileClient, ethProvider);
|
||||||
|
|
||||||
assert(jobQueueConfig, 'Missing job queue config');
|
assert(jobQueueConfig, 'Missing job queue config');
|
||||||
|
|
||||||
|
@ -135,6 +135,31 @@ query getPosition($blockHash: String!, $tokenId: String!) {
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const queryPositions = gql`
|
||||||
|
query getPosition($blockHash: String!, $contractAddress: String!, $tokenId: String!) {
|
||||||
|
positions(blockHash: $blockHash, contractAddress: $contractAddress, tokenId: $tokenId) {
|
||||||
|
value {
|
||||||
|
nonce,
|
||||||
|
operator,
|
||||||
|
token0,
|
||||||
|
token1,
|
||||||
|
fee,
|
||||||
|
tickLower,
|
||||||
|
tickUpper,
|
||||||
|
liquidity,
|
||||||
|
feeGrowthInside0LastX128,
|
||||||
|
feeGrowthInside1LastX128,
|
||||||
|
tokensOwed0,
|
||||||
|
tokensOwed1,
|
||||||
|
}
|
||||||
|
|
||||||
|
proof {
|
||||||
|
data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const queryPoolIdToPoolKey = gql`
|
export const queryPoolIdToPoolKey = gql`
|
||||||
query poolIdToPoolKey($blockHash: String!, $poolId: String!) {
|
query poolIdToPoolKey($blockHash: String!, $poolId: String!) {
|
||||||
poolIdToPoolKey(blockHash: $blockHash, poolId: $poolId) {
|
poolIdToPoolKey(blockHash: $blockHash, poolId: $poolId) {
|
||||||
@ -160,6 +185,17 @@ query getPool($blockHash: String!, $token0: String!, $token1: String!, $fee: Str
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const queryCallGetPool = gql`
|
||||||
|
query callGetPool($blockHash: String!, $contractAddress: String!, $key0: String!, $key1: String!, $key2: Int!) {
|
||||||
|
callGetPool(blockHash: $blockHash, contractAddress: $contractAddress, key0: $key0, key1: $key1, key2: $key2) {
|
||||||
|
value
|
||||||
|
proof {
|
||||||
|
data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const queryGetContract = gql`
|
export const queryGetContract = gql`
|
||||||
query queryGetContract($type: String!) {
|
query queryGetContract($type: String!) {
|
||||||
getContract(type: $type) {
|
getContract(type: $type) {
|
||||||
|
@ -8,6 +8,7 @@ import debug from 'debug';
|
|||||||
|
|
||||||
import { Indexer } from './indexer';
|
import { Indexer } from './indexer';
|
||||||
import { EventWatcher } from './events';
|
import { EventWatcher } from './events';
|
||||||
|
import { ValueResult } from '@vulcanize/util';
|
||||||
|
|
||||||
const log = debug('vulcanize:resolver');
|
const log = debug('vulcanize:resolver');
|
||||||
|
|
||||||
@ -83,6 +84,11 @@ export const createResolvers = async (indexer: Indexer, eventWatcher: EventWatch
|
|||||||
return indexer.position(blockHash, tokenId);
|
return indexer.position(blockHash, tokenId);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
positions: (_: any, { blockHash, contractAddress, tokenId }: { blockHash: string, contractAddress: string, tokenId: string }): Promise<ValueResult> => {
|
||||||
|
log('positions', blockHash, contractAddress, tokenId);
|
||||||
|
return indexer.positions(blockHash, contractAddress, tokenId);
|
||||||
|
},
|
||||||
|
|
||||||
poolIdToPoolKey: (_: any, { blockHash, poolId }: { blockHash: string, poolId: string }) => {
|
poolIdToPoolKey: (_: any, { blockHash, poolId }: { blockHash: string, poolId: string }) => {
|
||||||
log('poolIdToPoolKey', blockHash, poolId);
|
log('poolIdToPoolKey', blockHash, poolId);
|
||||||
return indexer.poolIdToPoolKey(blockHash, poolId);
|
return indexer.poolIdToPoolKey(blockHash, poolId);
|
||||||
@ -93,6 +99,11 @@ export const createResolvers = async (indexer: Indexer, eventWatcher: EventWatch
|
|||||||
return indexer.getPool(blockHash, token0, token1, fee);
|
return indexer.getPool(blockHash, token0, token1, fee);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
callGetPool: (_: any, { blockHash, contractAddress, key0, key1, key2 }: { blockHash: string, contractAddress: string, key0: string, key1: string, key2: number }): Promise<ValueResult> => {
|
||||||
|
log('callGetPool', blockHash, contractAddress, key0, key1, key2);
|
||||||
|
return indexer.callGetPool(blockHash, contractAddress, key0, key1, key2);
|
||||||
|
},
|
||||||
|
|
||||||
getContract: (_: any, { type }: { type: string }) => {
|
getContract: (_: any, { type }: { type: string }) => {
|
||||||
log('getContract', type);
|
log('getContract', type);
|
||||||
return indexer.getContract(type);
|
return indexer.getContract(type);
|
||||||
|
@ -32,6 +32,12 @@ type ResultGetPool {
|
|||||||
proof: Proof
|
proof: Proof
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ResultCallGetPool {
|
||||||
|
value: String!
|
||||||
|
|
||||||
|
proof: Proof
|
||||||
|
}
|
||||||
|
|
||||||
# Factory Events
|
# Factory Events
|
||||||
|
|
||||||
# event PoolCreated(address indexed token0, address indexed token1, uint24 indexed fee, int24 tickSpacing, address pool);
|
# event PoolCreated(address indexed token0, address indexed token1, uint24 indexed fee, int24 tickSpacing, address pool);
|
||||||
@ -71,6 +77,26 @@ type ResultPoolKey {
|
|||||||
proof: Proof
|
proof: Proof
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PositionsValue {
|
||||||
|
nonce: BigInt!
|
||||||
|
operator: String!
|
||||||
|
token0: String!,
|
||||||
|
token1: String!,
|
||||||
|
fee: Int!,
|
||||||
|
tickLower: BigInt!
|
||||||
|
tickUpper: BigInt!
|
||||||
|
liquidity: BigInt!
|
||||||
|
feeGrowthInside0LastX128: BigInt!
|
||||||
|
feeGrowthInside1LastX128: BigInt!
|
||||||
|
tokensOwed0: BigInt!
|
||||||
|
tokensOwed1: BigInt!
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResultPositions {
|
||||||
|
value: PositionsValue!
|
||||||
|
proof: Proof
|
||||||
|
}
|
||||||
|
|
||||||
# NonfungiblePositionManager Events
|
# NonfungiblePositionManager Events
|
||||||
|
|
||||||
# event IncreaseLiquidity(uint256 indexed tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);
|
# event IncreaseLiquidity(uint256 indexed tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);
|
||||||
@ -223,6 +249,12 @@ type Query {
|
|||||||
poolId: String!
|
poolId: String!
|
||||||
): ResultPoolKey
|
): ResultPoolKey
|
||||||
|
|
||||||
|
positions(
|
||||||
|
blockHash: String!,
|
||||||
|
contractAddress: String!,
|
||||||
|
tokenId: String!
|
||||||
|
): ResultPositions!
|
||||||
|
|
||||||
# Factory
|
# Factory
|
||||||
|
|
||||||
getPool(
|
getPool(
|
||||||
@ -232,6 +264,14 @@ type Query {
|
|||||||
fee: String!
|
fee: String!
|
||||||
): ResultGetPool
|
): ResultGetPool
|
||||||
|
|
||||||
|
callGetPool(
|
||||||
|
blockHash: String!
|
||||||
|
contractAddress: String!
|
||||||
|
key0: String!
|
||||||
|
key1: String!
|
||||||
|
key2: Int!
|
||||||
|
): ResultCallGetPool
|
||||||
|
|
||||||
# Pool
|
# Pool
|
||||||
|
|
||||||
feeGrowthGlobal0X128(
|
feeGrowthGlobal0X128(
|
||||||
|
@ -11,6 +11,7 @@ import { hideBin } from 'yargs/helpers';
|
|||||||
import debug from 'debug';
|
import debug from 'debug';
|
||||||
import 'graphql-import-node';
|
import 'graphql-import-node';
|
||||||
import { createServer } from 'http';
|
import { createServer } from 'http';
|
||||||
|
import { getDefaultProvider } from 'ethers';
|
||||||
|
|
||||||
import { getCache } from '@vulcanize/cache';
|
import { getCache } from '@vulcanize/cache';
|
||||||
import { EthClient } from '@vulcanize/ipld-eth-client';
|
import { EthClient } from '@vulcanize/ipld-eth-client';
|
||||||
@ -51,7 +52,7 @@ export const main = async (): Promise<any> => {
|
|||||||
await db.init();
|
await db.init();
|
||||||
|
|
||||||
assert(upstream, 'Missing upstream config');
|
assert(upstream, 'Missing upstream config');
|
||||||
const { ethServer: { gqlApiEndpoint, gqlPostgraphileEndpoint }, cache: cacheConfig } = upstream;
|
const { ethServer: { gqlApiEndpoint, gqlPostgraphileEndpoint, rpcProviderEndpoint }, cache: cacheConfig } = upstream;
|
||||||
assert(gqlApiEndpoint, 'Missing upstream ethServer.gqlApiEndpoint');
|
assert(gqlApiEndpoint, 'Missing upstream ethServer.gqlApiEndpoint');
|
||||||
assert(gqlPostgraphileEndpoint, 'Missing upstream ethServer.gqlPostgraphileEndpoint');
|
assert(gqlPostgraphileEndpoint, 'Missing upstream ethServer.gqlPostgraphileEndpoint');
|
||||||
|
|
||||||
@ -67,10 +68,12 @@ export const main = async (): Promise<any> => {
|
|||||||
cache
|
cache
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const ethProvider = getDefaultProvider(rpcProviderEndpoint);
|
||||||
|
|
||||||
// Note: In-memory pubsub works fine for now, as each watcher is a single process anyway.
|
// Note: In-memory pubsub works fine for now, as each watcher is a single process anyway.
|
||||||
// Later: https://www.apollographql.com/docs/apollo-server/data/subscriptions/#production-pubsub-libraries
|
// Later: https://www.apollographql.com/docs/apollo-server/data/subscriptions/#production-pubsub-libraries
|
||||||
const pubsub = new PubSub();
|
const pubsub = new PubSub();
|
||||||
const indexer = new Indexer(db, ethClient, postgraphileClient);
|
const indexer = new Indexer(db, ethClient, postgraphileClient, ethProvider);
|
||||||
|
|
||||||
assert(jobQueueConfig, 'Missing job queue config');
|
assert(jobQueueConfig, 'Missing job queue config');
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ import {
|
|||||||
checksCollectEvent
|
checksCollectEvent
|
||||||
} from '../test/utils';
|
} from '../test/utils';
|
||||||
|
|
||||||
const CONFIG_FILE = './environments/local.toml';
|
const CONFIG_FILE = './environments/test.toml';
|
||||||
|
|
||||||
describe('uni-watcher', () => {
|
describe('uni-watcher', () => {
|
||||||
let factory: Contract;
|
let factory: Contract;
|
||||||
@ -64,6 +64,7 @@ describe('uni-watcher', () => {
|
|||||||
let uniClient: UniClient;
|
let uniClient: UniClient;
|
||||||
let ethClient: EthClient;
|
let ethClient: EthClient;
|
||||||
let postgraphileClient: EthClient;
|
let postgraphileClient: EthClient;
|
||||||
|
let ethProvider: ethers.providers.JsonRpcProvider;
|
||||||
let signer: Signer;
|
let signer: Signer;
|
||||||
let recipient: string;
|
let recipient: string;
|
||||||
let deadline: number;
|
let deadline: number;
|
||||||
@ -106,8 +107,8 @@ describe('uni-watcher', () => {
|
|||||||
gqlSubscriptionEndpoint
|
gqlSubscriptionEndpoint
|
||||||
});
|
});
|
||||||
|
|
||||||
const provider = new ethers.providers.JsonRpcProvider(rpcProviderEndpoint);
|
ethProvider = new ethers.providers.JsonRpcProvider(rpcProviderEndpoint);
|
||||||
signer = provider.getSigner();
|
signer = ethProvider.getSigner();
|
||||||
recipient = await signer.getAddress();
|
recipient = await signer.getAddress();
|
||||||
|
|
||||||
// Deadline set to 2 days from current date.
|
// Deadline set to 2 days from current date.
|
||||||
@ -129,7 +130,7 @@ describe('uni-watcher', () => {
|
|||||||
factory = new Contract(factoryContract.address, FACTORY_ABI, signer);
|
factory = new Contract(factoryContract.address, FACTORY_ABI, signer);
|
||||||
|
|
||||||
// Verifying with the db.
|
// Verifying with the db.
|
||||||
const indexer = new Indexer(db, ethClient, postgraphileClient);
|
const indexer = new Indexer(db, ethClient, postgraphileClient, ethProvider);
|
||||||
assert(await indexer.isWatchedContract(factory.address), 'Factory contract not added to the database.');
|
assert(await indexer.isWatchedContract(factory.address), 'Factory contract not added to the database.');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -264,7 +265,7 @@ describe('uni-watcher', () => {
|
|||||||
nfpm = new Contract(nfpmContract.address, NFPM_ABI, signer);
|
nfpm = new Contract(nfpmContract.address, NFPM_ABI, signer);
|
||||||
|
|
||||||
// Verifying with the db.
|
// Verifying with the db.
|
||||||
const indexer = new Indexer(db, ethClient, postgraphileClient);
|
const indexer = new Indexer(db, ethClient, postgraphileClient, ethProvider);
|
||||||
assert(await indexer.isWatchedContract(nfpm.address), 'NFPM contract not added to the database.');
|
assert(await indexer.isWatchedContract(nfpm.address), 'NFPM contract not added to the database.');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ import { Client as UniClient } from '../src/client';
|
|||||||
import { Database } from '../src/database';
|
import { Database } from '../src/database';
|
||||||
import { watchContract } from '../src/utils/index';
|
import { watchContract } from '../src/utils/index';
|
||||||
|
|
||||||
const CONFIG_FILE = './environments/local.toml';
|
const CONFIG_FILE = './environments/test.toml';
|
||||||
|
|
||||||
const deployFactoryContract = async (db: Database, signer: Signer): Promise<Contract> => {
|
const deployFactoryContract = async (db: Database, signer: Signer): Promise<Contract> => {
|
||||||
// Deploy factory from uniswap package.
|
// Deploy factory from uniswap package.
|
||||||
|
Loading…
Reference in New Issue
Block a user