watcher-ts/packages/erc721-watcher/src/hooks.ts
prathamesh0 306bbb73ca
Use prefetching of blocks with events in watchers and codegen (#206)
* Avoid refetching block while fetching events

* Prefetch a batch of blocks with events while indexing

* Update mock indexer used in graph-node testing

* Process available blocks while prefetching

* Refactor events fetching to a method in util

* Move method to get GQL event query result to util
2022-10-20 18:46:56 +05:30

146 lines
5.1 KiB
TypeScript

//
// Copyright 2021 Vulcanize, Inc.
//
import assert from 'assert';
import { updateStateForElementaryType, ResultEvent } from '@cerc-io/util';
import { Indexer } from './indexer';
import { TransferCount } from './entity/TransferCount';
/**
* Hook function to store an initial state.
* @param indexer Indexer instance.
* @param blockHash Hash of the concerned block.
* @param contractAddress Address of the concerned contract.
* @returns Data block to be stored.
*/
export async function createInitialState (indexer: Indexer, contractAddress: string, blockHash: string): Promise<any> {
assert(indexer);
assert(blockHash);
assert(contractAddress);
// Store an empty State.
const stateData: any = {
state: {}
};
// Use updateStateForElementaryType to update initial state with an elementary property.
// Eg. const stateData = updateStateForElementaryType(stateData, '_totalBalance', result.value.toString());
// Use updateStateForMappingType to update initial state with a nested property.
// Eg. const stateData = updateStateForMappingType(stateData, '_allowances', [owner, spender], allowance.value.toString());
// Return initial state data to be saved.
return stateData;
}
/**
* Hook function to create state diff.
* @param indexer Indexer instance that contains methods to fetch the contract variable values.
* @param blockHash Block hash of the concerned block.
*/
export async function createStateDiff (indexer: Indexer, blockHash: string): Promise<void> {
assert(indexer);
assert(blockHash);
// Use indexer.createDiff() method to save custom state diff(s).
}
/**
* Hook function to create state checkpoint
* @param indexer Indexer instance.
* @param contractAddress Address of the concerned contract.
* @param blockHash Block hash of the concerned block.
* @returns Whether to disable default checkpoint. If false, the state from this hook is updated with that from default checkpoint.
*/
export async function createStateCheckpoint (indexer: Indexer, contractAddress: string, blockHash: string): Promise<boolean> {
assert(indexer);
assert(blockHash);
assert(contractAddress);
// Use indexer.createStateCheckpoint() method to create a custom checkpoint.
// Return false to update the state created by this hook by auto-generated checkpoint state.
// Return true to disable update of the state created by this hook by auto-generated checkpoint state.
return false;
}
/**
* Event hook function.
* @param indexer Indexer instance that contains methods to fetch and update the contract values in the database.
* @param eventData ResultEvent object containing event information.
*/
export async function handleEvent (indexer: Indexer, eventData: ResultEvent): Promise<void> {
assert(indexer);
assert(eventData);
// Perform indexing based on the type of event.
switch (eventData.event.__typename) {
case 'TransferEvent': {
// Get event fields from eventData.
const { from, to, tokenId } = eventData.event;
// Update balance entry for the sender in database.
if (from !== '0x0000000000000000000000000000000000000000') {
await indexer._balances(eventData.block.hash, eventData.contract, from, true);
}
// Update balance entry for the receiver in database.
if (to !== '0x0000000000000000000000000000000000000000') {
await indexer._balances(eventData.block.hash, eventData.contract, to, true);
}
// Update owner for the tokenId in database.
await indexer._owners(eventData.block.hash, eventData.contract, tokenId, true);
// Code to update a custom state property transferCount.
// {
// "transferCount": "1"
// }
// Fetch transferCount entity from database.
let transferCount = await indexer.getTransferCount(eventData.contract, eventData.block);
if (!transferCount) {
transferCount = new TransferCount();
transferCount.id = eventData.contract;
transferCount.count = 0;
}
transferCount.blockHash = eventData.block.hash;
transferCount.blockNumber = eventData.block.number;
// Increment count on transfer event.
transferCount.count++;
// Update state for custom property transferCount.
const stateUpdate = updateStateForElementaryType({}, 'transferCount', transferCount.count);
await indexer.createDiffStaged(eventData.contract, eventData.block.hash, stateUpdate);
// Save transferCount to database.
await indexer.saveOrUpdateTransferCount(transferCount);
break;
}
case 'ApprovalEvent': {
// Get event fields from eventData.
const { tokenId } = eventData.event;
// Update tokenApprovals for the tokenId in database.
await indexer._tokenApprovals(eventData.block.hash, eventData.contract, tokenId, true);
break;
}
case 'ApprovalForAllEvent': {
// Get event fields from eventData.
const { owner, operator } = eventData.event;
// Update operatorApprovals for the tokenId in database.
await indexer._operatorApprovals(eventData.block.hash, eventData.contract, owner, operator, true);
break;
}
}
}