@@ -88,6 +88,9 @@ export async function alarmMissingVaas(req: any, res: any) {
88
88
// Alarm any watchers that are behind by more than 24 hours
89
89
await alarmOldBlockTimes ( refTimes ) ;
90
90
91
+ // Alarm any guardians that have not sent a heartbeat in more than 1 hour
92
+ await alarmOldHeartbeats ( ) ;
93
+
91
94
// attempting to retrieve missing VAAs...
92
95
const messages : MissingVaasByChain = await commonGetMissingVaas ( ) ;
93
96
if ( messages ) {
@@ -399,6 +402,40 @@ async function storeAlarmedChains(alarms: AlarmedChainTime[]): Promise<void> {
399
402
await alarmedChains . set ( { times : alarms } ) ;
400
403
}
401
404
405
+ async function alarmOldHeartbeats ( ) : Promise < void > {
406
+ // Get Guardian Heartbeats from firestore
407
+ const now : Date = new Date ( ) ;
408
+ const firestore = new Firestore ( ) ;
409
+ const collectionRef = firestore . collection (
410
+ assertEnvironmentVariable ( 'FIRESTORE_GUARDIAN_HEARTBEAT_COLLECTION' )
411
+ ) ;
412
+ const snapshot = await collectionRef . get ( ) ;
413
+ // Walk all the documents in the collection
414
+ const documents = snapshot . docs ;
415
+ for ( const doc of documents ) {
416
+ const data = doc . data ( ) ;
417
+ if ( data ) {
418
+ // Only need to look at the timestamp field, which is in nanoseconds
419
+ const timestamp : string = data . timestamp ;
420
+ // Convert the timestamp to a milliseconds
421
+ const timestampMs : number = Math . floor ( Number ( timestamp ) / 1_000_000 ) ;
422
+ const heartbeatTime : Date = new Date ( timestampMs ) ;
423
+ const deltaTime : number = ( now . getTime ( ) - heartbeatTime . getTime ( ) ) / ( 1000 * 60 * 60 ) ; // hours
424
+ if ( deltaTime >= 1 ) {
425
+ // Send a message to slack
426
+ const alarmSlackInfo : SlackInfo = {
427
+ channelId : assertEnvironmentVariable ( 'MISSING_VAA_SLACK_CHANNEL_ID' ) ,
428
+ postUrl : assertEnvironmentVariable ( 'MISSING_VAA_SLACK_POST_URL' ) ,
429
+ botToken : assertEnvironmentVariable ( 'MISSING_VAA_SLACK_BOT_TOKEN' ) ,
430
+ bannerTxt : 'Wormhole Missing VAA Alarm' ,
431
+ msg : `The guardian ${ doc . id } has not sent a heartbeat in ${ deltaTime } hours.` ,
432
+ } ;
433
+ await formatAndSendToSlack ( alarmSlackInfo ) ;
434
+ }
435
+ }
436
+ }
437
+ }
438
+
402
439
type FirestoreVAA = {
403
440
chain : string ;
404
441
txHash : string ;
0 commit comments