@@ -53,6 +53,23 @@ async function getRPCLatestBlock(url) {
53
53
}
54
54
}
55
55
56
+ async function getBlockIngestorLatestBlock ( url ) {
57
+ const res = await fetch ( url , {
58
+ method : 'POST' ,
59
+ headers : {
60
+ 'Content-Type' : 'application/json' ,
61
+ } ,
62
+ body : JSON . stringify ( {
63
+ "query" : "query test {\n getIngestorStats(id: \"STATS\") {\n value\n }\n}\n" ,
64
+ "variables" : { } ,
65
+ "operationName" : "test"
66
+ } )
67
+ } ) ;
68
+ const output = await res . json ( )
69
+ const stats = JSON . parse ( output . data . getIngestorStats . value ) ;
70
+ return parseInt ( stats . lastBlockNumber , 10 ) ;
71
+ }
72
+
56
73
async function getBlockscoutLatestBlock ( ) {
57
74
try {
58
75
const blockScoutBlock = await fetch ( "https://blockscout.com/xdai/mainnet/api?module=block&action=eth_block_number" )
@@ -64,6 +81,17 @@ async function getBlockscoutLatestBlock() {
64
81
}
65
82
}
66
83
84
+ async function getArbiscanLatestBlock ( ) {
85
+ try {
86
+ const arbiscanBlock = await fetch ( `https://api.arbiscan.io/api?module=proxy&action=eth_blockNumber&apikey=${ process . env . ARBISCAN_API_KEY } ` )
87
+ const output = await arbiscanBlock . json ( )
88
+ let arbiscanLatestBlock = parseInt ( output . result , 16 )
89
+ return arbiscanLatestBlock ;
90
+ } catch ( err ) {
91
+ return NaN ;
92
+ }
93
+ }
94
+
67
95
async function getBalance ( account , url ) {
68
96
try {
69
97
const balanceRes = await fetch ( url , {
@@ -74,7 +102,7 @@ async function getBalance(account, url) {
74
102
body : JSON . stringify ( {
75
103
"jsonrpc" :"2.0" ,
76
104
"method" :"eth_getBalance" ,
77
- "params" :[ account ] ,
105
+ "params" :[ account , "latest" ] ,
78
106
"id" :1
79
107
} )
80
108
} )
@@ -112,8 +140,8 @@ module.exports = robot => {
112
140
return "🔴" ;
113
141
}
114
142
115
- async function getMessage ( ) {
116
- let message = ""
143
+ async function getMessageGnosis ( ) {
144
+ let message = "**On Gnosis:**\n "
117
145
// Get latest block from graph
118
146
const graphNumberRes = getGraphLatestBlock ( "https://xdai.colony.io/graph/subgraphs/name/joinColony/subgraph" )
119
147
@@ -250,6 +278,87 @@ module.exports = robot => {
250
278
return message
251
279
}
252
280
281
+ async function getArbitrumMessage ( ) {
282
+
283
+ const ARBITRUM_MINER_ADDRESS = "0xd090822a84e037Acc8a169C54a5943FF9fB82236"
284
+ const ARBITRUM_BROADCASTER_ADDRESS = "0xf4ab92A14c7CBc232E8293C59DfFbd98Fbdf9b3E"
285
+ const ARBITRUM_NETWORK_ADDRESS = "0xcccccdcc0ccf6c708d860e19353c5f9a49accccc"
286
+ const ARBITRUM_GRAPH_URL = "https://app.colony.io/auth-proxy/graphql"
287
+ const ourRPC = process . env . ARBITRUM_RPC
288
+ const publicRPC = process . env . ARBITRUM_PUBLIC_RPC
289
+
290
+
291
+ // Get latest block from our RPC
292
+ const ourRpcPromise = getRPCLatestBlock ( ourRPC ) ;
293
+
294
+ // Get latest block from another RPC
295
+ const publicRPCPromise = getRPCLatestBlock ( publicRPC ) ;
296
+
297
+ // Get latest block from block ingestor
298
+ const blockIngestorNumberPromise = getBlockIngestorLatestBlock ( ARBITRUM_GRAPH_URL ) ;
299
+
300
+ // Get balance of miner
301
+ const balancePromise = await getBalance ( ARBITRUM_MINER_ADDRESS , ourRPC )
302
+
303
+ // Get balance of MTX Broadcaster
304
+ const mtxBalancePromise = await getBalance ( ARBITRUM_BROADCASTER_ADDRESS , ourRPC )
305
+
306
+ const arbiscanLatestBlockPromise = getArbiscanLatestBlock ( )
307
+
308
+ let [ ourRpcBlock , publicRpcBlock , ingestorNumber , minerBalance , mtxBalance , arbiscanLatestBlock ] = await Promise . all ( [ ourRpcPromise , publicRPCPromise , blockIngestorNumberPromise , balancePromise , mtxBalancePromise , arbiscanLatestBlockPromise ] )
309
+
310
+ if ( isNaN ( arbiscanLatestBlock ) && ourRpcBlock > 0 ) { arbiscanLatestBlock = ourRpcBlock }
311
+ if ( isNaN ( publicRpcBlock ) && ourRpcBlock > 0 ) { publicRpcBlock = ourRpcBlock }
312
+
313
+ const smallestRpcDiscrepancy = Math . min (
314
+ Math . abs ( ourRpcBlock - arbiscanLatestBlock ) ,
315
+ Math . abs ( ourRpcBlock - publicRpcBlock )
316
+ )
317
+
318
+ // Get time since last mining cycle completed
319
+ // Get reputation mining cycle status
320
+ let secondsSinceOpen = - 1 ;
321
+ let nSubmitted = - 1 ;
322
+ try {
323
+ let provider ;
324
+ // Use our RPC if okay
325
+ if ( ourRpcBlock > 0 ) {
326
+ provider = new ethers . providers . JsonRpcProvider ( ourRPC )
327
+ } else {
328
+ provider = new ethers . providers . JsonRpcProvider ( publicRPC ) ;
329
+ }
330
+
331
+ const cn = new ethers . Contract ( ARBITRUM_NETWORK_ADDRESS , networkABI , provider )
332
+ const miningAddress = await cn . getReputationMiningCycle ( true ) ;
333
+
334
+ const rm = new ethers . Contract ( miningAddress , miningABI , provider ) ;
335
+ const openTimestamp = await rm . getReputationMiningWindowOpenTimestamp ( ) ;
336
+ secondsSinceOpen = Math . floor ( Date . now ( ) / 1000 ) - openTimestamp ;
337
+
338
+ nSubmitted = await rm . getNUniqueSubmittedHashes ( ) ;
339
+ } catch ( err ) {
340
+ // Use default values for anything not set
341
+ }
342
+
343
+ let message = "**On Arbitrum:**\n"
344
+ message += `Public RPC latest block: ${ publicRpcBlock } \n`
345
+ message += `Arbiscan latest block: ${ arbiscanLatestBlock } \n`
346
+ message += `${ status ( smallestRpcDiscrepancy , 60 , 120 ) } Our RPC latest block: ${ ourRpcBlock } \n`
347
+ message += `${ status ( ingestorNumber - ourRpcBlock , 25 * GRAPH_LAG_INCIDENT / 2 , 25 * GRAPH_LAG_INCIDENT ) } Our ingestor latest block: ${ ingestorNumber } \n`
348
+ message += `${ status ( - minerBalance , - 0.05 , - 0.01 ) } Miner balance (\`${ ARBITRUM_MINER_ADDRESS . slice ( 0 , 6 ) } ...${ ARBITRUM_MINER_ADDRESS . slice ( - 4 ) } \`): ${ minerBalance } \n`
349
+ message += `${ status ( - mtxBalance , - 0.1 , - 0.01 ) } Metatx broadcaster balance (\`${ ARBITRUM_BROADCASTER_ADDRESS . slice ( 0 , 6 ) } ...${ ARBITRUM_BROADCASTER_ADDRESS . slice ( - 4 ) } \`): ${ mtxBalance } \n`
350
+ message += `${ status ( secondsSinceOpen , 3600 , 4500 ) } Time since last mining cycle completed: ${ ( secondsSinceOpen / 60 ) . toFixed ( 0 ) } minutes\n`
351
+ message += `${ status ( nSubmitted , 2 , 10000 ) } ${ nSubmitted } unique submissions so far this cycle\n`
352
+ return message ;
353
+ }
354
+
355
+ async function getMessage ( ) {
356
+ const gnosisMessage = await getMessageGnosis ( ) ;
357
+ const arbitrumMessage = await getArbitrumMessage ( ) ;
358
+ return "\n" + arbitrumMessage + "\n" + gnosisMessage ;
359
+ }
360
+
361
+
253
362
robot . hear ( / ^ ! s t a t u s $ / , async ( ) => {
254
363
const message = await getMessage ( ) ;
255
364
channel . send ( message )
@@ -276,6 +385,17 @@ module.exports = robot => {
276
385
}
277
386
}
278
387
388
+ async function checkStatusArbitrum ( ) {
389
+ const message = await getArbitrumMessage ( ) ;
390
+ if ( message . indexOf ( "🔴 Our graph latest block" ) != - 1 && ! ongoingGraphIncident ) {
391
+ ongoingGraphIncident = true ;
392
+ channel . send ( "There appears to be an incident with the graph. \n" + message )
393
+ } else if ( message . indexOf ( "🔴" ) != - 1 && ! ongoingGenericIncident && ! ongoingGraphIncident ) {
394
+ ongoingGenericIncident = true ;
395
+ channel . send ( "There appears to be a generic incident. \n" + message )
396
+ }
397
+ }
398
+
279
399
const setupCronJob = ( ) => {
280
400
const job = new CronJob ( {
281
401
// Every minute
@@ -288,6 +408,20 @@ module.exports = robot => {
288
408
timeZone : 'Pacific/Niue'
289
409
} )
290
410
job . start ( )
411
+
412
+
413
+ const arbitrumJob = new CronJob ( {
414
+ // Every minute
415
+ cronTime : '00 * * * * *' ,
416
+ onTick : ( ) => {
417
+ checkStatusArbitrum ( )
418
+ } ,
419
+ start : true ,
420
+ // Last time zone of the day (UTC-11)
421
+ timeZone : 'Pacific/Niue'
422
+ } )
423
+ arbitrumJob . start ( )
424
+
291
425
}
292
426
setupCronJob ( )
293
427
}
0 commit comments