Skip to content

Commit

Permalink
refactor: better error handling when parsing events (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
nvtaveras authored and chapati23 committed Aug 6, 2024
1 parent 21f715c commit 2353b4d
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 42 deletions.
12 changes: 9 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import assert from "assert/strict";

// Types
import type {
HttpFunction,
Request,
Response,
} from "@google-cloud/functions-framework";
import { EventType } from "./types.js";
import parseTransactionReceipts from "./parse-transaction-receipts";
import sendDiscordNotification from "./send-discord-notification";
import sendTelegramNotification from "./send-telegram-notification";
Expand All @@ -17,16 +20,19 @@ export const watchdogNotifier: HttpFunction = async (

for (const parsedEvent of parsedEvents) {
switch (parsedEvent.event.eventName) {
case "ProposalCreated":
case EventType.ProposalCreated:
await sendDiscordNotification(parsedEvent.event, parsedEvent.txHash);
await sendTelegramNotification(parsedEvent.event, parsedEvent.txHash);
break;
case "MedianUpdated":
case EventType.MedianUpdated:
// Acts a health check/heartbeat for the service, as it's a frequently emitted event
console.info("[HealthCheck]: Block", parsedEvent.block);
break;
default:
throw new Error("Unknown event type", parsedEvent.event);
assert(
false,
`Unknown event type from payload: ${JSON.stringify(req.body)}`,
);
}
}

Expand Down
88 changes: 51 additions & 37 deletions src/parse-transaction-receipts.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import assert from "assert/strict";

// External
import { decodeEventLog } from "viem";

// Internal
import GovernorABI from "./governor-abi.js";
import { HealthCheckEvent, ProposalCreatedEvent } from "./types.js";
import { EventType, HealthCheckEvent, ProposalCreatedEvent } from "./types.js";
import hasLogs from "./utils/has-logs.js";
import getEventByTopic from "./utils/get-event-by-topic.js";
import isHealthCheckEvent from "./utils/is-health-check-event.js";
import isProposalCreatedEvent from "./utils/is-proposal-created-event.js";
import isTransactionReceipt from "./utils/is-transaction-receipt.js";
Expand Down Expand Up @@ -45,48 +48,59 @@ export default function parseTransactionReceipts(
}

for (const log of receipt.logs) {
if (!log.topics) {
throw new Error("No topics found in log");
}
assert(log.topics && log.topics.length > 0, "No topics found in log");

try {
const event = decodeEventLog({
abi: GovernorABI,
data: log.data as `0x${string}`,
topics: log.topics as [
signature: `0x${string}`,
...args: `0x${string}`[],
],
});
const eventSignature = log.topics[0];
const eventType = getEventByTopic(eventSignature);

if (isProposalCreatedEvent(event)) {
result.push({
event,
txHash: log.transactionHash,
switch (eventType) {
case EventType.Unknown:
// It can happen that a single transaction fires multiple events,
// some of which we are not interested in
continue;
case EventType.ProposalCreated: {
const event = decodeEventLog({
abi: GovernorABI,
data: log.data as `0x${string}`,
topics: log.topics as [
signature: `0x${string}`,
...args: `0x${string}`[],
],
});
}
// eslint-disable-next-line no-empty
} catch {}

try {
const event = decodeEventLog({
abi: SortedOraclesABI,
data: log.data as `0x${string}`,
topics: log.topics as [
signature: `0x${string}`,
...args: `0x${string}`[],
],
});

if (isHealthCheckEvent(event)) {
result.push({
block: Number(receipt.blockNumber),
event,
txHash: log.transactionHash,
if (isProposalCreatedEvent(event)) {
result.push({
event,
txHash: log.transactionHash,
});
}
break;
}
case EventType.MedianUpdated: {
const event = decodeEventLog({
abi: SortedOraclesABI,
data: log.data as `0x${string}`,
topics: log.topics as [
signature: `0x${string}`,
...args: `0x${string}`[],
],
});

if (isHealthCheckEvent(event)) {
result.push({
block: Number(receipt.blockNumber),
event,
txHash: log.transactionHash,
});
}
break;
}
// eslint-disable-next-line no-empty
} catch {}
default:
assert(
false,
`Unknown event type. Did you forget to add a new event?`,
);
}
}
}

Expand Down
10 changes: 8 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,14 @@ export interface LogsEntity {
transactionIndex: string;
}

export enum EventType {
Unknown = "Unknown",
ProposalCreated = "ProposalCreated",
MedianUpdated = "MedianUpdated",
}

export interface ProposalCreatedEvent {
eventName: "ProposalCreated";
eventName: EventType.ProposalCreated;
args: {
calldatas: readonly `0x${string}`[];
description: string;
Expand All @@ -43,7 +49,7 @@ export interface ProposalCreatedEvent {
}

export interface HealthCheckEvent {
eventName: "MedianUpdated";
eventName: EventType.MedianUpdated;
block: number;
args: {
token: `0x${string}`;
Expand Down
14 changes: 14 additions & 0 deletions src/utils/get-event-by-topic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { EventType } from "../types";

export default function getEventByTopic(topic: string): EventType {
switch (topic) {
// keccak256(abi.encodePacked("ProposalCreated(uint256,address,address[],uint256[],string[],bytes[],uint256,uint256,string)"))
case "0x7d84a6263ae0d98d3329bd7b46bb4e8d6f98cd35a7adb45c274c8b7fd5ebd5e0":
return EventType.ProposalCreated;
// keccak256(abi.encodePacked("MedianUpdated(address,uint256)"))
case "0xa9981ebfc3b766a742486e898f54959b050a66006dbce1a4155c1f84a08bcf41":
return EventType.MedianUpdated;
default:
return EventType.Unknown;
}
}

0 comments on commit 2353b4d

Please sign in to comment.