2021-10-28 11:01:56 +00:00
//
// Copyright 2021 Vulcanize, Inc.
//
import 'reflect-metadata' ;
import debug from 'debug' ;
import path from 'path' ;
import fs from 'fs' ;
2021-11-01 09:43:22 +00:00
import { ContractInterface , utils } from 'ethers' ;
2021-10-28 11:01:56 +00:00
import { ResultObject } from '@vulcanize/assemblyscript/lib/loader' ;
2021-11-01 09:43:22 +00:00
import { createEvent , getSubgraphConfig } from './utils' ;
import { instantiate } from './loader' ;
2021-10-28 11:01:56 +00:00
const log = debug ( 'vulcanize:graph-watcher' ) ;
2021-11-01 09:43:22 +00:00
interface DataSource {
instance : ResultObject & { exports : any } ,
contractInterface : utils.Interface
}
2021-10-28 11:01:56 +00:00
export class GraphWatcher {
_subgraphPath : string ;
2021-11-01 09:43:22 +00:00
_dataSources : any [ ] = [ ] ;
_dataSourceMap : { [ key : string ] : DataSource } = { } ;
2021-10-28 11:01:56 +00:00
constructor ( subgraphPath : string ) {
this . _subgraphPath = subgraphPath ;
}
async init ( ) {
const { dataSources } = await getSubgraphConfig ( this . _subgraphPath ) ;
this . _dataSources = dataSources ;
2021-11-01 09:43:22 +00:00
// Create wasm instance and contract interface for each dataSource in subgraph yaml.
const dataPromises = this . _dataSources . map ( async ( dataSource : any ) = > {
const { source : { address , abi } , mapping } = dataSource ;
2021-10-28 11:01:56 +00:00
const { abis , file } = mapping ;
2021-11-01 09:43:22 +00:00
const abisMap = abis . reduce ( ( acc : { [ key : string ] : ContractInterface } , abi : any ) = > {
const { name , file } = abi ;
const abiFilePath = path . join ( this . _subgraphPath , file ) ;
acc [ name ] = JSON . parse ( fs . readFileSync ( abiFilePath ) . toString ( ) ) ;
return acc ;
} , { } ) ;
const contractInterface = new utils . Interface ( abisMap [ abi ] ) ;
2021-10-28 11:01:56 +00:00
const data = {
2021-11-01 09:43:22 +00:00
abis : abisMap ,
2021-10-28 11:01:56 +00:00
dataSource : {
address
}
} ;
const filePath = path . join ( this . _subgraphPath , file ) ;
2021-11-01 09:43:22 +00:00
return {
instance : await instantiate ( filePath , data ) ,
contractInterface
} ;
} , { } ) ;
const data = await Promise . all ( dataPromises ) ;
// Create a map from dataSource contract address to instance and contract interface.
this . _dataSourceMap = this . _dataSources . reduce ( ( acc : { [ key : string ] : DataSource } , dataSource : any , index : number ) = > {
const { instance } = data [ index ] ;
// Important to call _start for built subgraphs on instantiation!
// TODO: Check api version https://github.com/graphprotocol/graph-node/blob/6098daa8955bdfac597cec87080af5449807e874/runtime/wasm/src/module/mod.rs#L533
instance . exports . _start ( ) ;
const { source : { address } } = dataSource ;
acc [ address ] = data [ index ] ;
2021-10-28 11:01:56 +00:00
return acc ;
} , { } ) ;
}
async handleEvent ( eventData : any ) {
2021-11-01 09:43:22 +00:00
const { contract , event , eventSignature , block , tx , eventIndex } = eventData ;
2021-10-28 11:01:56 +00:00
2021-11-01 09:43:22 +00:00
// Get dataSource in subgraph yaml based on contract address.
2021-10-28 11:01:56 +00:00
const dataSource = this . _dataSources . find ( dataSource = > dataSource . source . address === contract ) ;
if ( ! dataSource ) {
log ( ` Subgraph doesnt have configuration for contract ${ contract } ` ) ;
return ;
}
2021-11-01 09:43:22 +00:00
// Get event handler based on event signature.
const eventHandler = dataSource . mapping . eventHandlers . find ( ( eventHandler : any ) = > eventHandler . event === eventSignature ) ;
if ( ! eventHandler ) {
log ( ` No handler configured in subgraph for event ${ eventSignature } ` ) ;
return ;
}
const { instance : { exports } , contractInterface } = this . _dataSourceMap [ contract ] ;
const eventFragment = contractInterface . getEvent ( eventSignature ) ;
const eventParams = eventFragment . inputs . map ( ( input ) = > {
return {
name : input.name ,
value : event [ input . name ] ,
kind : input.type
} ;
} ) ;
2021-10-28 11:01:56 +00:00
2021-11-01 09:43:22 +00:00
const data = {
eventParams : eventParams ,
block ,
tx ,
eventIndex
} ;
2021-10-28 11:01:56 +00:00
2021-11-01 09:43:22 +00:00
// Create ethereum event to be passed to the wasm event handler.
const ethereumEvent = await createEvent ( exports , contract , data ) ;
2021-10-28 11:01:56 +00:00
2021-11-01 09:43:22 +00:00
await exports [ eventHandler . handler ] ( ethereumEvent ) ;
2021-10-28 11:01:56 +00:00
}
}