From 40000cfaca308addafc3dd183838614b3ad95663 Mon Sep 17 00:00:00 2001 From: nicholaspai Date: Fri, 6 Oct 2023 12:52:36 -0400 Subject: [PATCH 1/9] feat(inventory-manager): Wrap ETH on Arbitrum We expect ETH to be sent to the relayer address on Arbitrum because its [hardcoded](https://github.com/across-protocol/contracts-v2/blob/3634df5ec327326ce030629aa0a089f651f20bc0/contracts/chain-adapters/Arbitrum_Adapter.sol#L158) as a refund address for all HubPool messages that get sent to the ArbitrumSpoke. --- src/clients/bridges/AdapterManager.ts | 10 ++++++---- src/clients/bridges/ArbitrumAdapter.ts | 17 +++++++++++++++-- src/common/ContractAddresses.ts | 14 ++++++++++++++ 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/clients/bridges/AdapterManager.ts b/src/clients/bridges/AdapterManager.ts index 4c7ad5ca6..d8274c5e0 100644 --- a/src/clients/bridges/AdapterManager.ts +++ b/src/clients/bridges/AdapterManager.ts @@ -3,6 +3,7 @@ import { SpokePoolClient, HubPoolClient } from "../"; import { OptimismAdapter, ArbitrumAdapter, PolygonAdapter, BaseAdapter, ZKSyncAdapter } from "./"; import { OutstandingTransfers } from "../../interfaces"; import { utils } from "@across-protocol/sdk-v2"; +import { CHAIN_IDs } from "@across-protocol/constants-v2"; import { BaseChainAdapter } from "./op-stack/base/BaseChainAdapter"; import { spokesThatHoldEthAndWeth } from "../../common/Constants"; export class AdapterManager { @@ -10,9 +11,9 @@ export class AdapterManager { // Some L2's canonical bridges send ETH, not WETH, over the canonical bridges, resulting in recipient addresses // receiving ETH that needs to be wrapped on the L2. This array contains the chainIds of the chains that this - // manager will attempt to wrap ETH on into WETH. This is not necessary for chains that receive WETH, the ERC20, - // over the bridge. - public chainsToWrapEtherOn = spokesThatHoldEthAndWeth; + // manager will attempt to wrap ETH on into WETH. This list also includes chains like Arbitrum where the relayer is + // expected to receive ETH as a gas refund from an L1 to L2 deposit that was intended to rebalance inventory. + public chainsToWrapEtherOn = [...spokesThatHoldEthAndWeth, CHAIN_IDs.ARBITRUM]; constructor( readonly logger: winston.Logger, @@ -77,7 +78,8 @@ export class AdapterManager { // Check how much ETH is on the target chain and if it is above the threshold the wrap it to WETH. Note that this only // needs to be done on chains where rebalancing WETH from L1 to L2 results in the relayer receiving ETH - // (not the ERC20). + // (not the ERC20), or if the relayer expects to be sent ETH perhaps as a gas refund from an original L1 to L2 + // deposit. async wrapEthIfAboveThreshold(wrapThreshold: BigNumber, simMode = false): Promise { await utils.mapAsync( this.chainsToWrapEtherOn.filter((chainId) => isDefined(this.spokePoolClients[chainId])), diff --git a/src/clients/bridges/ArbitrumAdapter.ts b/src/clients/bridges/ArbitrumAdapter.ts index 4851e882b..68be9c645 100644 --- a/src/clients/bridges/ArbitrumAdapter.ts +++ b/src/clients/bridges/ArbitrumAdapter.ts @@ -13,6 +13,7 @@ import { toWei, paginatedEventQuery, Event, + assert, } from "../../utils"; import { SpokePoolClient } from "../../clients"; import { BaseAdapter } from "./BaseAdapter"; @@ -194,8 +195,20 @@ export class ArbitrumAdapter extends BaseAdapter { ); } - async wrapEthIfAboveThreshold(): Promise { - throw new Error("Unnecessary to wrap ETH on Arbitrum"); + async wrapEthIfAboveThreshold(threshold: BigNumber, simMode = false): Promise { + const { chainId } = this; + assert(chainId === this.chainId, `chainId ${chainId} is not supported`); + + const weth = CONTRACT_ADDRESSES[this.chainId].weth; + const ethBalance = await this.getSigner(chainId).getBalance(); + if (ethBalance.gt(threshold)) { + const l2Signer = this.getSigner(chainId); + const contract = new Contract(weth.address, weth.abi, l2Signer); + const value = ethBalance.sub(threshold); + this.logger.debug({ at: this.getName(), message: "Wrapping ETH", threshold, value, ethBalance }); + return await this._wrapEthIfAboveThreshold(threshold, contract, value, simMode); + } + return null; } getL1Bridge(l1Token: SupportedL1Token): Contract { diff --git a/src/common/ContractAddresses.ts b/src/common/ContractAddresses.ts index 1a6b5e9d4..b7b5d1a54 100644 --- a/src/common/ContractAddresses.ts +++ b/src/common/ContractAddresses.ts @@ -695,6 +695,20 @@ export const CONTRACT_ADDRESSES: { }, ], }, + weth: { + address: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", + abi: [ + { + constant: false, + inputs: [], + name: "deposit", + outputs: [], + payable: true, + stateMutability: "payable", + type: "function", + }, + ], + }, outbox: { address: "0x0B9857ae2D4A3DBe74ffE1d7DF045bb7F96E4840", abi: [ From 62e94cc5b49c56fbd71015adb1ec24711c650dba Mon Sep 17 00:00:00 2001 From: Paul <108695806+pxrl@users.noreply.github.com> Date: Fri, 6 Oct 2023 15:48:08 +0200 Subject: [PATCH 2/9] fix: Restore privateKey wallet validation (#968) privateKey is case sensitive. --- src/utils/CLIUtils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils/CLIUtils.ts b/src/utils/CLIUtils.ts index 9c84ea3a6..d0aa9f9db 100644 --- a/src/utils/CLIUtils.ts +++ b/src/utils/CLIUtils.ts @@ -10,9 +10,9 @@ export function retrieveSignerFromCLIArgs(): Promise { // Call into the process' argv to retrieve the CLI args. const args = minimist(process.argv.slice(2)); // Resolve the wallet type & verify that it is valid. - const keyType = ((args.wallet as string) ?? "MNEMONIC").toLowerCase(); + const keyType = (args.wallet as string) ?? "mnemonic"; if (!isValidKeyType(keyType)) { - throw new Error("Must define mnemonic, privatekey or gckms for wallet"); + throw new Error(`Unsupported key type (${keyType}); expected "mnemonic", "privateKey" or "gckms"`); } // Build out the signer options to pass to the signer utils. @@ -31,5 +31,5 @@ export function retrieveSignerFromCLIArgs(): Promise { * @returns True if the key type is valid, false otherwise. */ function isValidKeyType(keyType: unknown): keyType is "mnemonic" | "privateKey" | "gckms" { - return ["mnemonic", "privateKey", "gckms"].includes((keyType as string).toLowerCase()); + return ["mnemonic", "privateKey", "gckms"].includes(keyType as string); } From 15245f78f9e95d9eb010bc153d3269ab9bb97e7d Mon Sep 17 00:00:00 2001 From: nicholaspai Date: Fri, 6 Oct 2023 14:14:53 -0400 Subject: [PATCH 3/9] fix asserts --- src/clients/bridges/ArbitrumAdapter.ts | 2 +- src/clients/bridges/op-stack/OpStackAdapter.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/clients/bridges/ArbitrumAdapter.ts b/src/clients/bridges/ArbitrumAdapter.ts index 68be9c645..aa230d5ef 100644 --- a/src/clients/bridges/ArbitrumAdapter.ts +++ b/src/clients/bridges/ArbitrumAdapter.ts @@ -197,7 +197,7 @@ export class ArbitrumAdapter extends BaseAdapter { async wrapEthIfAboveThreshold(threshold: BigNumber, simMode = false): Promise { const { chainId } = this; - assert(chainId === this.chainId, `chainId ${chainId} is not supported`); + assert(42161 === chainId, `chainId ${chainId} is not supported`); const weth = CONTRACT_ADDRESSES[this.chainId].weth; const ethBalance = await this.getSigner(chainId).getBalance(); diff --git a/src/clients/bridges/op-stack/OpStackAdapter.ts b/src/clients/bridges/op-stack/OpStackAdapter.ts index cfff52c8b..418552ecd 100644 --- a/src/clients/bridges/op-stack/OpStackAdapter.ts +++ b/src/clients/bridges/op-stack/OpStackAdapter.ts @@ -140,7 +140,7 @@ export class OpStackAdapter extends BaseAdapter { async wrapEthIfAboveThreshold(threshold: BigNumber, simMode = false): Promise { const { chainId } = this; - assert(chainId === this.chainId, `chainId ${chainId} is not supported`); + assert([10, 8453].includes(chainId), `chainId ${chainId} is not supported`); const ovmWeth = CONTRACT_ADDRESSES[this.chainId].weth; const ethBalance = await this.getSigner(chainId).getBalance(); From 89ae7a2e310256e82f2d7004ebd6a825345aa453 Mon Sep 17 00:00:00 2001 From: nicholaspai Date: Fri, 6 Oct 2023 14:25:56 -0400 Subject: [PATCH 4/9] WIP --- src/clients/bridges/ArbitrumAdapter.ts | 14 +++++++++++--- src/clients/bridges/BaseAdapter.ts | 8 ++++++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/clients/bridges/ArbitrumAdapter.ts b/src/clients/bridges/ArbitrumAdapter.ts index aa230d5ef..36b8b57b5 100644 --- a/src/clients/bridges/ArbitrumAdapter.ts +++ b/src/clients/bridges/ArbitrumAdapter.ts @@ -195,17 +195,25 @@ export class ArbitrumAdapter extends BaseAdapter { ); } - async wrapEthIfAboveThreshold(threshold: BigNumber, simMode = false): Promise { + // The arbitrum relayer expects to receive ETH steadily per HubPool bundle processed, since it is the L2 refund + // address hardcoded in the Arbitrum Adapter. Therefore, to avoid excessively wrapping ETH, we only wrap + // it once it gets above a certain threshold. + async wrapEthIfAboveThreshold(target: BigNumber, simMode = false): Promise { const { chainId } = this; assert(42161 === chainId, `chainId ${chainId} is not supported`); const weth = CONTRACT_ADDRESSES[this.chainId].weth; const ethBalance = await this.getSigner(chainId).getBalance(); + + // For Arbitrum specifically, ETH should accumulate steadily. So reduce ETH balance down to the target + // if it exceeds a threshold. + // TODO: Add a configurable ETH target as opposed to just a threshold. + const threshold = target.mul(10); if (ethBalance.gt(threshold)) { const l2Signer = this.getSigner(chainId); const contract = new Contract(weth.address, weth.abi, l2Signer); - const value = ethBalance.sub(threshold); - this.logger.debug({ at: this.getName(), message: "Wrapping ETH", threshold, value, ethBalance }); + const value = ethBalance.sub(target); + this.logger.debug({ at: this.getName(), message: "Wrapping ETH", threshold, target, value, ethBalance }); return await this._wrapEthIfAboveThreshold(threshold, contract, value, simMode); } return null; diff --git a/src/clients/bridges/BaseAdapter.ts b/src/clients/bridges/BaseAdapter.ts index 4a2f8867c..d219ac98b 100644 --- a/src/clients/bridges/BaseAdapter.ts +++ b/src/clients/bridges/BaseAdapter.ts @@ -325,9 +325,13 @@ export abstract class BaseAdapter { const { chainId, txnClient } = this; const method = "deposit"; const mrkdwn = - `Ether on chain ${this.chainId} was wrapped due to being over the threshold of ` + + `${createFormatFunction(2, 4, false, 18)(toBN(value).toString())} Ether on chain ${ + this.chainId + } was wrapped due to being over the threshold of ` + `${createFormatFunction(2, 4, false, 18)(toBN(wrapThreshold).toString())} ETH.`; - const message = `Eth wrapped on target chain ${this.chainId}🎁`; + const message = `${createFormatFunction(2, 4, false, 18)(toBN(value).toString())} Eth wrapped on target chain ${ + this.chainId + }🎁`; if (simMode) { const { succeed, reason } = ( await txnClient.simulate([{ contract: l2WEthContract, chainId, method, args: [], value, mrkdwn, message }]) From b9bd645f9565ade41d119946b58c56349bd42f4d Mon Sep 17 00:00:00 2001 From: nicholaspai <9457025+nicholaspai@users.noreply.github.com> Date: Fri, 6 Oct 2023 14:29:35 -0400 Subject: [PATCH 5/9] Update src/clients/bridges/BaseAdapter.ts Co-authored-by: Paul <108695806+pxrl@users.noreply.github.com> --- src/clients/bridges/BaseAdapter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clients/bridges/BaseAdapter.ts b/src/clients/bridges/BaseAdapter.ts index d219ac98b..010374032 100644 --- a/src/clients/bridges/BaseAdapter.ts +++ b/src/clients/bridges/BaseAdapter.ts @@ -326,7 +326,7 @@ export abstract class BaseAdapter { const method = "deposit"; const mrkdwn = `${createFormatFunction(2, 4, false, 18)(toBN(value).toString())} Ether on chain ${ - this.chainId + chainId } was wrapped due to being over the threshold of ` + `${createFormatFunction(2, 4, false, 18)(toBN(wrapThreshold).toString())} ETH.`; const message = `${createFormatFunction(2, 4, false, 18)(toBN(value).toString())} Eth wrapped on target chain ${ From 8ea2f1591e60b4cd36ce989928bfa09ec409e0b1 Mon Sep 17 00:00:00 2001 From: nicholaspai <9457025+nicholaspai@users.noreply.github.com> Date: Fri, 6 Oct 2023 14:29:42 -0400 Subject: [PATCH 6/9] Update src/clients/bridges/BaseAdapter.ts Co-authored-by: Paul <108695806+pxrl@users.noreply.github.com> --- src/clients/bridges/BaseAdapter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clients/bridges/BaseAdapter.ts b/src/clients/bridges/BaseAdapter.ts index 010374032..413c0dfa6 100644 --- a/src/clients/bridges/BaseAdapter.ts +++ b/src/clients/bridges/BaseAdapter.ts @@ -330,7 +330,7 @@ export abstract class BaseAdapter { } was wrapped due to being over the threshold of ` + `${createFormatFunction(2, 4, false, 18)(toBN(wrapThreshold).toString())} ETH.`; const message = `${createFormatFunction(2, 4, false, 18)(toBN(value).toString())} Eth wrapped on target chain ${ - this.chainId + chainId }🎁`; if (simMode) { const { succeed, reason } = ( From 3611ffbae5e00ee3c64b34d1afa011773b56b6b8 Mon Sep 17 00:00:00 2001 From: nicholaspai Date: Fri, 6 Oct 2023 14:36:24 -0400 Subject: [PATCH 7/9] Update BaseAdapter.ts --- src/clients/bridges/BaseAdapter.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/clients/bridges/BaseAdapter.ts b/src/clients/bridges/BaseAdapter.ts index 413c0dfa6..53970bbac 100644 --- a/src/clients/bridges/BaseAdapter.ts +++ b/src/clients/bridges/BaseAdapter.ts @@ -325,13 +325,19 @@ export abstract class BaseAdapter { const { chainId, txnClient } = this; const method = "deposit"; const mrkdwn = - `${createFormatFunction(2, 4, false, 18)(toBN(value).toString())} Ether on chain ${ - chainId - } was wrapped due to being over the threshold of ` + + `${createFormatFunction( + 2, + 4, + false, + 18 + )(toBN(value).toString())} Ether on chain ${chainId} was wrapped due to being over the threshold of ` + `${createFormatFunction(2, 4, false, 18)(toBN(wrapThreshold).toString())} ETH.`; - const message = `${createFormatFunction(2, 4, false, 18)(toBN(value).toString())} Eth wrapped on target chain ${ - chainId - }🎁`; + const message = `${createFormatFunction( + 2, + 4, + false, + 18 + )(toBN(value).toString())} Eth wrapped on target chain ${chainId}🎁`; if (simMode) { const { succeed, reason } = ( await txnClient.simulate([{ contract: l2WEthContract, chainId, method, args: [], value, mrkdwn, message }]) From 4a472123163218c2243aa902ea8184c475ec0334 Mon Sep 17 00:00:00 2001 From: nicholaspai Date: Fri, 6 Oct 2023 15:07:17 -0400 Subject: [PATCH 8/9] Add configurable target/threshold --- src/clients/InventoryClient.ts | 2 +- src/clients/bridges/AdapterManager.ts | 14 +++++--- src/clients/bridges/ArbitrumAdapter.ts | 13 ++++---- src/clients/bridges/BaseAdapter.ts | 6 +++- src/clients/bridges/ZKSyncAdapter.ts | 10 ++++-- .../bridges/op-stack/OpStackAdapter.ts | 10 ++++-- src/interfaces/InventoryManagement.ts | 10 +++++- src/relayer/RelayerConfig.ts | 32 +++++++++++++++++++ 8 files changed, 77 insertions(+), 20 deletions(-) diff --git a/src/clients/InventoryClient.ts b/src/clients/InventoryClient.ts index 89d49e719..4eebb0ebf 100644 --- a/src/clients/InventoryClient.ts +++ b/src/clients/InventoryClient.ts @@ -612,7 +612,7 @@ export class InventoryClient { return; } this.log("Checking ETH->WETH Wrap status"); - await this.adapterManager.wrapEthIfAboveThreshold(this.inventoryConfig.wrapEtherThreshold, this.simMode); + await this.adapterManager.wrapEthIfAboveThreshold(this.inventoryConfig, this.simMode); } async update(): Promise { diff --git a/src/clients/bridges/AdapterManager.ts b/src/clients/bridges/AdapterManager.ts index d8274c5e0..d3329d518 100644 --- a/src/clients/bridges/AdapterManager.ts +++ b/src/clients/bridges/AdapterManager.ts @@ -1,7 +1,7 @@ -import { BigNumber, isDefined, winston, Signer, getL2TokenAddresses, TransactionResponse } from "../../utils"; +import { BigNumber, isDefined, winston, Signer, getL2TokenAddresses, TransactionResponse, assert } from "../../utils"; import { SpokePoolClient, HubPoolClient } from "../"; import { OptimismAdapter, ArbitrumAdapter, PolygonAdapter, BaseAdapter, ZKSyncAdapter } from "./"; -import { OutstandingTransfers } from "../../interfaces"; +import { InventoryConfig, OutstandingTransfers } from "../../interfaces"; import { utils } from "@across-protocol/sdk-v2"; import { CHAIN_IDs } from "@across-protocol/constants-v2"; import { BaseChainAdapter } from "./op-stack/base/BaseChainAdapter"; @@ -80,11 +80,17 @@ export class AdapterManager { // needs to be done on chains where rebalancing WETH from L1 to L2 results in the relayer receiving ETH // (not the ERC20), or if the relayer expects to be sent ETH perhaps as a gas refund from an original L1 to L2 // deposit. - async wrapEthIfAboveThreshold(wrapThreshold: BigNumber, simMode = false): Promise { + async wrapEthIfAboveThreshold(inventoryConfig: InventoryConfig, simMode = false): Promise { await utils.mapAsync( this.chainsToWrapEtherOn.filter((chainId) => isDefined(this.spokePoolClients[chainId])), async (chainId) => { - await this.adapters[chainId].wrapEthIfAboveThreshold(wrapThreshold, simMode); + const wrapThreshold = inventoryConfig.wrapEtherThresholdPerChain[chainId] ?? inventoryConfig.wrapEtherThreshold; + const wrapTarget = inventoryConfig.wrapEtherTargetPerChain[chainId] ?? inventoryConfig.wrapEtherTarget; + assert( + wrapThreshold.gte(wrapTarget), + `wrapEtherThreshold ${wrapThreshold.toString()} must be >= wrapEtherTarget ${wrapTarget.toString()}` + ); + await this.adapters[chainId].wrapEthIfAboveThreshold(wrapThreshold, wrapTarget, simMode); } ); } diff --git a/src/clients/bridges/ArbitrumAdapter.ts b/src/clients/bridges/ArbitrumAdapter.ts index 36b8b57b5..68598f5aa 100644 --- a/src/clients/bridges/ArbitrumAdapter.ts +++ b/src/clients/bridges/ArbitrumAdapter.ts @@ -196,19 +196,18 @@ export class ArbitrumAdapter extends BaseAdapter { } // The arbitrum relayer expects to receive ETH steadily per HubPool bundle processed, since it is the L2 refund - // address hardcoded in the Arbitrum Adapter. Therefore, to avoid excessively wrapping ETH, we only wrap - // it once it gets above a certain threshold. - async wrapEthIfAboveThreshold(target: BigNumber, simMode = false): Promise { + // address hardcoded in the Arbitrum Adapter. + async wrapEthIfAboveThreshold( + threshold: BigNumber, + target: BigNumber, + simMode = false + ): Promise { const { chainId } = this; assert(42161 === chainId, `chainId ${chainId} is not supported`); const weth = CONTRACT_ADDRESSES[this.chainId].weth; const ethBalance = await this.getSigner(chainId).getBalance(); - // For Arbitrum specifically, ETH should accumulate steadily. So reduce ETH balance down to the target - // if it exceeds a threshold. - // TODO: Add a configurable ETH target as opposed to just a threshold. - const threshold = target.mul(10); if (ethBalance.gt(threshold)) { const l2Signer = this.getSigner(chainId); const contract = new Contract(weth.address, weth.abi, l2Signer); diff --git a/src/clients/bridges/BaseAdapter.ts b/src/clients/bridges/BaseAdapter.ts index 53970bbac..7cbe1366a 100644 --- a/src/clients/bridges/BaseAdapter.ts +++ b/src/clients/bridges/BaseAdapter.ts @@ -372,5 +372,9 @@ export abstract class BaseAdapter { abstract checkTokenApprovals(address: string, l1Tokens: string[]): Promise; - abstract wrapEthIfAboveThreshold(threshold: BigNumber, simMode: boolean): Promise; + abstract wrapEthIfAboveThreshold( + threshold: BigNumber, + target: BigNumber, + simMode: boolean + ): Promise; } diff --git a/src/clients/bridges/ZKSyncAdapter.ts b/src/clients/bridges/ZKSyncAdapter.ts index e4c3a8723..9a6214005 100644 --- a/src/clients/bridges/ZKSyncAdapter.ts +++ b/src/clients/bridges/ZKSyncAdapter.ts @@ -229,7 +229,11 @@ export class ZKSyncAdapter extends BaseAdapter { * @param threshold * @returns */ - async wrapEthIfAboveThreshold(threshold: BigNumber, simMode = false): Promise { + async wrapEthIfAboveThreshold( + threshold: BigNumber, + target: BigNumber, + simMode = false + ): Promise { const { chainId } = this; assert(chainId === 324, `chainId ${chainId} is not supported`); @@ -239,8 +243,8 @@ export class ZKSyncAdapter extends BaseAdapter { const l2Signer = this.getSigner(chainId); // @dev Can re-use ABI from L1 weth as its the same for the purposes of this function. const contract = new Contract(l2WethAddress, CONTRACT_ADDRESSES[this.hubChainId].weth.abi, l2Signer); - const value = ethBalance.sub(threshold); - this.logger.debug({ at: this.getName(), message: "Wrapping ETH", threshold, value, ethBalance }); + const value = ethBalance.sub(target); + this.logger.debug({ at: this.getName(), message: "Wrapping ETH", threshold, target, value, ethBalance }); return await this._wrapEthIfAboveThreshold(threshold, contract, value, simMode); } return null; diff --git a/src/clients/bridges/op-stack/OpStackAdapter.ts b/src/clients/bridges/op-stack/OpStackAdapter.ts index 418552ecd..c5f2fe268 100644 --- a/src/clients/bridges/op-stack/OpStackAdapter.ts +++ b/src/clients/bridges/op-stack/OpStackAdapter.ts @@ -138,7 +138,11 @@ export class OpStackAdapter extends BaseAdapter { ); } - async wrapEthIfAboveThreshold(threshold: BigNumber, simMode = false): Promise { + async wrapEthIfAboveThreshold( + threshold: BigNumber, + target: BigNumber, + simMode = false + ): Promise { const { chainId } = this; assert([10, 8453].includes(chainId), `chainId ${chainId} is not supported`); @@ -147,8 +151,8 @@ export class OpStackAdapter extends BaseAdapter { if (ethBalance.gt(threshold)) { const l2Signer = this.getSigner(chainId); const contract = new Contract(ovmWeth.address, ovmWeth.abi, l2Signer); - const value = ethBalance.sub(threshold); - this.logger.debug({ at: this.getName(), message: "Wrapping ETH", threshold, value, ethBalance }); + const value = ethBalance.sub(target); + this.logger.debug({ at: this.getName(), message: "Wrapping ETH", threshold, target, value, ethBalance }); return await this._wrapEthIfAboveThreshold(threshold, contract, value, simMode); } return null; diff --git a/src/interfaces/InventoryManagement.ts b/src/interfaces/InventoryManagement.ts index 1874f6da2..89dc3de7e 100644 --- a/src/interfaces/InventoryManagement.ts +++ b/src/interfaces/InventoryManagement.ts @@ -12,5 +12,13 @@ export interface InventoryConfig { }; }; }; - wrapEtherThreshold: BigNumber; // Number of Ether, that if the balance is above, wrap it to WETH on the L2. in wei + // If ETH balance on chain is above threshold, wrap the excess over the target to WETH. + wrapEtherTargetPerChain: { + [chainId: number]: BigNumber; + }; + wrapEtherTarget: BigNumber; + wrapEtherThresholdPerChain: { + [chainId: number]: BigNumber; + }; + wrapEtherThreshold: BigNumber; } diff --git a/src/relayer/RelayerConfig.ts b/src/relayer/RelayerConfig.ts index 19801228f..45ceaeff8 100644 --- a/src/relayer/RelayerConfig.ts +++ b/src/relayer/RelayerConfig.ts @@ -73,7 +73,39 @@ export class RelayerConfig extends CommonConfig { this.inventoryConfig.wrapEtherThreshold = this.inventoryConfig.wrapEtherThreshold ? toBNWei(this.inventoryConfig.wrapEtherThreshold) : toBNWei(1); // default to keeping 2 Eth on the target chains and wrapping the rest to WETH. + this.inventoryConfig.wrapEtherThresholdPerChain = this.inventoryConfig.wrapEtherThresholdPerChain ?? {}; + this.inventoryConfig.wrapEtherTarget = this.inventoryConfig.wrapEtherTarget + ? toBNWei(this.inventoryConfig.wrapEtherTarget) + : this.inventoryConfig.wrapEtherThreshold; // default to wrapping ETH to threshold, same as target. + this.inventoryConfig.wrapEtherTargetPerChain = this.inventoryConfig.wrapEtherTargetPerChain ?? {}; + assert( + this.inventoryConfig.wrapEtherThreshold.gte(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: + Object.keys(this.inventoryConfig.wrapEtherThresholdPerChain).forEach((chainId) => { + if (this.inventoryConfig.wrapEtherThresholdPerChain[chainId] !== undefined) { + this.inventoryConfig.wrapEtherThresholdPerChain[chainId] = toBNWei( + this.inventoryConfig.wrapEtherThresholdPerChain[chainId] + ); + } + }); + Object.keys(this.inventoryConfig.wrapEtherTargetPerChain).forEach((chainId) => { + if (this.inventoryConfig.wrapEtherTargetPerChain[chainId] !== undefined) { + this.inventoryConfig.wrapEtherTargetPerChain[chainId] = toBNWei( + this.inventoryConfig.wrapEtherTargetPerChain[chainId] + ); + // Check newly set target against threshold + const threshold = + this.inventoryConfig.wrapEtherThresholdPerChain[chainId] ?? this.inventoryConfig.wrapEtherThreshold; + const target = this.inventoryConfig.wrapEtherTargetPerChain[chainId]; + assert( + threshold.gte(target), + `wrapEtherThresholdPerChain ${threshold.toString()} must be >= wrapEtherTargetPerChain ${target}` + ); + } + }); Object.keys(this.inventoryConfig.tokenConfig).forEach((l1Token) => { Object.keys(this.inventoryConfig.tokenConfig[l1Token]).forEach((chainId) => { const { targetPct, thresholdPct, unwrapWethThreshold, unwrapWethTarget } = From bd56e7e5b77289708e5bc24f7c2db17515365112 Mon Sep 17 00:00:00 2001 From: nicholaspai Date: Fri, 6 Oct 2023 15:30:24 -0400 Subject: [PATCH 9/9] lint --- src/clients/bridges/BaseAdapter.ts | 19 ++++++------------- src/relayer/RelayerConfig.ts | 4 ++-- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/clients/bridges/BaseAdapter.ts b/src/clients/bridges/BaseAdapter.ts index 7cbe1366a..0added6dc 100644 --- a/src/clients/bridges/BaseAdapter.ts +++ b/src/clients/bridges/BaseAdapter.ts @@ -324,20 +324,13 @@ export abstract class BaseAdapter { ): Promise { const { chainId, txnClient } = this; const method = "deposit"; + const formatFunc = createFormatFunction(2, 4, false, 18); const mrkdwn = - `${createFormatFunction( - 2, - 4, - false, - 18 - )(toBN(value).toString())} Ether on chain ${chainId} was wrapped due to being over the threshold of ` + - `${createFormatFunction(2, 4, false, 18)(toBN(wrapThreshold).toString())} ETH.`; - const message = `${createFormatFunction( - 2, - 4, - false, - 18 - )(toBN(value).toString())} Eth wrapped on target chain ${chainId}🎁`; + `${formatFunc( + toBN(value).toString() + )} Ether on chain ${chainId} was wrapped due to being over the threshold of ` + + `${formatFunc(toBN(wrapThreshold).toString())} ETH.`; + const message = `${formatFunc(toBN(value).toString())} Eth wrapped on target chain ${chainId}🎁`; if (simMode) { const { succeed, reason } = ( await txnClient.simulate([{ contract: l2WEthContract, chainId, method, args: [], value, mrkdwn, message }]) diff --git a/src/relayer/RelayerConfig.ts b/src/relayer/RelayerConfig.ts index 45ceaeff8..c6dabe3ef 100644 --- a/src/relayer/RelayerConfig.ts +++ b/src/relayer/RelayerConfig.ts @@ -73,11 +73,11 @@ export class RelayerConfig extends CommonConfig { this.inventoryConfig.wrapEtherThreshold = this.inventoryConfig.wrapEtherThreshold ? toBNWei(this.inventoryConfig.wrapEtherThreshold) : toBNWei(1); // default to keeping 2 Eth on the target chains and wrapping the rest to WETH. - this.inventoryConfig.wrapEtherThresholdPerChain = this.inventoryConfig.wrapEtherThresholdPerChain ?? {}; + this.inventoryConfig.wrapEtherThresholdPerChain ??= {}; this.inventoryConfig.wrapEtherTarget = this.inventoryConfig.wrapEtherTarget ? toBNWei(this.inventoryConfig.wrapEtherTarget) : this.inventoryConfig.wrapEtherThreshold; // default to wrapping ETH to threshold, same as target. - this.inventoryConfig.wrapEtherTargetPerChain = this.inventoryConfig.wrapEtherTargetPerChain ?? {}; + this.inventoryConfig.wrapEtherTargetPerChain ??= {}; assert( this.inventoryConfig.wrapEtherThreshold.gte(this.inventoryConfig.wrapEtherTarget), `default wrapEtherThreshold ${this.inventoryConfig.wrapEtherThreshold} must be >= default wrapEtherTarget ${this.inventoryConfig.wrapEtherTarget}}`