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(inventory-manager): Wrap ETH on Arbitrum #969

Merged
merged 10 commits into from
Oct 6, 2023
10 changes: 6 additions & 4 deletions src/clients/bridges/AdapterManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@ 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 {
public adapters: { [chainId: number]: BaseAdapter } = {};

// 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,
Expand Down Expand Up @@ -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<void> {
await utils.mapAsync(
this.chainsToWrapEtherOn.filter((chainId) => isDefined(this.spokePoolClients[chainId])),
Expand Down
17 changes: 15 additions & 2 deletions src/clients/bridges/ArbitrumAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
toWei,
paginatedEventQuery,
Event,
assert,
} from "../../utils";
import { SpokePoolClient } from "../../clients";
import { BaseAdapter } from "./BaseAdapter";
Expand Down Expand Up @@ -194,8 +195,20 @@ export class ArbitrumAdapter extends BaseAdapter {
);
}

async wrapEthIfAboveThreshold(): Promise<TransactionResponse | null> {
throw new Error("Unnecessary to wrap ETH on Arbitrum");
async wrapEthIfAboveThreshold(threshold: BigNumber, simMode = false): Promise<TransactionResponse | null> {
const { chainId } = this;
assert(chainId === this.chainId, `chainId ${chainId} is not supported`);
pxrl marked this conversation as resolved.
Show resolved Hide resolved

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);
pxrl marked this conversation as resolved.
Show resolved Hide resolved
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 {
Expand Down
14 changes: 14 additions & 0 deletions src/common/ContractAddresses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: [
Expand Down
6 changes: 3 additions & 3 deletions src/utils/CLIUtils.ts
pxrl marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ export function retrieveSignerFromCLIArgs(): Promise<Wallet> {
// 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.
Expand All @@ -31,5 +31,5 @@ export function retrieveSignerFromCLIArgs(): Promise<Wallet> {
* @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);
}