Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: modular architecture #141

Merged
merged 1 commit into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/commands/dumpDb.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { DumpDbOptions, Registry } from "../types";
import { DBService, getLogger } from "../utils";
import { DBService } from "../services";
import { getLogger } from "../utils";

/**
* Dump the database as JSON to STDOUT for a given chain ID
Expand Down
4 changes: 2 additions & 2 deletions src/commands/run.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { RunOptions } from "../types";
import { getLogger, DBService, ApiService } from "../utils";
import { ChainContext } from "../domain";
import { getLogger } from "../utils";
import { DBService, ApiService, ChainContext } from "../services";

/**
* Run the watch-tower 👀🐮
Expand Down
6 changes: 3 additions & 3 deletions src/domain/addContract.ts → src/domain/events/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
handleExecutionError,
isComposableCowCompatible,
metrics,
} from "../utils";
} from "../../utils";
import { BytesLike, ethers } from "ethers";

import {
Expand All @@ -17,9 +17,9 @@ import {
Owner,
Proof,
Registry,
} from "../types";
} from "../../types";

import { ChainContext } from "./chainContext";
import { ChainContext } from "../../services/chain";
import { ConditionalOrderParams } from "@cowprotocol/cow-sdk";

/**
Expand Down
3 changes: 2 additions & 1 deletion src/domain/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./chainContext";
export * as events from "./events";
export * as polling from "./polling";
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { Order } from "@cowprotocol/contracts";
import { BigNumber, ethers } from "ethers";

const MINIMUM_VALIDITY_SECONDS = 60;

/**
* Process an order to determine if it is valid
* @param order The GPv2.Order data struct to validate
* @throws Error if the order is invalid
*/
export function validateOrder(order: Order) {
export function check(order: Order) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would think, check should return a boolean not throw.
If you want it to throw, a better name is assertValidOrder(order)

nit: If you want to make this more modular, you can have all the assertions as independent checks. This way, is easier to test them in isolation, reuse them for different checks, or define different messages.

type OrderCheck = (order: Order) => boolean

export ZeroAmountOrderCheck: OrderCheck = ...

This way, you can use it for filtering or for asserting and is easier to maintain as you can have smaller atomic checks.

Also, allows you to define your error messages easier:

const BAD_ORDER_ERRORS: Record<OrderCheck, string> = ...

// amounts must be non-zero
if (BigNumber.from(order.sellAmount).isZero()) {
throw new Error("Order has zero sell amount");
Expand All @@ -30,8 +32,11 @@ export function validateOrder(order: Order) {
throw new Error("Order has identical sell and buy token addresses");
}

// Check to make sure that the order has at least 120s of validity
if (Math.floor(Date.now() / 1000) + 60 > Number(order.validTo)) {
// Check to make sure that the order has at least a specified validity
if (
Math.floor(Date.now() / 1000) + MINIMUM_VALIDITY_SECONDS >
Number(order.validTo)
) {
throw new Error("Order expires too soon");
}
}
2 changes: 2 additions & 0 deletions src/domain/polling/filtering/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * as badOrder from "./badOrder";
export * as policy from "./policy";
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ConditionalOrderParams } from "@cowprotocol/cow-sdk";
import { Config, FilterAction as FilterActionSchema } from "../types";
import { Config, FilterAction as FilterActionSchema } from "../../../types";

export enum FilterAction {
DROP = "DROP",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@ import {
import { ethers } from "ethers";
import { BytesLike } from "ethers/lib/utils";

import { ConditionalOrder, OrderStatus } from "../types";
import { ConditionalOrder, OrderStatus } from "../../types";
import {
formatStatus,
getLogger,
pollConditionalOrder,
handleOnChainCustomError,
metrics,
} from "../utils";
} from "../../utils";
import {
ConditionalOrder as ConditionalOrderSDK,
OrderBookApi,
Expand All @@ -30,9 +29,9 @@ import {
SupportedChainId,
formatEpoch,
} from "@cowprotocol/cow-sdk";
import { ChainContext, SDK_BACKOFF_NUM_OF_ATTEMPTS } from "./chainContext";
import { FilterAction } from "../utils/filterPolicy";
import { validateOrder } from "../utils/filterOrder";
import { ChainContext, SDK_BACKOFF_NUM_OF_ATTEMPTS } from "../../services";
import { badOrder, policy } from "./filtering";
import { pollConditionalOrder } from "./poll";

const GPV2SETTLEMENT = "0x9008D19f58AAbD9eD0D60971565AA8510560ab41";

Expand Down Expand Up @@ -125,12 +124,12 @@ export async function checkForAndPlaceOrder(
});

switch (filterResult) {
case FilterAction.DROP:
case policy.FilterAction.DROP:
log.debug("Dropping conditional order. Reason: AcceptPolicy: DROP");
ordersPendingDelete.push(conditionalOrder);

continue;
case FilterAction.SKIP:
case policy.FilterAction.SKIP:
log.debug("Skipping conditional order. Reason: AcceptPolicy: SKIP");
continue;
}
Expand Down Expand Up @@ -337,7 +336,7 @@ async function _processConditionalOrder(

// We now have the order, so we can validate it. This will throw if the order is invalid
// and we will catch it below.
validateOrder(orderToSubmit);
badOrder.check(orderToSubmit);

// calculate the orderUid
const orderUid = _getOrderUid(chainId, orderToSubmit, owner);
Expand Down
2 changes: 1 addition & 1 deletion src/utils/poll.ts → src/domain/polling/poll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
PollParams,
PollResult,
} from "@cowprotocol/cow-sdk";
import { getLogger } from "./logging";
import { getLogger } from "../../utils/logging";

// Watch-tower will index every block, so we will by default the processing block and not the latest.
const POLL_FROM_LATEST_BLOCK = false;
Expand Down
5 changes: 2 additions & 3 deletions src/utils/api.ts → src/services/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ import { Server } from "http";
import express, { Request, Response, Router } from "express";
import { Express } from "express-serve-static-core";
import * as client from "prom-client";
import { getLogger } from "./logging";
import { DBService } from "./db";
import { getLogger } from "../utils/logging";
import { DBService, ChainContext } from "../services";
import { Registry } from "../types";
import { version, name, description } from "../../package.json";
import { ChainContext } from "../domain";

export class ApiService {
protected port: number;
Expand Down
12 changes: 6 additions & 6 deletions src/domain/chainContext.ts → src/services/chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@ import {
OrderBookApi,
ApiBaseUrls,
} from "@cowprotocol/cow-sdk";
import { addContract } from "./addContract";
import { checkForAndPlaceOrder } from "./checkForAndPlaceOrder";
import { addContract } from "../domain/events";
import { checkForAndPlaceOrder } from "../domain/polling";
import { EventFilter, providers } from "ethers";
import {
composableCowContract,
DBService,
getLogger,
isRunningInKubernetesPod,
metrics,
} from "../utils";
import { DBService } from ".";
import { hexZeroPad } from "ethers/lib/utils";
import { FilterPolicy } from "../utils/filterPolicy";
import { policy } from "../domain/polling/filtering";

const WATCHDOG_FREQUENCY_SECS = 5; // 5 seconds
const WATCHDOG_TIMEOUT_DEFAULT_SECS = 30;
Expand Down Expand Up @@ -86,7 +86,7 @@ export class ChainContext {
chainId: SupportedChainId;
registry: Registry;
orderBook: OrderBookApi;
filterPolicy: FilterPolicy | undefined;
filterPolicy: policy.FilterPolicy | undefined;
contract: ComposableCoW;
multicall: Multicall3;

Expand Down Expand Up @@ -129,7 +129,7 @@ export class ChainContext {
},
});

this.filterPolicy = new FilterPolicy(filterPolicy);
this.filterPolicy = new policy.FilterPolicy(filterPolicy);
this.contract = composableCowContract(this.provider, this.chainId);
this.multicall = Multicall3__factory.connect(MULTICALL3, this.provider);
}
Expand Down
7 changes: 7 additions & 0 deletions src/services/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { DBService } from "./storage";
import { ApiService } from "./api";
import { ChainContext, SDK_BACKOFF_NUM_OF_ATTEMPTS } from "./chain";

// Exporting the `SDK_BACKOFF_NUM_OF_ATTEMPTS` is a smell that can be eliminated
// when upstream (`cow-sdk`) allows passing instantiated `OrderBookApi` for `.poll`.
export { DBService, ApiService, ChainContext, SDK_BACKOFF_NUM_OF_ATTEMPTS };
2 changes: 1 addition & 1 deletion src/utils/db.ts → src/services/storage.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { DatabaseOptions, Level } from "level";
import { getLogger } from "./logging";
import { getLogger } from "../utils/logging";

const DEFAULT_DB_LOCATION = "./database";

Expand Down
3 changes: 2 additions & 1 deletion src/types/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { BytesLike, ethers } from "ethers";

import type { ConditionalOrderCreatedEvent } from "./generated/ComposableCoW";
import { ConditionalOrderParams, PollResult } from "@cowprotocol/cow-sdk";
import { DBService, metrics } from "../utils";
import { DBService } from "../services";
import { metrics } from "../utils";

// Standardise the storage key
const LAST_NOTIFIED_ERROR_STORAGE_KEY = "LAST_NOTIFIED_ERROR";
Expand Down
2 changes: 1 addition & 1 deletion src/utils/context.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Slack = require("node-slack");
import { DBService } from "./db";
import { DBService } from "../services";

import { ContextOptions, ExecutionContext, Registry } from "../types";
import { SupportedChainId } from "@cowprotocol/cow-sdk";
Expand Down
3 changes: 0 additions & 3 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
export * from "./context";
export * from "./contracts";
export * from "./poll";
export * from "./misc";
export * from "./db";
export * from "./logging";
export * from "./api";
export * as metrics from "./metrics";
Loading