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

feat: add orderbook API base url #106

Merged
merged 3 commits into from
Oct 18, 2023
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"dependencies": {
"@commander-js/extra-typings": "^11.0.0",
"@cowprotocol/contracts": "^1.4.0",
"@cowprotocol/cow-sdk": "^4.0.0",
"@cowprotocol/cow-sdk": "^4.0.3",
"chalk": "^4.1.2",
"ethers": "^5.7.2",
"express": "^4.18.2",
Expand Down
36 changes: 31 additions & 5 deletions src/domain/chainContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import {
RegistryBlock,
blockToRegistryBlock,
} from "../types";
import { SupportedChainId, OrderBookApi } from "@cowprotocol/cow-sdk";
import {
SupportedChainId,
OrderBookApi,
ApiBaseUrls,
} from "@cowprotocol/cow-sdk";
import { addContract } from "./addContract";
import { checkForAndPlaceOrder } from "./checkForAndPlaceOrder";
import { ethers } from "ethers";
Expand All @@ -32,6 +36,8 @@ const WATCHDOG_FREQUENCY = 5 * 1000; // 5 seconds

const MULTICALL3 = "0xcA11bde05977b3631167028862bE2a173976CA11";

export const SDK_BACKOFF_NUM_OF_ATTEMPTS = 5;

enum ChainSync {
/** The chain is currently in the warm-up phase, synchronising from contract genesis or lastBlockProcessed */
SYNCING = "SYNCING",
Expand Down Expand Up @@ -77,12 +83,14 @@ export class ChainContext {
orderBook: OrderBookApi;
contract: ComposableCoW;
multicall: Multicall3;
orderBookApiBaseUrls?: ApiBaseUrls;

protected constructor(
options: RunSingleOptions,
provider: ethers.providers.Provider,
chainId: SupportedChainId,
registry: Registry
registry: Registry,
orderBookApi?: string
) {
const { deploymentBlock, pageSize, dryRun } = options;
this.deploymentBlock = deploymentBlock;
Expand All @@ -92,7 +100,19 @@ export class ChainContext {
this.provider = provider;
this.chainId = chainId;
this.registry = registry;
this.orderBook = new OrderBookApi({ chainId: this.chainId });
this.orderBookApiBaseUrls = orderBookApi
? ({
[this.chainId]: orderBookApi,
} as ApiBaseUrls) // FIXME: do not do this casting once this is fixed https://github.com/cowprotocol/cow-sdk/issues/176
: undefined;

this.orderBook = new OrderBookApi({
chainId: this.chainId,
baseUrls: this.orderBookApiBaseUrls,
backoffOpts: {
numOfAttempts: SDK_BACKOFF_NUM_OF_ATTEMPTS,
},
});

this.contract = composableCowContract(this.provider, this.chainId);
this.multicall = Multicall3__factory.connect(MULTICALL3, this.provider);
Expand All @@ -108,7 +128,7 @@ export class ChainContext {
options: RunSingleOptions,
storage: DBService
): Promise<ChainContext> {
const { rpc, deploymentBlock } = options;
const { rpc, orderBookApi, deploymentBlock } = options;

const provider = new ethers.providers.JsonRpcProvider(rpc);
const chainId = (await provider.getNetwork()).chainId;
Expand All @@ -120,7 +140,13 @@ export class ChainContext {
);

// Save the context to the static map to be used by the API
const context = new ChainContext(options, provider, chainId, registry);
const context = new ChainContext(
options,
provider,
chainId,
registry,
orderBookApi
);
anxolin marked this conversation as resolved.
Show resolved Hide resolved
ChainContext.chains[chainId] = context;

return context;
Expand Down
11 changes: 9 additions & 2 deletions src/domain/checkForAndPlaceOrder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
SupportedChainId,
formatEpoch,
} from "@cowprotocol/cow-sdk";
import { ChainContext } from "./chainContext";
import { ChainContext, SDK_BACKOFF_NUM_OF_ATTEMPTS } from "./chainContext";
import {
pollingOnChainDurationSeconds,
activeOrdersTotal,
Expand Down Expand Up @@ -236,7 +236,8 @@ async function _processConditionalOrder(
context: ChainContext,
orderRef: string
): Promise<PollResult> {
const { provider, orderBook, dryRun, chainId } = context;
const { provider, orderBook, dryRun, chainId, orderBookApiBaseUrls } =
context;
const { handler } = conditionalOrder.params;
const log = getLogger(
"checkForAndPlaceOrder:_processConditionalOrder",
Expand All @@ -262,6 +263,12 @@ async function _processConditionalOrder(
blockNumber,
},
provider,
orderbookApiConfig: {
baseUrls: orderBookApiBaseUrls,
backoffOpts: {
numOfAttempts: SDK_BACKOFF_NUM_OF_ATTEMPTS,
},
},
};
let pollResult = await pollConditionalOrder(
pollParams,
Expand Down
36 changes: 33 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,16 @@ const deploymentBlockOption = new Option(
.env("DEPLOYMENT_BLOCK")
.argParser(parseIntOption);

const multiOrderBookApiOption = new Option(
"--orderBookApi <orderBookApi...>",
"Orderbook API base URLs (i.e. https://api.cow.fi/mainnet, https://api.cow.fi/xdai, etc.)"
).default([]);

const orderBookApiOption = new Option(
"--orderBookApi <orderBookApi>",
"Orderbook API base URL (i.e. https://api.cow.fi/mainnet)"
).default(undefined);
anxolin marked this conversation as resolved.
Show resolved Hide resolved

async function main() {
program.name("watch-tower").description(description).version(version);

Expand All @@ -108,6 +118,7 @@ async function main() {
.description("Run the watch-tower, monitoring only a single chain")
.addOption(rpcOption)
.addOption(deploymentBlockOption)
.addOption(orderBookApiOption)
.addOption(databasePathOption)
.addOption(logLevelOption)
.addOption(watchdogTimeoutOption)
Expand All @@ -119,7 +130,8 @@ async function main() {
.addOption(disableNotificationsOption)
.addOption(slackWebhookOption)
.action((options) => {
const { logLevel } = options;
const { logLevel, orderBookApi } = options;

anxolin marked this conversation as resolved.
Show resolved Hide resolved
const [pageSize, apiPort, watchdogTimeout, deploymentBlock] = [
options.pageSize,
options.apiPort,
Expand All @@ -130,7 +142,14 @@ async function main() {
initLogging({ logLevel });

// Run the watch-tower
run({ ...options, deploymentBlock, pageSize, apiPort, watchdogTimeout });
run({
...options,
deploymentBlock,
orderBookApi,
pageSize,
apiPort,
watchdogTimeout,
});
});

program
Expand All @@ -142,6 +161,7 @@ async function main() {
)
.addOption(multiRpcOption)
.addOption(multiDeploymentBlockOption)
.addOption(multiOrderBookApiOption)
.addOption(databasePathOption)
.addOption(logLevelOption)
.addOption(watchdogTimeoutOption)
Expand All @@ -161,7 +181,11 @@ async function main() {
].map((value) => Number(value));

initLogging({ logLevel });
const { rpc: rpcs, deploymentBlock: deploymentBlocksEnv } = options;
const {
rpc: rpcs,
orderBookApi: orderBookApis,
deploymentBlock: deploymentBlocksEnv,
} = options;

// Ensure that the deployment blocks are all numbers
const deploymentBlocks = deploymentBlocksEnv.map((block) =>
Expand All @@ -176,11 +200,17 @@ async function main() {
throw new Error("RPC and deployment blocks must be the same length");
}

// Ensure that the orderBookApis and RPCs are the same length
if (orderBookApis.length > 0 && rpcs.length !== orderBookApis.length) {
throw new Error("orderBookApi and RPC urls must be the same length");
}

// Run the watch-tower
runMulti({
...options,
rpcs,
deploymentBlocks,
orderBookApis,
pageSize,
apiPort,
watchdogTimeout,
Expand Down
2 changes: 2 additions & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ export interface RunOptions extends WatchtowerOptions {

export interface RunSingleOptions extends RunOptions {
rpc: string;
orderBookApi?: string;
deploymentBlock: number;
}

export interface RunMultiOptions extends RunOptions {
rpcs: string[];
deploymentBlocks: number[];
orderBookApis: string[];
}

export interface ReplayBlockOptions extends WatchtowerReplayOptions {
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -466,10 +466,10 @@
resolved "https://registry.yarnpkg.com/@cowprotocol/contracts/-/contracts-1.4.0.tgz#e93e5f25aac76feeaa348fa57231903274676247"
integrity sha512-XLs3SlPmXD4lbiWIO7mxxuCn1eE5isuO6EUlE1cj17HqN/wukDAN0xXYPx6umOH/XdjGS33miMiPHELEyY9siw==

"@cowprotocol/cow-sdk@^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@cowprotocol/cow-sdk/-/cow-sdk-4.0.0.tgz#5b81bea462206719d9d02180a341dd905cb77573"
integrity sha512-bJvR//XkEJngxbDedOCahDnawwCG/yKn1jwe+3eiQwSqoJx3gvimP/ahNW9BstMvJZ96Pen0xKrBg0BiJA55lA==
"@cowprotocol/cow-sdk@^4.0.3":
version "4.0.3"
resolved "https://registry.yarnpkg.com/@cowprotocol/cow-sdk/-/cow-sdk-4.0.3.tgz#bd4da3e1821c33e6f5a29e04786aaa16c346bb02"
integrity sha512-bEGpHwfFpUv4he5kH99gmmJU1kZaqRH4JUCeFqIzZAxey86i+qLzVS00r3GDw5o/tKYY/0677hgusH2srr8MZw==
dependencies:
"@cowprotocol/contracts" "^1.4.0"
"@ethersproject/abstract-signer" "^5.7.0"
Expand Down