Extra info for handling events (tx, block) (#136)

* Extra info in events for downstream processing.

* Changes in uni-info-watcher after change in uni-watcher event schema.

Co-authored-by: nabarun <nabarun@deepstacksoft.com>
This commit is contained in:
Ashwin Phatak 2021-07-13 17:32:57 +05:30 committed by GitHub
parent 208b0f7f4f
commit a4f5d43bc5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 283 additions and 223 deletions

View File

@ -1,4 +1,5 @@
import assert from 'assert';
import _ from 'lodash';
import { Cache } from '@vulcanize/cache';
@ -64,9 +65,21 @@ export class EthClient {
async getLogs (vars: Vars): Promise<any> {
const result = await this._getCachedOrFetch('getLogs', vars);
const { getLogs: logs } = result;
const { getLogs: logs, block: { number: blockNumHex, timestamp: timestampHex } } = result;
const blockNumber = parseInt(blockNumHex, 16);
const timestamp = parseInt(timestampHex, 16);
return logs;
return logs.map((logEntry: any) => {
return _.merge({}, logEntry, {
transaction: {
block: {
hash: vars.blockHash,
number: blockNumber,
timestamp
}
}
});
});
}
async watchLogs (onNext: (value: any) => void): Promise<ZenObservable.Subscription> {

View File

@ -21,9 +21,14 @@ query getLogs($blockHash: Bytes32!, $contract: Address) {
}
topics
data
index
cid
ipldBlock
}
block(hash: $blockHash) {
number
timestamp
}
}
`;

View File

@ -14,6 +14,7 @@ import { loadTick } from './utils/tick';
const log = debug('vulcanize:events');
interface PoolCreatedEvent {
__typename: 'PoolCreatedEvent';
token0: string;
token1: string;
fee: bigint;
@ -22,11 +23,13 @@ interface PoolCreatedEvent {
}
interface InitializeEvent {
__typename: 'InitializeEvent';
sqrtPriceX96: bigint;
tick: bigint;
}
interface MintEvent {
__typename: 'MintEvent';
sender: string;
owner: string;
tickLower: bigint;
@ -37,6 +40,7 @@ interface MintEvent {
}
interface BurnEvent {
__typename: 'BurnEvent';
owner: string;
tickLower: bigint;
tickUpper: bigint;
@ -45,13 +49,24 @@ interface BurnEvent {
amount1: bigint;
}
interface Block {
number: number;
hash: string;
timestamp: number;
}
interface Transaction {
hash: string;
from: string;
}
interface ResultEvent {
block: Block;
tx: Transaction;
contract: string;
event: PoolCreatedEvent | InitializeEvent | MintEvent | BurnEvent;
proof: {
data: string
}
event: {
__typename: string;
[key: string]: any;
data: string;
}
}
@ -82,29 +97,29 @@ export class EventWatcher {
}
}
async _handleEvents ({ blockHash, blockNumber, contract, txHash, event }: { blockHash: string, blockNumber: number, contract: string, txHash: string, event: ResultEvent}): Promise<void> {
async _handleEvents ({ block, tx, contract, event }: ResultEvent): Promise<void> {
// TODO: Process proof (proof.data) in event.
const { event: { __typename: eventType, ...eventValues } } = event;
const { __typename: eventType } = event;
switch (eventType) {
case 'PoolCreatedEvent':
log('Factory PoolCreated event', contract);
this._handlePoolCreated(blockHash, blockNumber, contract, txHash, eventValues as PoolCreatedEvent);
this._handlePoolCreated(block, contract, tx, event as PoolCreatedEvent);
break;
case 'InitializeEvent':
log('Pool Initialize event', contract);
this._handleInitialize(blockHash, blockNumber, contract, txHash, eventValues as InitializeEvent);
this._handleInitialize(block, contract, tx, event as InitializeEvent);
break;
case 'MintEvent':
log('Pool Mint event', contract);
this._handleMint(blockHash, blockNumber, contract, txHash, eventValues as MintEvent);
this._handleMint(block, contract, tx, event as MintEvent);
break;
case 'BurnEvent':
log('Pool Burn event', contract);
this._handleBurn(blockHash, blockNumber, contract, txHash, eventValues as BurnEvent);
this._handleBurn(block, contract, tx, event as BurnEvent);
break;
default:
@ -112,7 +127,8 @@ export class EventWatcher {
}
}
async _handlePoolCreated (blockHash: string, blockNumber: number, contractAddress: string, txHash: string, poolCreatedEvent: PoolCreatedEvent): Promise<void> {
async _handlePoolCreated (block: Block, contractAddress: string, tx: Transaction, poolCreatedEvent: PoolCreatedEvent): Promise<void> {
const { number: blockNumber, hash: blockHash } = block;
const { token0: token0Address, token1: token1Address, fee, pool: poolAddress } = poolCreatedEvent;
// Load factory.
@ -184,7 +200,8 @@ export class EventWatcher {
});
}
async _handleInitialize (blockHash: string, blockNumber: number, contractAddress: string, txHash: string, initializeEvent: InitializeEvent): Promise<void> {
async _handleInitialize (block: Block, contractAddress: string, tx: Transaction, initializeEvent: InitializeEvent): Promise<void> {
const { number: blockNumber, timestamp: blockTimestamp } = block;
const { sqrtPriceX96, tick } = initializeEvent;
const pool = await this._db.getPool({ id: contractAddress, blockNumber });
assert(pool, `Pool ${contractAddress} not found.`);
@ -199,8 +216,8 @@ export class EventWatcher {
bundle.ethPriceUSD = await getEthPriceInUSD(this._db);
this._db.saveBundle(bundle, blockNumber);
await updatePoolDayData(this._db, { contractAddress, blockNumber });
await updatePoolHourData(this._db, { contractAddress, blockNumber });
await updatePoolDayData(this._db, { contractAddress, blockNumber, blockTimestamp });
await updatePoolHourData(this._db, { contractAddress, blockNumber, blockTimestamp });
const [token0, token1] = await Promise.all([
this._db.getToken({ id: pool.token0.id, blockNumber }),
@ -219,7 +236,9 @@ export class EventWatcher {
]);
}
async _handleMint (blockHash: string, blockNumber: number, contractAddress: string, txHash: string, mintEvent: MintEvent): Promise<void> {
async _handleMint (block: Block, contractAddress: string, tx: Transaction, mintEvent: MintEvent): Promise<void> {
const { number: blockNumber, timestamp: blockTimestamp } = block;
const { hash: txHash } = tx;
const bundle = await this._db.loadBundle({ id: '1', blockNumber });
const poolAddress = contractAddress;
const pool = await this._db.loadPool({ id: poolAddress, blockNumber });
@ -279,7 +298,7 @@ export class EventWatcher {
factory.totalValueLockedETH = factory.totalValueLockedETH.plus(pool.totalValueLockedETH);
factory.totalValueLockedUSD = factory.totalValueLockedETH.times(bundle.ethPriceUSD);
const transaction = await loadTransaction(this._db, { txHash, blockNumber });
const transaction = await loadTransaction(this._db, { txHash, blockNumber, blockTimestamp });
await this._db.loadMint({
id: transaction.id + '#' + pool.txCount.toString(),
@ -322,13 +341,13 @@ export class EventWatcher {
// 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 updateUniswapDayData(this._db, { blockNumber, contractAddress, blockTimestamp });
await updatePoolDayData(this._db, { blockNumber, contractAddress, blockTimestamp });
await updatePoolHourData(this._db, { blockNumber, contractAddress, blockTimestamp });
await updateTokenDayData(this._db, token0, { blockNumber, blockTimestamp });
await updateTokenDayData(this._db, token1, { blockNumber, blockTimestamp });
await updateTokenHourData(this._db, token0, { blockNumber, blockTimestamp });
await updateTokenHourData(this._db, token1, { blockNumber, blockTimestamp });
await Promise.all([
this._db.saveToken(token0, blockNumber),
@ -346,7 +365,9 @@ export class EventWatcher {
// Skipping update inner tick vars and tick day data as they are not queried.
}
async _handleBurn (blockHash: string, blockNumber: number, contractAddress: string, txHash: string, burnEvent: BurnEvent): Promise<void> {
async _handleBurn (block: Block, contractAddress: string, tx: Transaction, burnEvent: BurnEvent): Promise<void> {
const { number: blockNumber, timestamp: blockTimestamp } = block;
const { hash: txHash } = tx;
const bundle = await this._db.loadBundle({ id: '1', blockNumber });
const poolAddress = contractAddress;
const pool = await this._db.loadPool({ id: poolAddress, blockNumber });
@ -407,7 +428,7 @@ export class EventWatcher {
factory.totalValueLockedUSD = factory.totalValueLockedETH.times(bundle.ethPriceUSD);
// Burn entity.
const transaction = await loadTransaction(this._db, { txHash, blockNumber });
const transaction = await loadTransaction(this._db, { txHash, blockNumber, blockTimestamp });
await this._db.loadBurn({
id: transaction.id + '#' + pool.txCount.toString(),
@ -441,13 +462,13 @@ export class EventWatcher {
upperTick.liquidityGross = upperTick.liquidityGross - amount;
upperTick.liquidityNet = upperTick.liquidityNet + amount;
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, token0, { blockNumber });
await updateTokenHourData(this._db, token0, { blockNumber });
await updateTokenHourData(this._db, token0, { blockNumber });
await updateUniswapDayData(this._db, { blockNumber, contractAddress, blockTimestamp });
await updatePoolDayData(this._db, { blockNumber, contractAddress, blockTimestamp });
await updatePoolHourData(this._db, { blockNumber, contractAddress, blockTimestamp });
await updateTokenDayData(this._db, token0, { blockNumber, blockTimestamp });
await updateTokenDayData(this._db, token0, { blockNumber, blockTimestamp });
await updateTokenHourData(this._db, token0, { blockNumber, blockTimestamp });
await updateTokenHourData(this._db, token0, { blockNumber, blockTimestamp });
// Skipping update Tick fee and Tick day data as they are not queried.

View File

@ -22,21 +22,17 @@ export const convertTokenToDecimal = (tokenAmount: bigint, exchangeDecimals: big
return (new Decimal(tokenAmount.toString())).div(exponentToBigDecimal(exchangeDecimals));
};
export const loadTransaction = async (db: Database, event: { txHash: string, blockNumber: number }): 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.
export const loadTransaction = async (db: Database, event: { txHash: string, blockNumber: number, blockTimestamp: number }): Promise<Transaction> => {
const { txHash, blockNumber, blockTimestamp } = event;
const transaction = await db.loadTransaction({
id: txHash,
blockNumber,
timestamp
timestamp: BigInt(blockTimestamp)
});
transaction.blockNumber = blockNumber;
transaction.timestamp = timestamp;
transaction.timestamp = BigInt(blockTimestamp);
return db.saveTransaction(transaction, blockNumber);
};

View File

@ -13,17 +13,14 @@ import { UniswapDayData } from '../entity/UniswapDayData';
* @param db
* @param event
*/
export const updateUniswapDayData = async (db: Database, event: { contractAddress: string, blockNumber: number }): Promise<UniswapDayData> => {
const { blockNumber } = event;
export const updateUniswapDayData = async (db: Database, event: { contractAddress: string, blockNumber: number, blockTimestamp: number }): Promise<UniswapDayData> => {
const { blockNumber, blockTimestamp } = 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 dayID = Math.floor(blockTimestamp / 86400); // Rounded.
const dayStartTimestamp = dayID * 86400;
const uniswapDayData = await db.loadUniswapDayData({
@ -39,14 +36,9 @@ export const updateUniswapDayData = async (db: Database, event: { contractAddres
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);
export const updatePoolDayData = async (db: Database, event: { contractAddress: string, blockNumber: number, blockTimestamp: number }): Promise<PoolDayData> => {
const { contractAddress, blockNumber, blockTimestamp } = event;
const dayID = Math.floor(blockTimestamp / 86400);
const dayStartTimestamp = dayID * 86400;
const dayPoolID = contractAddress
@ -88,14 +80,9 @@ export const updatePoolDayData = async (db: Database, event: { contractAddress:
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.
export const updatePoolHourData = async (db: Database, event: { contractAddress: string, blockNumber: number, blockTimestamp: number }): Promise<PoolHourData> => {
const { contractAddress, blockNumber, blockTimestamp } = event;
const hourIndex = Math.floor(blockTimestamp / 3600); // Get unique hour within unix history.
const hourStartUnix = hourIndex * 3600; // Want the rounded effect.
const hourPoolID = contractAddress
@ -137,15 +124,10 @@ export const updatePoolHourData = async (db: Database, event: { contractAddress:
return poolHourData;
};
export const updateTokenDayData = async (db: Database, token: Token, event: { blockNumber: number }): Promise<TokenDayData> => {
const { blockNumber } = event;
export const updateTokenDayData = async (db: Database, token: Token, event: { blockNumber: number, blockTimestamp: number }): Promise<TokenDayData> => {
const { blockNumber, blockTimestamp } = event;
const bundle = await db.loadBundle({ id: '1', blockNumber });
// 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 dayID = Math.floor(blockTimestamp / 86400);
const dayStartTimestamp = dayID * 86400;
const tokenDayID = token.id
@ -183,15 +165,10 @@ export const updateTokenDayData = async (db: Database, token: Token, event: { bl
return db.saveTokenDayData(tokenDayData, blockNumber);
};
export const updateTokenHourData = async (db: Database, token: Token, event: { blockNumber: number }): Promise<TokenHourData> => {
const { blockNumber } = event;
export const updateTokenHourData = async (db: Database, token: Token, event: { blockNumber: number, blockTimestamp: number }): Promise<TokenHourData> => {
const { blockNumber, blockTimestamp } = event;
const bundle = await db.loadBundle({ id: '1', blockNumber });
// 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 hourIndex = Math.floor(blockTimestamp / 3600); // Get unique hour within unix history.
const hourStartUnix = hourIndex * 3600; // Want the rounded effect.
const tokenHourID = token.id

View File

@ -16,11 +16,15 @@ export class Client {
gql`
subscription SubscriptionReceipt {
onEvent {
blockHash
blockNumber
block {
number
hash
timestamp
}
contract
txHash
event {
tx {
hash
}
proof {
data
}
@ -61,7 +65,6 @@ export class Client {
}
}
}
}
`,
({ data }) => {
onNext(data.onEvent);

View File

@ -7,21 +7,37 @@ export class Event {
@PrimaryGeneratedColumn()
id!: number;
// TODO: Denormalizing the block fields is simpler but perhaps not necessary.
@Column('varchar', { length: 66 })
blockHash!: string;
@Column('integer')
blockNumber!: number;
@Column('integer')
blockTimestamp!: number;
@Column('varchar', { length: 66 })
txHash!: string;
// Index of the log in the block.
@Column('integer')
index!: number;
@Column('varchar', { length: 42 })
contract!: string;
@Column('varchar', { length: 256 })
eventName!: string;
// TODO: Polymorphic relationships?
@Column('text')
eventData!: string;
eventInfo!: string;
@Column('text')
extraInfo!: string;
@Column('boolean', { default: false })
isProcessed!: boolean;
@Column('text')
proof!: string;

View File

@ -30,14 +30,14 @@ export class EventWatcher {
const receipt = _.get(value, 'data.listen.relatedNode');
log('watchLogs', JSON.stringify(receipt, null, 2));
const blocks = [];
const blocks: string[] = [];
const { logContracts } = receipt;
if (logContracts && logContracts.length) {
for (let logIndex = 0; logIndex < logContracts.length; logIndex++) {
const { ethTransactionCidByTxId: { ethHeaderCidByHeaderId: { blockHash, blockNumber } } } = receipt;
const { ethTransactionCidByTxId: { ethHeaderCidByHeaderId: { blockHash } } } = receipt;
await this._indexer.getBlockEvents(blockHash);
blocks.push({ blockHash, blockNumber });
blocks.push(blockHash);
}
}
@ -48,7 +48,7 @@ export class EventWatcher {
// Process events, if from known uniswap contracts.
for (let bi = 0; bi < blocks.length; bi++) {
const { blockHash, blockNumber } = blocks[bi];
const blockHash = blocks[bi];
if (processedBlocks[blockHash]) {
continue;
}
@ -56,13 +56,13 @@ export class EventWatcher {
const events = await this._indexer.getBlockEvents(blockHash);
for (let ei = 0; ei < events.length; ei++) {
const eventObj = events[ei];
const uniContract = await this._indexer.isUniswapContract(eventObj.extra.contract);
const uniContract = await this._indexer.isUniswapContract(eventObj.contract);
if (uniContract) {
log('event', JSON.stringify(eventObj, null, 2));
// TODO: Move processing to background queue (need sequential processing of events).
// Trigger other indexer methods based on event topic.
await this._indexer.processEvent(blockHash, blockNumber, uniContract, eventObj.extra.txHash, eventObj);
await this._indexer.processEvent(eventObj);
}
}

View File

@ -19,13 +19,17 @@ import poolABI from './artifacts/pool.json';
const log = debug('vulcanize:indexer');
type EventResult = {
event: any;
proof: string;
extra: any;
};
type ResultEvent = {
block: any;
tx: any;
type EventsResult = Array<EventResult>;
contract: string;
eventIndex: number;
event: any;
proof: string;
};
export class Indexer {
_config: Config;
@ -57,11 +61,11 @@ export class Indexer {
return this._pubsub.asyncIterator(['event']);
}
async getBlockEvents (blockHash: string): Promise<EventsResult> {
async getBlockEvents (blockHash: string): Promise<Array<Event>> {
const didSyncEvents = await this._db.didSyncEvents({ blockHash });
if (!didSyncEvents) {
// Fetch and save events first and make a note in the event sync progress table.
await this._fetchAndSaveEvents({ blockHash });
await this.fetchAndSaveEvents({ blockHash });
log('getEvents: db miss, fetching from upstream server');
}
@ -70,30 +74,37 @@ export class Indexer {
const events = await this._db.getBlockEvents({ blockHash });
log(`getEvents: db hit, num events: ${events.length}`);
const result = events
.map(e => {
const eventFields = JSON.parse(e.eventData);
return events;
}
getResultEvent (event: Event): ResultEvent {
const eventFields = JSON.parse(event.eventInfo);
return {
block: {
hash: event.blockHash,
number: event.blockNumber,
timestamp: event.blockTimestamp
},
tx: {
hash: event.txHash
},
contract: event.contract,
eventIndex: event.index,
event: {
__typename: `${e.eventName}Event`,
__typename: `${event.eventName}Event`,
...eventFields
},
// TODO: Return proof only if requested.
proof: JSON.parse(e.proof),
extra: {
contract: e.contract,
txHash: e.txHash
}
proof: JSON.parse(event.proof),
};
});
// log(JSONbig.stringify(result, null, 2));
return result;
}
async getEvents (blockHash: string, contract: string, name: string | null): Promise<EventsResult> {
async getEvents (blockHash: string, contract: string, name: string | null): Promise<Array<Event>> {
const uniContract = await this.isUniswapContract(contract);
if (!uniContract) {
throw new Error('Not a uniswap contract');
@ -102,7 +113,7 @@ export class Indexer {
const didSyncEvents = await this._db.didSyncEvents({ blockHash });
if (!didSyncEvents) {
// Fetch and save events first and make a note in the event sync progress table.
await this._fetchAndSaveEvents({ blockHash });
await this.fetchAndSaveEvents({ blockHash });
log('getEvents: db miss, fetching from upstream server');
}
@ -114,50 +125,30 @@ export class Indexer {
const result = events
.filter(event => contract === event.contract)
// TODO: Filter using db WHERE condition when name is not empty.
.filter(event => !name || name === event.eventName)
.map(e => {
const eventFields = JSON.parse(e.eventData);
return {
event: {
__typename: `${e.eventName}Event`,
...eventFields
},
// TODO: Return proof only if requested.
proof: JSON.parse(e.proof),
extra: {
contract: e.contract,
txHash: e.txHash
}
};
});
// log(JSONbig.stringify(result, null, 2));
.filter(event => !name || name === event.eventName);
return result;
}
async triggerIndexingOnEvent (blockNumber: number, event: EventResult): Promise<void> {
switch (event.event.__typename) {
async triggerIndexingOnEvent (dbEvent: Event): Promise<void> {
const re = this.getResultEvent(dbEvent);
switch (re.event.__typename) {
case 'PoolCreatedEvent': {
const poolContract = ethers.utils.getAddress(event.event.pool);
await this._db.saveContract(poolContract, KIND_POOL, blockNumber);
const poolContract = ethers.utils.getAddress(re.event.pool);
await this._db.saveContract(poolContract, KIND_POOL, dbEvent.blockNumber);
}
}
}
async publishEventToSubscribers (blockHash: string, blockNumber: number, contract: string, txHash: string, event: EventResult): Promise<void> {
log(`pushing event to GQL subscribers: ${event.event.__typename}`);
async publishEventToSubscribers (dbEvent: Event): Promise<void> {
const resultEvent = this.getResultEvent(dbEvent);
log(`pushing event to GQL subscribers: ${resultEvent.event.__typename}`);
// Publishing the event here will result in pushing the payload to GQL subscribers for `onEvent`.
await this._pubsub.publish('event', {
onEvent: {
blockHash,
blockNumber,
contract,
txHash,
event
}
onEvent: resultEvent
});
}
@ -165,29 +156,47 @@ export class Indexer {
return this._db.getContract(ethers.utils.getAddress(address));
}
async processEvent (blockHash: string, blockNumber: number, contract: Contract, txHash: string, event: EventResult): Promise<void> {
async processEvent (event: Event): Promise<void> {
// Trigger indexing of data based on the event.
await this.triggerIndexingOnEvent(blockNumber, event);
await this.triggerIndexingOnEvent(event);
// Also trigger downstream event watcher subscriptions.
await this.publishEventToSubscribers(blockHash, blockNumber, contract.address, txHash, event);
await this.publishEventToSubscribers(event);
}
async _fetchAndSaveEvents ({ blockHash }: { blockHash: string }): Promise<void> {
async fetchAndSaveEvents ({ blockHash }: { blockHash: string }): Promise<void> {
const logs = await this._ethClient.getLogs({ blockHash });
const dbEvents: Array<DeepPartial<Event>> = [];
for (let logIndex = 0; logIndex < logs.length; logIndex++) {
const logObj = logs[logIndex];
const { topics, data, cid, ipldBlock, account: { address }, transaction: { hash: txHash } } = logObj;
for (let li = 0; li < logs.length; li++) {
const logObj = logs[li];
const {
topics,
data,
index: logIndex,
cid,
ipldBlock,
account: {
address
},
transaction: {
hash: txHash,
block: {
number: blockNumber,
timestamp: blockTimestamp
}
}
} = logObj;
let eventName;
let eventProps = {};
let eventInfo = {};
let extraInfo = {};
const contract = ethers.utils.getAddress(address);
const uniContract = await this.isUniswapContract(contract);
if (!uniContract) {
// TODO: Can only be known if events are processed serially.
continue;
}
@ -198,7 +207,7 @@ export class Indexer {
case 'PoolCreated': {
eventName = logDescription.name;
const { token0, token1, fee, tickSpacing, pool } = logDescription.args;
eventProps = { token0, token1, fee, tickSpacing, pool };
eventInfo = { token0, token1, fee, tickSpacing, pool };
break;
}
@ -212,14 +221,14 @@ export class Indexer {
case 'Initialize': {
eventName = logDescription.name;
const { sqrtPriceX96, tick } = logDescription.args;
eventProps = { sqrtPriceX96: sqrtPriceX96.toString(), tick };
eventInfo = { sqrtPriceX96: sqrtPriceX96.toString(), tick };
break;
}
case 'Mint': {
eventName = logDescription.name;
const { sender, owner, tickLower, tickUpper, amount, amount0, amount1 } = logDescription.args;
eventProps = {
eventInfo = {
sender,
owner,
tickLower,
@ -234,7 +243,7 @@ export class Indexer {
case 'Burn': {
eventName = logDescription.name;
const { owner, tickLower, tickUpper, amount, amount0, amount1 } = logDescription.args;
eventProps = {
eventInfo = {
owner,
tickLower,
tickUpper,
@ -248,7 +257,7 @@ export class Indexer {
case 'Swap': {
eventName = logDescription.name;
const { sender, recipient, amount0, amount1, sqrtPriceX96, liquidity, tick } = logDescription.args;
eventProps = {
eventInfo = {
sender,
recipient,
amount0: amount0.toString(),
@ -269,10 +278,14 @@ export class Indexer {
if (eventName) {
dbEvents.push({
blockHash,
blockNumber,
blockTimestamp,
index: logIndex,
txHash,
contract,
eventName,
eventData: JSONbig.stringify({ ...eventProps }),
eventInfo: JSONbig.stringify(eventInfo),
extraInfo: JSONbig.stringify(extraInfo),
proof: JSONbig.stringify({
data: JSONbig.stringify({
blockHash,

View File

@ -54,7 +54,9 @@ export const createResolvers = async (indexer: Indexer): Promise<any> => {
events: async (_: any, { blockHash, contract, name }: { blockHash: string, contract: string, name: string }) => {
log('events', blockHash, contract, name || '');
return indexer.getEvents(blockHash, contract, name);
const events = await indexer.getEvents(blockHash, contract, name);
return events.map(event => indexer.getResultEvent(event));
}
}
};

View File

@ -12,14 +12,6 @@ type Proof {
data: String!
}
# Result type, with proof, for string method return values.
type ResultString {
value: String
# Proof from state/storage trie.
proof: Proof
}
# Result type, with proof, for uint256 method return values.
type ResultUInt256 {
value: BigInt!
@ -162,23 +154,39 @@ union PoolEvent = InitializeEvent | MintEvent | BurnEvent | SwapEvent
# All events emitted by the watcher.
union Event = TransferEvent | PoolCreatedEvent | IncreaseLiquidityEvent | DecreaseLiquidityEvent | CollectEvent | InitializeEvent | MintEvent | BurnEvent | SwapEvent
# Result type, with proof, for event return values.
# Ethereum types
type Block {
hash: String!
number: Int!
timestamp: Int!
}
type Transaction {
hash: String!
index: Int!
from: String!
to: String!
}
# Result event, include additional context over and above the event data.
type ResultEvent {
# Block and tx data for the event.
block: Block!
tx: Transaction!
# Contract that generated the event.
contract: String!
# Index of the event in the block.
eventIndex: Int!
event: Event!
# Proof from receipts trie.
proof: Proof
}
# Watched event, include additional context over and above the event data.
type WatchedEvent {
blockHash: String!
blockNumber: Int!
contract: String!
txHash: String!
event: ResultEvent!
}
#
# Queries
@ -224,6 +232,12 @@ type Query {
contract: String!
name: String
): [ResultEvent!]
# Get uniswap events in a given block range.
eventsInRange(
fromBlockNumber: Int!
toBlockNumber: Int!
): [ResultEvent!]
}
#
@ -232,6 +246,6 @@ type Query {
type Subscription {
# Watch for events (at head of chain).
onEvent: WatchedEvent!
onEvent: ResultEvent!
}
`;