From 32138427f5730666d8440a43821d020461ff5c3c Mon Sep 17 00:00:00 2001 From: Paul <108695806+pxrl@users.noreply.github.com> Date: Tue, 26 Mar 2024 03:11:11 +1100 Subject: [PATCH] fix(relayer): Post-process external inventory config (#1333) The recent change to support externally-defined inventory configs neglected to post-process the imported config. It's important to scale each configured amount before taking the config into use. This change also relocates parsing back to the RelayerConfig constructor, which helps to simplify the implementation. --- src/relayer/RelayerClientHelper.ts | 21 ++------------------- src/relayer/RelayerConfig.ts | 24 +++++++++++++++--------- src/utils/fsUtils.ts | 11 +++++++++++ 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/relayer/RelayerClientHelper.ts b/src/relayer/RelayerClientHelper.ts index aa133a2ec..a132df02b 100644 --- a/src/relayer/RelayerClientHelper.ts +++ b/src/relayer/RelayerClientHelper.ts @@ -1,4 +1,4 @@ -import { typeguards, utils as sdkUtils } from "@across-protocol/sdk-v2"; +import { utils as sdkUtils } from "@across-protocol/sdk-v2"; import winston from "winston"; import { AcrossApiClient, BundleDataClient, InventoryClient, ProfitClient, TokenClient } from "../clients"; import { AdapterManager, CrossChainTransferClient } from "../clients/bridges"; @@ -11,7 +11,7 @@ import { updateSpokePoolClients, } from "../common"; import { SpokePoolClientsByChain } from "../interfaces"; -import { isDefined, readFile, Signer } from "../utils"; +import { Signer } from "../utils"; import { RelayerConfig } from "./RelayerConfig"; export interface RelayerClients extends Clients { @@ -115,23 +115,6 @@ export async function constructRelayerClients( adapterManager ); - // If an external inventory configuration was defined, read it in now before instantiating the InventoryClient. - if (isDefined(config.externalInventoryConfig)) { - const _inventoryConfig = await readFile(config.externalInventoryConfig); - try { - config.inventoryConfig = JSON.parse(_inventoryConfig); - } catch (err) { - const msg = typeguards.isError(err) ? err.message : (err as Record)?.code; - throw new Error(`Inventory config error in ${config.externalInventoryConfig} (${msg ?? "unknown error"})`); - } - logger.debug({ - at: "Relayer#constructRelayerClients", - message: "Updated Inventory config.", - source: config.externalInventoryConfig, - inventoryConfig: config.inventoryConfig, - }); - } - const inventoryClient = new InventoryClient( signerAddr, logger, diff --git a/src/relayer/RelayerConfig.ts b/src/relayer/RelayerConfig.ts index 7f4eccb20..7c7277dc0 100644 --- a/src/relayer/RelayerConfig.ts +++ b/src/relayer/RelayerConfig.ts @@ -1,12 +1,11 @@ -import { BigNumber, toBNWei, assert, isDefined, toBN, replaceAddressCase, ethers } from "../utils"; +import { typeguards } from "@across-protocol/sdk-v2"; +import { BigNumber, toBNWei, assert, isDefined, readFileSync, toBN, replaceAddressCase, ethers } from "../utils"; import { CommonConfig, ProcessEnv } from "../common"; import * as Constants from "../common/Constants"; import { InventoryConfig } from "../interfaces"; export class RelayerConfig extends CommonConfig { - readonly externalInventoryConfig?: string; - inventoryConfig: InventoryConfig; - + readonly inventoryConfig: InventoryConfig; readonly debugProfitability: boolean; // Whether token price fetch failures will be ignored when computing relay profitability. // If this is false, the relayer will throw an error when fetching prices fails. @@ -71,14 +70,20 @@ export class RelayerConfig extends CommonConfig { ? JSON.parse(SLOW_DEPOSITORS).map((depositor) => ethers.utils.getAddress(depositor)) : []; + this.minRelayerFeePct = toBNWei(MIN_RELAYER_FEE_PCT || Constants.RELAYER_MIN_FEE_PCT); + assert( !isDefined(RELAYER_EXTERNAL_INVENTORY_CONFIG) || !isDefined(RELAYER_INVENTORY_CONFIG), "Concurrent inventory management configurations detected." ); - this.externalInventoryConfig = RELAYER_EXTERNAL_INVENTORY_CONFIG; - this.inventoryConfig = JSON.parse(RELAYER_INVENTORY_CONFIG ?? "{}"); - - this.minRelayerFeePct = toBNWei(MIN_RELAYER_FEE_PCT || Constants.RELAYER_MIN_FEE_PCT); + try { + this.inventoryConfig = isDefined(RELAYER_EXTERNAL_INVENTORY_CONFIG) + ? JSON.parse(readFileSync(RELAYER_EXTERNAL_INVENTORY_CONFIG)) + : JSON.parse(RELAYER_INVENTORY_CONFIG ?? "{}"); + } catch (err) { + const msg = typeguards.isError(err) ? err.message : (err as Record)?.code; + throw new Error(`Inventory config error (${msg ?? "unknown error"})`); + } if (Object.keys(this.inventoryConfig).length > 0) { this.inventoryConfig = replaceAddressCase(this.inventoryConfig); // Cast any non-address case addresses. @@ -92,7 +97,7 @@ export class RelayerConfig extends CommonConfig { this.inventoryConfig.wrapEtherTargetPerChain ??= {}; assert( this.inventoryConfig.wrapEtherThreshold.gte(this.inventoryConfig.wrapEtherTarget), - `default wrapEtherThreshold ${this.inventoryConfig.wrapEtherThreshold} must be >= default wrapEtherTarget ${this.inventoryConfig.wrapEtherTarget}}` + `default wrapEtherThreshold ${this.inventoryConfig.wrapEtherThreshold} must be >= default wrapEtherTarget ${this.inventoryConfig.wrapEtherTarget}` ); // Validate the per chain target and thresholds for wrapping ETH: @@ -141,6 +146,7 @@ export class RelayerConfig extends CommonConfig { }); }); } + this.debugProfitability = DEBUG_PROFITABILITY === "true"; this.relayerGasPadding = toBNWei(RELAYER_GAS_PADDING || Constants.DEFAULT_RELAYER_GAS_PADDING); this.relayerGasMultiplier = toBNWei(RELAYER_GAS_MULTIPLIER || Constants.DEFAULT_RELAYER_GAS_MULTIPLIER); diff --git a/src/utils/fsUtils.ts b/src/utils/fsUtils.ts index 64cf99c16..18b38b715 100644 --- a/src/utils/fsUtils.ts +++ b/src/utils/fsUtils.ts @@ -1,6 +1,17 @@ import * as fs from "fs/promises"; +import { readFileSync as _readFileSync } from "node:fs"; import { typeguards } from "@across-protocol/sdk-v2"; +export function readFileSync(fileName: string): string { + try { + return _readFileSync(fileName, { encoding: "utf8" }); + } catch (err) { + // @dev fs methods can return errors that are not Error objects (i.e. errno). + const msg = typeguards.isError(err) ? err.message : (err as Record)?.code; + throw new Error(`Unable to read ${fileName} (${msg ?? "unknown error"})`); + } +} + export async function readFile(fileName: string): Promise { try { return await fs.readFile(fileName, { encoding: "utf8" });