Skip to content

Commit

Permalink
feat: support multi/single network run
Browse files Browse the repository at this point in the history
  • Loading branch information
anxolin committed Nov 21, 2023
1 parent 361504f commit 4a1e087
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 80 deletions.
2 changes: 2 additions & 0 deletions src/commands/runMulti.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export async function runMulti(options: RunMultiOptions) {
deploymentBlocks,
watchdogTimeouts,
orderBookApis,
filterPolicyConfigFiles,
oneShot,
disableApi,
apiPort,
Expand Down Expand Up @@ -51,6 +52,7 @@ export async function runMulti(options: RunMultiOptions) {
deploymentBlock: deploymentBlocks[index],
watchdogTimeout: watchdogTimeouts[index],
orderBookApi: orderBookApis[index],
filterPolicyConfig: filterPolicyConfigFiles[index],
},
storage
);
Expand Down
39 changes: 23 additions & 16 deletions src/domain/chainContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,7 @@ import {
reorgsTotal,
} from "../utils/metrics";
import { hexZeroPad } from "ethers/lib/utils";
import {
FilterPolicy,
fetchPolicy as fetchFilterPolicyConfig,
} from "../utils/filterPolicy";
import { FilterPolicy } from "../utils/filterPolicy";

const WATCHDOG_FREQUENCY = 5 * 1000; // 5 seconds

Expand Down Expand Up @@ -71,6 +68,11 @@ export interface ChainWatcherHealth {
};
}

export interface FilterPolicyConfig {
baseUrl: string;
// authToken: string; // TODO: Implement authToken
}

/**
* The chain context handles watching a single chain for new conditional orders
* and executing them.
Expand All @@ -89,7 +91,7 @@ export class ChainContext {
chainId: SupportedChainId;
registry: Registry;
orderBook: OrderBookApi;
filterPolicy: FilterPolicy;
filterPolicy: FilterPolicy | undefined;
contract: ComposableCoW;
multicall: Multicall3;

Expand All @@ -106,6 +108,7 @@ export class ChainContext {
watchdogTimeout,
owners,
orderBookApi,
filterPolicyConfig,
} = options;
this.deploymentBlock = deploymentBlock;
this.pageSize = pageSize;
Expand All @@ -131,13 +134,20 @@ export class ChainContext {
},
});

this.filterPolicy = new FilterPolicy();
console.log("filterPolicyConfig", filterPolicyConfig);
this.filterPolicy = filterPolicyConfig
? new FilterPolicy({
configBaseUrl: filterPolicyConfig,
// configAuthToken: filterPolicyConfigAuthToken, // TODO: Implement authToken
})
: undefined;
this.contract = composableCowContract(this.provider, this.chainId);
this.multicall = Multicall3__factory.connect(MULTICALL3, this.provider);
}

/**
* Initialise a chain context.
* Initialize a chain context.
*
* @param options as parsed by commander from the command line arguments.
* @param storage the db singleton that provides persistence.
* @returns A chain context that is monitoring for orders on the chain.
Expand Down Expand Up @@ -460,15 +470,12 @@ async function processBlock(
block.number.toString()
);

// Get the latest filter policy
const policyConfig = await fetchFilterPolicyConfig(chainId).catch((error) => {
console.log(`Error fetching the filter policy config for chain `, error);
return null;
});

if (policyConfig) {
filterPolicy.setHandlers(policyConfig.handlers);
filterPolicy.setOwners(policyConfig.owners);
// Get the latest filter policy
if (filterPolicy) {
filterPolicy.reloadPolicies().catch((error) => {
console.log(`Error fetching the filter policy config for chain `, error);
return null;
});
}

// Transaction watcher for adding new contracts
Expand Down
59 changes: 36 additions & 23 deletions src/domain/checkForAndPlaceOrder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
PollResultCode,
PollResultErrors,
PollResultSuccess,
SigningScheme,
SupportedChainId,
formatEpoch,
} from "@cowprotocol/cow-sdk";
Expand Down Expand Up @@ -129,26 +130,28 @@ export async function checkForAndPlaceOrder(
const { result: lastHint } = conditionalOrder.pollResult || {};

// Apply filtering policy
const filterResult = filterPolicy.preFilter({
owner,
conditionalOrderParams: conditionalOrder.params,
});

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

continue;
case FilterAction.SKIP:
log.debug(
"Skipping conditional order. Reason: AcceptPolicy: SKIP",
conditionalOrder.params
);
continue;
if (filterPolicy) {
const filterResult = filterPolicy.preFilter({
owner,
conditionalOrderParams: conditionalOrder.params,
});

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

continue;
case FilterAction.SKIP:
log.debug(
"Skipping conditional order. Reason: AcceptPolicy: SKIP",
conditionalOrder.params
);
continue;
}
}

// Check if the order is due (by epoch)
Expand Down Expand Up @@ -466,16 +469,26 @@ async function _placeOrder(params: {
const { chainId } = orderBook.context;
try {
const postOrder: OrderCreation = {
...order,
kind: order.kind,
from: order.from,
sellToken: order.sellToken,
buyToken: order.buyToken,
sellAmount: order.sellAmount.toString(),
buyAmount: order.buyAmount.toString(),
receiver: order.receiver,
feeAmount: order.feeAmount.toString(),
signingScheme: "eip1271",
validTo: order.validTo,
appData: order.appData,
partiallyFillable: order.partiallyFillable,
sellTokenBalance: order.sellTokenBalance,
buyTokenBalance: order.buyTokenBalance,
signingScheme: SigningScheme.EIP1271,
signature: order.signature,
};

// If the operation is a dry run, don't post to the API
log.info(`Post order ${orderUid} to OrderBook on chain ${chainId}`);
log.debug(`Order`, postOrder);
log.debug(`Post order details`, postOrder);
if (!dryRun) {
const orderUid = await orderBook.sendOrder(postOrder);
orderBookDiscreteOrdersTotal.labels(...metricLabels).inc();
Expand Down
34 changes: 29 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const databasePathOption = new Option(
.default(DEFAULT_DATABASE_PATH)
.env("DATABASE_PATH");

const chainConfigHelp = `Chain configuration in the format of <rpc>,<deploymentBlock>[,<watchdogTimeout>,<orderBookApi>], e.g. http://erigon.dappnode:8545,12345678,30,https://api.cow.fi/mainnet`;
const chainConfigHelp = `Chain configuration in the format of <rpc>,<deploymentBlock>[,<watchdogTimeout>,<orderBookApi>,<filterPolicyConfig>], e.g. http://erigon.dappnode:8545,12345678,30,https://api.cow.fi/mainnet,https://raw.githubusercontent.com/cowprotocol/watch-tower/config/filter-policy-1.json`;
const multiChainConfigOption = new Option(
"--chain-config <chainConfig...>",
chainConfigHelp
Expand Down Expand Up @@ -231,7 +231,7 @@ function parseChainConfigOption(option: string): ChainConfigOptions {
// Ensure there are at least two parts (rpc and deploymentBlock)
if (parts.length < 2) {
throw new InvalidArgumentError(
`Chain configuration must be in the format of <rpc>,<deploymentBlock>[,<watchdogTimeout>,<orderBookApi>], e.g. http://erigon.dappnode:8545,12345678,30,https://api.cow.fi/mainnet`
`Chain configuration must be in the format of <rpc>,<deploymentBlock>[,<watchdogTimeout>,<orderBookApi>,<filterPolicyConfig>], e.g. http://erigon.dappnode:8545,12345678,30,https://api.cow.fi/mainnet,https://raw.githubusercontent.com/cowprotocol/watch-tower/config/filter-policy-1.json`
);
}

Expand Down Expand Up @@ -264,15 +264,31 @@ function parseChainConfigOption(option: string): ChainConfigOptions {
}

// If there is a fourth part, it is the orderBookApi
const orderBookApi = parts.length > 3 ? parts[3] : undefined;
const orderBookApi = parts.length > 3 && parts[3] ? parts[3] : undefined;
// Ensure that the orderBookApi is a valid URL
if (orderBookApi && !isValidUrl(orderBookApi)) {
throw new InvalidArgumentError(
`${orderBookApi} must be a valid URL (orderBookApi)`
);
}

return { rpc, deploymentBlock, watchdogTimeout, orderBookApi };
// If there is a fifth part, it is the filterPolicyConfig
const filterPolicyConfig =
parts.length > 4 && parts[4] ? parts[4] : undefined;
// Ensure that the orderBookApi is a valid URL
if (filterPolicyConfig && !isValidUrl(filterPolicyConfig)) {
throw new InvalidArgumentError(
`${filterPolicyConfig} must be a valid URL (orderBookApi)`
);
}

return {
rpc,
deploymentBlock,
watchdogTimeout,
orderBookApi,
filterPolicyConfig,
};
}

function parseChainConfigOptions(
Expand All @@ -282,15 +298,23 @@ function parseChainConfigOptions(
deploymentBlocks: [],
watchdogTimeouts: [],
orderBookApis: [],
filterPolicyConfigFiles: [],
}
): MultiChainConfigOptions {
const parsedOption = parseChainConfigOption(option);
const { rpc, deploymentBlock, watchdogTimeout, orderBookApi } = parsedOption;
const {
rpc,
deploymentBlock,
watchdogTimeout,
orderBookApi,
filterPolicyConfig,
} = parsedOption;

previous.rpcs.push(rpc);
previous.deploymentBlocks.push(deploymentBlock);
previous.watchdogTimeouts.push(watchdogTimeout);
previous.orderBookApis.push(orderBookApi);
previous.filterPolicyConfigFiles.push(filterPolicyConfig);
return previous;
}

Expand Down
7 changes: 5 additions & 2 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,17 @@ export type ChainConfigOptions = {
rpc: string;
deploymentBlock: number;
watchdogTimeout: number;
orderBookApi: OrderBookApi;
orderBookApi?: string;
filterPolicyConfig?: string;
// filterPolicyConfigAuthToken?: string; // TODO: Implement authToken
};

export type MultiChainConfigOptions = {
rpcs: string[];
deploymentBlocks: number[];
watchdogTimeouts: number[];
orderBookApis: OrderBookApi[];
orderBookApis: (string | undefined)[];
filterPolicyConfigFiles: (string | undefined)[];
};

export type RunSingleOptions = RunOptions & ChainConfigOptions;
Expand Down
Loading

0 comments on commit 4a1e087

Please sign in to comment.