Get missing fields in transaction data for subgraph event handlers (#102)

* Get missing fields in event transaction

* Cache transaction data for event handlers in subgraph
This commit is contained in:
2021-12-29 13:21:39 +05:30
committed by GitHub
parent 561c2c9066
commit 9b1aa29afd
8 changed files with 128 additions and 35 deletions
+8 -10
View File
@@ -42,9 +42,7 @@ export interface GraphData {
}
export interface Context {
event: {
block?: Block
}
block?: Block
}
const log = debug('vulcanize:graph-node');
@@ -71,8 +69,8 @@ export const instantiate = async (
const entityName = __getString(entity);
const entityId = __getString(id);
assert(context.event.block);
const entityData = await database.getEntity(entityName, entityId, context.event.block.blockHash);
assert(context.block);
const entityData = await database.getEntity(entityName, entityId, context.block.blockHash);
if (!entityData) {
return null;
@@ -91,8 +89,8 @@ export const instantiate = async (
const entityInstance = await Entity.wrap(data);
assert(context.event.block);
let dbData = await database.fromGraphEntity(instanceExports, context.event.block, entityName, entityInstance);
assert(context.block);
let dbData = await database.fromGraphEntity(instanceExports, context.block, entityName, entityInstance);
await database.saveEntity(entityName, dbData);
// Resolve any field name conflicts in the dbData for auto-diff.
@@ -108,7 +106,7 @@ export const instantiate = async (
// Create an auto-diff.
assert(indexer.createDiffStaged);
assert(dataSource?.address);
await indexer.createDiffStaged(dataSource.address, context.event.block.blockHash, diffData);
await indexer.createDiffStaged(dataSource.address, context.block.blockHash, diffData);
},
'log.log': (level: number, msg: number) => {
@@ -161,10 +159,10 @@ export const instantiate = async (
functionParams = await Promise.all(functionParamsPromise);
assert(context.event.block);
assert(context.block);
// TODO: Check for function overloading.
let result = await contract[functionName](...functionParams, { blockTag: context.event.block.blockHash });
let result = await contract[functionName](...functionParams, { blockTag: context.block.blockHash });
// Using function signature does not work.
const { outputs } = contract.interface.getFunction(functionName);
+19 -11
View File
@@ -26,14 +26,19 @@ export const DECIMAL128_PMIN = '1e-6143';
// Maximum -ve decimal value.
export const DECIMAL128_NMAX = '-1e-6143';
interface Transaction {
export interface Transaction {
hash: string;
index: number;
from: string;
to: string;
value: string;
gasLimit: string;
gasPrice: string;
input: string;
}
export interface Block {
headerId: number;
blockHash: string;
blockNumber: string;
timestamp: string;
@@ -231,16 +236,19 @@ export const createEvent = async (instanceExports: any, contractAddress: string,
const txToStringPtr = await __newString(tx.to);
const txTo = tx.to && await Address.fromString(txToStringPtr);
const txValuePtr = await BigInt.fromI32(0);
const txGasLimitPtr = await BigInt.fromI32(0);
const txGasPricePtr = await BigInt.fromI32(0);
const txinputPtr = await Bytes.empty();
const valueStringPtr = await __newString(tx.value);
const txValuePtr = await BigInt.fromString(valueStringPtr);
const gasLimitStringPtr = await __newString(tx.gasLimit);
const txGasLimitPtr = await BigInt.fromString(gasLimitStringPtr);
const gasPriceStringPtr = await __newString(tx.gasPrice);
const txGasPricePtr = await BigInt.fromString(gasPriceStringPtr);
const inputStringPtr = await __newString(tx.input);
const txInputByteArray = await ByteArray.fromHexString(inputStringPtr);
const txInputPtr = await Bytes.fromByteArray(txInputByteArray);
// Missing fields from watcher in transaction data:
// value
// gasLimit
// gasPrice
// input
const transaction = await ethereum.Transaction.__new(
txHash,
txIndex,
@@ -249,7 +257,7 @@ export const createEvent = async (instanceExports: any, contractAddress: string,
txValuePtr,
txGasLimitPtr,
txGasPricePtr,
txinputPtr
txInputPtr
);
const eventParamArrayPromise = inputs.map(async input => {
+30 -10
View File
@@ -11,9 +11,9 @@ import { ContractInterface, utils, providers } from 'ethers';
import { ResultObject } from '@vulcanize/assemblyscript/lib/loader';
import { EthClient } from '@vulcanize/ipld-eth-client';
import { IndexerInterface, getFullBlock, BlockHeight, ServerConfig } from '@vulcanize/util';
import { IndexerInterface, getFullBlock, BlockHeight, ServerConfig, getFullTransaction } from '@vulcanize/util';
import { createBlock, createEvent, getSubgraphConfig, resolveEntityFieldConflicts } from './utils';
import { createBlock, createEvent, getSubgraphConfig, resolveEntityFieldConflicts, Transaction } from './utils';
import { Context, GraphData, instantiate } from './loader';
import { Database } from './database';
@@ -34,10 +34,9 @@ export class GraphWatcher {
_wasmRestartBlocksInterval: number;
_dataSources: any[] = [];
_dataSourceMap: { [key: string]: DataSource } = {};
_transactionsMap: Map<string, Transaction> = new Map()
_context: Context = {
event: {}
}
_context: Context = {}
constructor (database: Database, postgraphileClient: EthClient, ethProvider: providers.BaseProvider, serverConfig: ServerConfig) {
this._database = database;
@@ -119,12 +118,14 @@ export class GraphWatcher {
}
async handleEvent (eventData: any) {
const { contract, event, eventSignature, block, tx, eventIndex } = eventData;
const { contract, event, eventSignature, block, tx: { hash: txHash }, eventIndex } = eventData;
// TODO: Use blockData fetched in handleBlock.
const blockData = await getFullBlock(this._postgraphileClient, this._ethProvider, block.hash);
if (!this._context.block) {
this._context.block = await getFullBlock(this._postgraphileClient, this._ethProvider, block.hash);
}
this._context.event.block = blockData;
const blockData = this._context.block;
assert(blockData);
// Get dataSource in subgraph yaml based on contract address.
const dataSource = this._dataSources.find(dataSource => dataSource.source.address === contract);
@@ -157,6 +158,8 @@ export class GraphWatcher {
const eventFragment = contractInterface.getEvent(eventSignature);
const tx = await this._getTransactionData(blockData.headerId, txHash);
const data = {
block: blockData,
inputs: eventFragment.inputs,
@@ -174,7 +177,10 @@ export class GraphWatcher {
async handleBlock (blockHash: string) {
const blockData = await getFullBlock(this._postgraphileClient, this._ethProvider, blockHash);
this._context.event.block = blockData;
this._context.block = blockData;
// Clear transactions map on handling new block.
this._transactionsMap.clear();
// Call block handler(s) for each contract.
for (const dataSource of this._dataSources) {
@@ -263,4 +269,18 @@ export class GraphWatcher {
throw error;
}
}
async _getTransactionData (headerId: number, txHash: string): Promise<Transaction> {
let transaction = this._transactionsMap.get(txHash);
if (transaction) {
return transaction;
}
transaction = await getFullTransaction(this._postgraphileClient, headerId, txHash);
assert(transaction);
this._transactionsMap.set(txHash, transaction);
return transaction;
}
}