This repository has been archived by the owner on Jul 8, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 212
/
base-event-handler.ts
127 lines (105 loc) · 4.15 KB
/
base-event-handler.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/**
* Base Event Handler
*
* This will iterate through a range of block_heights and then run a callback to process any events we
* are interested in. It also keeps a cursor in the database so we can resume from where we left off at any time.
*
*/
import * as fcl from "@onflow/fcl";
import { BlockCursorService } from "../services/block-cursor";
import { FlowService } from "../services/flow";
abstract class BaseEventHandler {
private stepTimeMs: number = 1000;
private stepSize: number = 200;
protected constructor(
private readonly blockCursorService: BlockCursorService,
private readonly flowService: FlowService,
public eventNames: string[]
) {}
async run() {
console.log("start event polling")
const poll = async () => {
let lastestBlockHeight = await this.flowService.getLatestBlockHeight();
// if blockCursor does not exist in DB (1st instantiation of application), create one with lastest block height
let blockCursor = await this.blockCursorService.findOrCreateLatestBlockCursor(lastestBlockHeight);
if (!blockCursor || !blockCursor.id) {
throw new Error("Could not get block cursor due to database error.");
}
// currentBlockHeight in the DB will equal the toBlock of the previous blockrange fcl query.
// increment fromBlock by 1 for next window.
let fromBlock = blockCursor.currentBlockHeight + 1;
let toBlock = await this.flowService.getLatestBlockHeight();
// on 1st iteration, fromBlock will be greater than toBlock due to newly inserted row.
if (fromBlock > toBlock) {
fromBlock = toBlock;
}
// getEventsAtBlockHeightRange() has a block limit of 250 blocks.
// If the range exceeds the limit of 200 then hard cap the block search range
if (fromBlock + this.stepSize < toBlock) {
toBlock = fromBlock + this.stepSize;
}
console.log(`Checking block range: fromBlock=${fromBlock} toBlock=${toBlock}`);
// iterate over eventNames and fetch events for block range
let events: any[] = [];
for (const eventName of this.eventNames) {
try {
const result = await fcl.send([
fcl.getEventsAtBlockHeightRange(eventName, fromBlock, toBlock)
]);
const decoded: [] = await fcl.decode(result);
if (decoded.length > 0) {
events.push(...decoded);
}
} catch (e) {
console.error(
`Error retrieving events for block range fromBlock=${fromBlock} toBlock=${toBlock}`,
e
);
}
}
if (events.length > 0) {
console.log(`Found ${events.length} events in block range`);
events.sort((event1, event2) => {
// order events by block height ascending
if (event1.blockHeight > event2.blockHeight) {
return 1;
} else if (event1.blockHeight < event2.blockHeight) {
return -1;
}
// if events are on the same block, order by transaction index
if (event1.transactionIndex > event2.transactionIndex) {
return 1;
} else if (event1.transactionIndex < event2.transactionIndex) {
return -1;
}
// if events are on the same transaction, order by event index
if (event1.eventIndex > event2.eventIndex) {
return 1;
} else if (event1.eventIndex < event2.eventIndex) {
return -1;
}
return 0;
});
// update database in order of events
for (const event of events) {
try {
await this.onEvent(event);
} catch (e) {
console.error(`Encountered Error processing event: ${JSON.stringify(event)}`, e);
}
}
}
// Record the last block queried
blockCursor = await this.blockCursorService.updateBlockCursorById(
blockCursor.id,
toBlock
);
// recursively call self to continue polling
setTimeout(poll, this.stepTimeMs);
}
// execute polling method.
poll();
};
abstract onEvent(event: any): Promise<void>;
}
export { BaseEventHandler };