Skip to content

Commit

Permalink
refactor: modular architecture (#141)
Browse files Browse the repository at this point in the history
# Description

This PR refactors legacy locations of code to more accurately reflect
the area of responsibility for the respective code.

# Changes

- [x] Any core "service" that is running is located within the
"services" module.
- [x] Domain logic is split related to indexing ("events"), and polling
("polling")
- [x] Filtering that has been applied is made more meaningful and moved
to a "filtering" module

## How to test
1. Verify that `yarn test` passes
2. Run from genesis on `mainnet` and verify the block watcher comes
online (passes `warmUp`).
  • Loading branch information
mfw78 authored Jan 25, 2024
1 parent b9bf2a9 commit d714c38
Show file tree
Hide file tree
Showing 16 changed files with 48 additions and 36 deletions.
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) {
// 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";

0 comments on commit d714c38

Please sign in to comment.