@@ -6,6 +6,7 @@ let { strOptions } = require('yaml/types')
6
6
let graphql = require ( 'graphql/language' )
7
7
let validation = require ( './validation' )
8
8
let ABI = require ( './abi' )
9
+ let asc = require ( 'assemblyscript' )
9
10
10
11
const throwCombinedError = ( filename , errors ) => {
11
12
throw new Error (
@@ -317,6 +318,76 @@ ${abiFunctions
317
318
} , immutable . List ( ) )
318
319
}
319
320
321
+ static validateBlockFunctions ( manifest , { resolveFile } ) {
322
+ return manifest
323
+ . get ( 'dataSources' )
324
+ . filter (
325
+ dataSource =>
326
+ dataSource . get ( 'kind' ) === 'ethereum/contract' &&
327
+ dataSource . getIn ( [ 'mapping' , 'blockHandlers' ] , immutable . List ( ) ) . count ( ) > 0 ,
328
+ )
329
+ . reduce ( ( errors , dataSource , dataSourceIndex ) => {
330
+ let path = [ 'dataSources' , dataSourceIndex , 'blockHandlers' ]
331
+ // Use the Assemblyscript parser to generate an AST from the mapping file
332
+ let mappingFile = dataSource . getIn ( [ 'mapping' , 'file' ] )
333
+ let mappingParser = new asc . Parser ( )
334
+ mappingParser . parseFile (
335
+ fs . readFileSync ( resolveFile ( mappingFile ) , 'utf-8' ) ,
336
+ '' ,
337
+ false ,
338
+ )
339
+
340
+ let blockHandlers = dataSource . getIn (
341
+ [ 'mapping' , 'blockHandlers' ] ,
342
+ immutable . List ( ) ,
343
+ )
344
+
345
+ // Ensure each blockHandler has a corresponding mapping handler
346
+ // with a compatible function signature
347
+ return errors . concat (
348
+ blockHandlers . reduce (
349
+ ( errors , handler , index ) =>
350
+ mappingParser . program . sources
351
+ . filter ( source => source . kind == asc . SourceKind . DEFAULT )
352
+ . some ( source =>
353
+ source . statements
354
+ . filter (
355
+ statement => statement . kind === asc . NodeKind . FUNCTIONDECLARATION ,
356
+ )
357
+ . some (
358
+ functionDeclaration =>
359
+ functionDeclaration . name . text === handler . get ( 'handler' ) &&
360
+ functionDeclaration . signature . parameters . length === 1 &&
361
+ functionDeclaration . signature . parameters [ 0 ] . name . text ==
362
+ 'block' &&
363
+ functionDeclaration . signature . parameters [ 0 ] . type . name . identifier
364
+ . text === 'ethereum' &&
365
+ functionDeclaration . signature . parameters [ 0 ] . type . name . next
366
+ . identifier . text === handler . get ( 'input' , 'Block' ) &&
367
+ functionDeclaration . signature . parameters [ 0 ] . type . name . next
368
+ . next === null &&
369
+ functionDeclaration . signature . returnType . name . identifier . text ===
370
+ 'void' ,
371
+ ) ,
372
+ )
373
+ ? errors
374
+ : errors . push (
375
+ immutable . fromJS ( {
376
+ path : [ ...path , index ] ,
377
+ message : `\
378
+ Matching mapping handler not found in '${ mappingFile } ' for blockHandler: '${ handler . get (
379
+ 'handler' ,
380
+ ) } '.
381
+ Signature:
382
+ ${ handler . get ( 'handler' ) } (block: ethereum.${ handler . get ( 'input' , 'Block' ) } ): void` ,
383
+ } ) ,
384
+ ) ,
385
+ immutable . List ( ) ,
386
+ ) ,
387
+ )
388
+ } , immutable . List ( ) )
389
+ }
390
+
320
391
static validateRepository ( manifest , { resolveFile } ) {
321
392
return manifest . get ( 'repository' ) !==
322
393
'https://github.com/graphprotocol/example-subgraph'
@@ -446,6 +517,7 @@ More than one template named '${name}', template names must be unique.`,
446
517
...Subgraph . validateEthereumContractHandlers ( manifest ) ,
447
518
...Subgraph . validateEvents ( manifest , { resolveFile } ) ,
448
519
...Subgraph . validateCallFunctions ( manifest , { resolveFile } ) ,
520
+ ...Subgraph . validateBlockFunctions ( manifest , { resolveFile } ) ,
449
521
...Subgraph . validateUniqueDataSourceNames ( manifest ) ,
450
522
...Subgraph . validateUniqueTemplateNames ( manifest ) ,
451
523
)
0 commit comments