Skip to content

Commit

Permalink
Do not fail on intermediate errors, but fail at the end if there was …
Browse files Browse the repository at this point in the history
…any errors
  • Loading branch information
anxolin committed Aug 11, 2023
1 parent 106f80b commit 012b8fe
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 24 deletions.
54 changes: 45 additions & 9 deletions actions/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@ import {
Event,
TransactionEvent,
Storage,
Log,
} from "@tenderly/actions";
import { BytesLike, ethers } from "ethers";

import type { ComposableCoW, IConditionalOrder } from "./types/ComposableCoW";
import type {
ComposableCoW,
ComposableCoWInterface,
IConditionalOrder,
} from "./types/ComposableCoW";
import { ComposableCoW__factory } from "./types/factories/ComposableCoW__factory";
import { writeRegistry } from "./utils";

/**
* Listens to these events on the `ComposableCoW` contract:
Expand All @@ -19,25 +25,48 @@ import { ComposableCoW__factory } from "./types/factories/ComposableCoW__factory
*/
export const addContract: ActionFn = async (context: Context, event: Event) => {
const transactionEvent = event as TransactionEvent;
const iface = ComposableCoW__factory.createInterface();
const composableCow = ComposableCoW__factory.createInterface();

// Load the registry
const registry = await Registry.load(context, transactionEvent.network);

// Process the logs
let hasErrors = false;
transactionEvent.logs.forEach((log) => {
const { error } = _addContract(log, composableCow, registry);
hasErrors ||= error;
});

hasErrors ||= await writeRegistry(registry);

// Notify error
if (hasErrors) {
// TODO notify error to slack

throw Error("Error while processing orders");
}
};

export function _addContract(
log: Log,
composableCow: ComposableCoWInterface,
registry: Registry
): { error: boolean } {
try {
// Check if the log is a ConditionalOrderCreated event
if (log.topics[0] === iface.getEventTopic("ConditionalOrderCreated")) {
const [owner, params] = iface.decodeEventLog(
if (
log.topics[0] === composableCow.getEventTopic("ConditionalOrderCreated")
) {
const [owner, params] = composableCow.decodeEventLog(
"ConditionalOrderCreated",
log.data,
log.topics
) as [string, IConditionalOrder.ConditionalOrderParamsStruct];

// Attempt to add the conditional order to the registry
add(owner, params, null, log.address, registry);
} else if (log.topics[0] == iface.getEventTopic("MerkleRootSet")) {
const [owner, root, proof] = iface.decodeEventLog(
} else if (log.topics[0] == composableCow.getEventTopic("MerkleRootSet")) {
const [owner, root, proof] = composableCow.decodeEventLog(
"MerkleRootSet",
log.data,
log.topics
Expand Down Expand Up @@ -73,9 +102,16 @@ export const addContract: ActionFn = async (context: Context, event: Event) => {
});
}
}
});
await registry.write();
};
} catch (error) {
console.error(
"[addContract] Error handling ConditionalOrderCreated/MerkleRootSet event",
error
);
return { error: true };
}

return { error: false };
}

/**
* Attempt to add an owner's conditional order to the registry
Expand Down
18 changes: 17 additions & 1 deletion actions/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import assert = require("assert");

import { ethers } from "ethers";
import { ConnectionInfo, Logger } from "ethers/lib/utils";
import { OrderStatus } from "./register";
import { OrderStatus, Registry } from "./register";

async function getSecret(key: string, context: Context): Promise<string> {
const value = await context.secrets.get(key);
Expand Down Expand Up @@ -70,3 +70,19 @@ export function formatStatus(status: OrderStatus) {
return `UNKNOWN (${status})`;
}
}

function handlePromiseErrors<T>(
errorMessage: string,
promise: Promise<T>
): Promise<boolean> {
return promise
.then(() => true)
.catch((error) => {
console.error(errorMessage, error);
return false;
});
}

export function writeRegistry(registry: Registry): Promise<boolean> {
return handlePromiseErrors("Error writing registry", registry.write());
}
69 changes: 55 additions & 14 deletions actions/watch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
BlockEvent,
Context,
Event,
Log,
TransactionEvent,
} from "@tenderly/actions";
import {
Expand All @@ -22,7 +23,8 @@ import {
} from "./types";
import { Registry, OrderStatus, ConditionalOrder } from "./register";
import { BytesLike, Logger } from "ethers/lib/utils";
import { apiUrl, formatStatus, getProvider } from "./utils";
import { apiUrl, formatStatus, getProvider, writeRegistry } from "./utils";
import { GPv2SettlementInterface } from "./types/GPv2Settlement";

const GPV2SETTLEMENT = "0x9008D19f58AAbD9eD0D60971565AA8510560ab41";

Expand All @@ -36,13 +38,35 @@ export const checkForSettlement: ActionFn = async (
event: Event
) => {
const transactionEvent = event as TransactionEvent;
const iface = GPv2Settlement__factory.createInterface();
const settlement = GPv2Settlement__factory.createInterface();

const registry = await Registry.load(context, transactionEvent.network);

transactionEvent.logs.forEach((log) => {
if (log.topics[0] === iface.getEventTopic("Trade")) {
const [owner, , , , , , orderUid] = iface.decodeEventLog(
let hasErrors = false;
transactionEvent.logs.forEach(async (log) => {
const { error } = await _checkForSettlement(log, settlement, registry);
hasErrors ||= error;
});

// Update the registry
hasErrors ||= await writeRegistry(registry);

// Notify error
if (hasErrors) {
// TODO notify error to slack

throw Error("Error while processing orders");
}
};

async function _checkForSettlement(
log: Log,
settlement: GPv2SettlementInterface,
registry: Registry
): Promise<{ error: boolean }> {
try {
if (log.topics[0] === settlement.getEventTopic("Trade")) {
const [owner, , , , , , orderUid] = settlement.decodeEventLog(
"Trade",
log.data,
log.topics
Expand All @@ -63,9 +87,14 @@ export const checkForSettlement: ActionFn = async (
});
}
}
});
await registry.write();
};
} catch (error: any) {
console.error("Error checking for settlement", error);

return { error: true };
}

return { error: false };
}

async function getTradeableOrderWithSignature(
owner: string,
Expand Down Expand Up @@ -100,6 +129,7 @@ export const checkForAndPlaceOrder: ActionFn = async (
const { network } = blockEvent;

// enumerate all the owners
let hasErrors = false;
for (const [owner, conditionalOrders] of registry.ownerOrders.entries()) {
const ordersPendingDelete = [];
// enumerate all the `ConditionalOrder`s for a given owner
Expand All @@ -110,14 +140,16 @@ export const checkForAndPlaceOrder: ActionFn = async (
chainContext.provider
);

const { deleteConditionalOrder } = await _checkForAndPlaceOrder(
const { deleteConditionalOrder, error } = await _checkForAndPlaceOrder(
owner,
network,
conditionalOrder,
contract,
chainContext
);

hasErrors ||= error;

if (deleteConditionalOrder) {
ordersPendingDelete.push(conditionalOrder);
}
Expand All @@ -129,7 +161,14 @@ export const checkForAndPlaceOrder: ActionFn = async (
}

// Update the registry
await registry.write();
hasErrors ||= await writeRegistry(registry);

// Notify error
if (hasErrors) {
// TODO notify error to slack

throw Error("Error while processing settlements");
}
};

async function _checkForAndPlaceOrder(
Expand All @@ -138,7 +177,8 @@ async function _checkForAndPlaceOrder(
conditionalOrder: ConditionalOrder,
contract: ComposableCoW,
chainContext: ChainContext
): Promise<{ deleteConditionalOrder: boolean }> {
): Promise<{ deleteConditionalOrder: boolean; error: boolean }> {
let error = false;
try {
const { order, signature } = await getTradeableOrderWithSignature(
owner,
Expand Down Expand Up @@ -174,12 +214,13 @@ async function _checkForAndPlaceOrder(
);
}
} catch (e: any) {
error = true;
if (e.code === Logger.errors.CALL_EXCEPTION) {
switch (e.errorName) {
case "OrderNotValid":
// The conditional order has not expired, or been cancelled, but the order is not valid
// For example, with TWAPs, this may be after `span` seconds have passed in the epoch.
return { deleteConditionalOrder: false };
return { deleteConditionalOrder: false, error };
case "SingleOrderNotAuthed":
console.log(
`Single order on safe ${owner} not authed. Unfilled orders:`
Expand All @@ -189,12 +230,12 @@ async function _checkForAndPlaceOrder(
}
printUnfilledOrders(conditionalOrder.orders);
console.log("Removing conditional order from registry");
return { deleteConditionalOrder: true };
return { deleteConditionalOrder: true, error };
}
console.error(`Unexpected error while processing order: ${e?.message}`);
}

return { deleteConditionalOrder: false };
return { deleteConditionalOrder: false, error };
}

function getOrderUid(network: string, orderToSubmit: Order, owner: string) {
Expand Down

0 comments on commit 012b8fe

Please sign in to comment.