From 045264498bc2d8286b1d8313da1ea8034028f0cb Mon Sep 17 00:00:00 2001 From: bmzig <57361391+bmzig@users.noreply.github.com> Date: Thu, 19 Sep 2024 07:25:57 -0500 Subject: [PATCH 1/8] fix: do not submit multicall transactions with no calldata (#1828) Signed-off-by: bennett --- src/clients/MultiCallerClient.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/clients/MultiCallerClient.ts b/src/clients/MultiCallerClient.ts index 8769b518b..da4390fa7 100644 --- a/src/clients/MultiCallerClient.ts +++ b/src/clients/MultiCallerClient.ts @@ -633,7 +633,8 @@ export class TryMulticallClient extends MultiCallerClient { mrkdwn: mrkdwn.join(""), }; }; - txnRequestsToSubmit.push(...txnCalldataToRebuild.map(rebuildTryMulticall)); + const tryMulticallTxns = txnCalldataToRebuild.filter((txn) => txn.calldata.length !== 0).map(rebuildTryMulticall); + txnRequestsToSubmit.push(...tryMulticallTxns); } if (simulate) { From 04d4c511431a1e7630244ed85af65dbe91cd308f Mon Sep 17 00:00:00 2001 From: Paul <108695806+pxrl@users.noreply.github.com> Date: Thu, 19 Sep 2024 17:12:40 +0200 Subject: [PATCH 2/8] refactor: Centralise BigNumber import (#1820) Stop depending directly on ethers for this, such that it should be easier to eventually shift away from ethers v5. --- .eslintrc.js | 7 +++++ package.json | 1 + scripts/hubpool.ts | 8 +++--- src/adapter/bridges/UsdcCCTPBridge.ts | 4 +-- .../bridges/UsdcTokenSplitterBridge.ts | 4 +-- src/adapter/bridges/ZKSyncBridge.ts | 2 +- src/adapter/utils.ts | 3 ++- src/clients/InventoryClient.ts | 8 +++--- src/clients/MultiCallerClient.ts | 2 +- .../bridges/op-stack/OpStackAdapter.ts | 4 +-- .../bridges/op-stack/UsdcCCTPBridge.ts | 4 +-- .../op-stack/UsdcTokenSplitterBridge.ts | 11 ++++++-- src/clients/bridges/utils.ts | 3 ++- src/finalizer/index.ts | 6 +++-- src/interfaces/InventoryManagement.ts | 4 +-- src/interfaces/Report.ts | 2 +- src/interfaces/Token.ts | 2 +- src/interfaces/index.ts | 2 +- src/monitor/Monitor.ts | 27 +++++++++---------- src/utils/BNUtils.ts | 6 ++++- src/utils/CCTPUtils.ts | 3 ++- src/utils/SDKUtils.ts | 3 +++ src/utils/TokenUtils.ts | 8 +++--- src/utils/TransactionUtils.ts | 2 +- src/utils/index.ts | 2 -- test/Dataworker.buildRoots.ts | 3 +-- test/InventoryClient.InventoryRebalance.ts | 3 ++- test/InventoryClient.RefundChain.ts | 4 +-- test/TransactionClient.ts | 3 +-- test/mocks/MockCrossChainTransferClient.ts | 4 +-- test/mocks/MockInventoryClient.ts | 5 ++-- test/mocks/MockTransactionClient.ts | 4 +-- test/utils/utils.ts | 4 +-- 33 files changed, 91 insertions(+), 67 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index b5052e51d..dfc7b7e9b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -38,6 +38,13 @@ module.exports = { "no-duplicate-imports": "error", "@typescript-eslint/no-floating-promises": ["error"], "@typescript-eslint/no-misused-promises": ["error", { checksVoidReturn: false }], + "no-restricted-imports": [ + "error", + { + patterns: [{ group: ["@ethersproject/bignumber"], message: "Use 'src/utils/BNUtils' instead" }], + paths: [{ name: "ethers", importNames: ["BigNumber"], message: "Use 'src/utils/BNUtils' instead" }], + }, + ], }, settings: { node: { diff --git a/package.json b/package.json index 704391dc1..53d1763b6 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "@ethersproject/abi": "^5.7.0", "@ethersproject/abstract-provider": "^5.7.0", "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", "@google-cloud/kms": "^3.6.0", "@google-cloud/storage": "^6.10.1", "@maticnetwork/maticjs": "^3.6.0", diff --git a/scripts/hubpool.ts b/scripts/hubpool.ts index 3c14fe311..8d5325da5 100644 --- a/scripts/hubpool.ts +++ b/scripts/hubpool.ts @@ -1,14 +1,12 @@ import minimist from "minimist"; import { WETH9__factory as WETH9 } from "@across-protocol/contracts"; import { constants as sdkConsts } from "@across-protocol/sdk"; -import { BigNumber, ethers, Signer } from "ethers"; +import { ethers, Signer } from "ethers"; import { config } from "dotenv"; -import { getNetworkName, getSigner } from "../src/utils"; +import { BigNumber, bnOne, bnUint256Max, formatEther, formatUnits, getNetworkName, getSigner } from "../src/utils"; import * as utils from "./utils"; const { PROTOCOL_DEFAULT_CHAIN_ID_INDICES } = sdkConsts; -const { MaxUint256, One: bnOne } = ethers.constants; -const { formatEther, formatUnits } = ethers.utils; const { NODE_SUCCESS, NODE_INPUT_ERR, NODE_APP_ERR } = utils; @@ -99,7 +97,7 @@ async function dispute(args: Record, signer: Signer): P if (allowance.lt(bondAmount)) { console.log(`Approving ${network} HubPool @ ${hubPool.address} to transfer ${symbol}.`); - const approval = await bondToken.connect(signer).approve(hubPool.address, MaxUint256); + const approval = await bondToken.connect(signer).approve(hubPool.address, bnUint256Max); console.log(`Approval: ${approval.hash}...`); await approval.wait(); } diff --git a/src/adapter/bridges/UsdcCCTPBridge.ts b/src/adapter/bridges/UsdcCCTPBridge.ts index 40cf0e332..c26f4e282 100644 --- a/src/adapter/bridges/UsdcCCTPBridge.ts +++ b/src/adapter/bridges/UsdcCCTPBridge.ts @@ -1,7 +1,7 @@ -import { BigNumber, Contract, Signer } from "ethers"; +import { Contract, Signer } from "ethers"; import { CONTRACT_ADDRESSES, chainIdsToCctpDomains } from "../../common"; import { BridgeTransactionDetails, BaseBridgeAdapter, BridgeEvents } from "./BaseBridgeAdapter"; -import { EventSearchConfig, Provider, TOKEN_SYMBOLS_MAP, compareAddressesSimple, assert } from "../../utils"; +import { BigNumber, EventSearchConfig, Provider, TOKEN_SYMBOLS_MAP, compareAddressesSimple, assert } from "../../utils"; import { processEvent } from "../utils"; import { cctpAddressToBytes32, retrieveOutstandingCCTPBridgeUSDCTransfers } from "../../utils/CCTPUtils"; diff --git a/src/adapter/bridges/UsdcTokenSplitterBridge.ts b/src/adapter/bridges/UsdcTokenSplitterBridge.ts index 108e688ae..d9f98aa7d 100644 --- a/src/adapter/bridges/UsdcTokenSplitterBridge.ts +++ b/src/adapter/bridges/UsdcTokenSplitterBridge.ts @@ -1,8 +1,8 @@ -import { BigNumber, Signer } from "ethers"; +import { Signer } from "ethers"; import { CONTRACT_ADDRESSES, CANONICAL_BRIDGE } from "../../common"; import { UsdcCCTPBridge } from "./UsdcCCTPBridge"; import { BridgeTransactionDetails, BaseBridgeAdapter, BridgeEvents } from "./BaseBridgeAdapter"; -import { EventSearchConfig, Provider, TOKEN_SYMBOLS_MAP, compareAddressesSimple, assert } from "../../utils"; +import { BigNumber, EventSearchConfig, Provider, TOKEN_SYMBOLS_MAP, compareAddressesSimple, assert } from "../../utils"; export class UsdcTokenSplitterBridge extends BaseBridgeAdapter { protected cctpBridge: BaseBridgeAdapter; diff --git a/src/adapter/bridges/ZKSyncBridge.ts b/src/adapter/bridges/ZKSyncBridge.ts index 314dfb245..74204a289 100644 --- a/src/adapter/bridges/ZKSyncBridge.ts +++ b/src/adapter/bridges/ZKSyncBridge.ts @@ -176,7 +176,7 @@ export class ZKSyncBridge extends BaseBridgeAdapter { return l2Gas; } - protected getMailboxContract() { + protected getMailboxContract(): Contract { return this.zkSyncMailbox; } } diff --git a/src/adapter/utils.ts b/src/adapter/utils.ts index bd7a76cd1..11de44697 100644 --- a/src/adapter/utils.ts +++ b/src/adapter/utils.ts @@ -1,6 +1,7 @@ -import { BigNumber, Event } from "ethers"; +import { Event } from "ethers"; import { TOKEN_APPROVALS_TO_FIRST_ZERO } from "../common"; import { + BigNumber, spreadEventWithBlockNumber, toBN, MAX_SAFE_ALLOWANCE, diff --git a/src/clients/InventoryClient.ts b/src/clients/InventoryClient.ts index 8b83b28b6..d02f0204a 100644 --- a/src/clients/InventoryClient.ts +++ b/src/clients/InventoryClient.ts @@ -1,4 +1,3 @@ -import { utils as ethersUtils } from "ethers"; import { constants, utils as sdkUtils } from "@across-protocol/sdk"; import WETH_ABI from "../common/abi/Weth.json"; import { @@ -10,6 +9,7 @@ import { createFormatFunction, blockExplorerLink, Contract, + formatUnits, runTransaction, isDefined, DefaultLogLevels, @@ -577,9 +577,9 @@ export class InventoryClient { cumulativeVirtualBalance, cumulativeVirtualBalanceWithShortfall, cumulativeVirtualBalanceWithShortfallPostRefunds, - targetPct: ethersUtils.formatUnits(tokenConfig.targetPct, 18), - targetOverage: ethersUtils.formatUnits(targetOverageBuffer, 18), - effectiveTargetPct: ethersUtils.formatUnits(effectiveTargetPct, 18), + targetPct: formatUnits(tokenConfig.targetPct, 18), + targetOverage: formatUnits(targetOverageBuffer, 18), + effectiveTargetPct: formatUnits(effectiveTargetPct, 18), expectedPostRelayAllocation, chainsToEvaluate, } diff --git a/src/clients/MultiCallerClient.ts b/src/clients/MultiCallerClient.ts index da4390fa7..42bbbe26b 100644 --- a/src/clients/MultiCallerClient.ts +++ b/src/clients/MultiCallerClient.ts @@ -1,7 +1,7 @@ import { utils as sdkUtils } from "@across-protocol/sdk"; -import { BigNumber } from "ethers"; import { DEFAULT_MULTICALL_CHUNK_SIZE, Multicall2Call } from "../common"; import { + BigNumber, winston, bnZero, getNetworkName, diff --git a/src/clients/bridges/op-stack/OpStackAdapter.ts b/src/clients/bridges/op-stack/OpStackAdapter.ts index f47c07acb..0091ec93b 100644 --- a/src/clients/bridges/op-stack/OpStackAdapter.ts +++ b/src/clients/bridges/op-stack/OpStackAdapter.ts @@ -5,10 +5,10 @@ import { Contract, BigNumber, BigNumberish, + bnZero, TransactionResponse, Event, checkAddressChecksum, - ethers, spreadEventWithBlockNumber, assign, winston, @@ -146,7 +146,7 @@ export class OpStackAdapter extends BaseAdapter { method, args, gasLimitMultiplier, - ethers.constants.Zero, + bnZero, simMode ); } diff --git a/src/clients/bridges/op-stack/UsdcCCTPBridge.ts b/src/clients/bridges/op-stack/UsdcCCTPBridge.ts index c9144dff0..37ac2f932 100644 --- a/src/clients/bridges/op-stack/UsdcCCTPBridge.ts +++ b/src/clients/bridges/op-stack/UsdcCCTPBridge.ts @@ -1,7 +1,7 @@ -import { BigNumber, Contract, Signer } from "ethers"; +import { Contract, Signer } from "ethers"; import { CONTRACT_ADDRESSES, chainIdsToCctpDomains } from "../../../common"; import { BridgeTransactionDetails, OpStackBridge, OpStackEvents } from "./OpStackBridgeInterface"; -import { EventSearchConfig, Provider, TOKEN_SYMBOLS_MAP } from "../../../utils"; +import { BigNumber, EventSearchConfig, Provider, TOKEN_SYMBOLS_MAP } from "../../../utils"; import { cctpAddressToBytes32, retrieveOutstandingCCTPBridgeUSDCTransfers } from "../../../utils/CCTPUtils"; export class UsdcCCTPBridge extends OpStackBridge { diff --git a/src/clients/bridges/op-stack/UsdcTokenSplitterBridge.ts b/src/clients/bridges/op-stack/UsdcTokenSplitterBridge.ts index 887ea3560..310148178 100644 --- a/src/clients/bridges/op-stack/UsdcTokenSplitterBridge.ts +++ b/src/clients/bridges/op-stack/UsdcTokenSplitterBridge.ts @@ -1,7 +1,14 @@ -import { BigNumber, Signer } from "ethers"; +import { Signer } from "ethers"; import { DefaultERC20Bridge } from "./DefaultErc20Bridge"; import { UsdcCCTPBridge } from "./UsdcCCTPBridge"; -import { EventSearchConfig, Provider, TOKEN_SYMBOLS_MAP, assert, compareAddressesSimple } from "../../../utils"; +import { + BigNumber, + EventSearchConfig, + Provider, + TOKEN_SYMBOLS_MAP, + assert, + compareAddressesSimple, +} from "../../../utils"; import { BridgeTransactionDetails, OpStackBridge, OpStackEvents } from "./OpStackBridgeInterface"; import { CONTRACT_ADDRESSES } from "../../../common"; diff --git a/src/clients/bridges/utils.ts b/src/clients/bridges/utils.ts index db3c02a1d..f1ea8cb9c 100644 --- a/src/clients/bridges/utils.ts +++ b/src/clients/bridges/utils.ts @@ -1,6 +1,7 @@ -import { BigNumber, Contract, Event } from "ethers"; +import { Contract, Event } from "ethers"; import { TOKEN_APPROVALS_TO_FIRST_ZERO } from "../../common"; import { + BigNumber, MAX_SAFE_ALLOWANCE, blockExplorerLink, bnZero, diff --git a/src/finalizer/index.ts b/src/finalizer/index.ts index e15f5072c..4cedb298f 100644 --- a/src/finalizer/index.ts +++ b/src/finalizer/index.ts @@ -1,6 +1,6 @@ import { utils as sdkUtils } from "@across-protocol/sdk"; import assert from "assert"; -import { BigNumber, Contract, constants } from "ethers"; +import { Contract } from "ethers"; import { getAddress } from "ethers/lib/utils"; import { groupBy, uniq } from "lodash"; import { AugmentedTransaction, HubPoolClient, MultiCallerClient, TransactionClient } from "../clients"; @@ -17,6 +17,8 @@ import { import { DataworkerConfig } from "../dataworker/DataworkerConfig"; import { SpokePoolClientsByChain } from "../interfaces"; import { + BigNumber, + bnZero, Signer, blockExplorerLink, config, @@ -253,7 +255,7 @@ export async function finalize( const txnClient = new TransactionClient(logger); - let gasEstimation = constants.Zero; + let gasEstimation = bnZero; const batchGasLimit = BigNumber.from(10_000_000); // @dev To avoid running into block gas limit in case the # of finalizations gets too high, keep a running // counter of the approximate gas estimation and cut off the list of finalizations if it gets too high. diff --git a/src/interfaces/InventoryManagement.ts b/src/interfaces/InventoryManagement.ts index 4f2e76c11..37ef2899f 100644 --- a/src/interfaces/InventoryManagement.ts +++ b/src/interfaces/InventoryManagement.ts @@ -1,5 +1,5 @@ -import { BigNumber, utils as ethersUtils } from "ethers"; -import { TOKEN_SYMBOLS_MAP } from "../utils"; +import { utils as ethersUtils } from "ethers"; +import { BigNumber, TOKEN_SYMBOLS_MAP } from "../utils"; export type TokenBalanceConfig = { targetOverageBuffer: BigNumber; // Max multiplier for targetPct, to give flexibility in repayment chain selection. diff --git a/src/interfaces/Report.ts b/src/interfaces/Report.ts index a9a81b6ff..61146b332 100644 --- a/src/interfaces/Report.ts +++ b/src/interfaces/Report.ts @@ -1,4 +1,4 @@ -import { BigNumber } from "ethers"; +import { BigNumber } from "../utils"; export enum BundleAction { PROPOSED = "proposed", diff --git a/src/interfaces/Token.ts b/src/interfaces/Token.ts index a4b6a9354..c8acb7d53 100644 --- a/src/interfaces/Token.ts +++ b/src/interfaces/Token.ts @@ -1,4 +1,4 @@ -import { BigNumber } from "ethers"; +import { BigNumber } from "../utils"; import { SortableEvent } from "."; export interface TokenTransfer extends SortableEvent { diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts index 5d3bfc8e4..fb346ae95 100644 --- a/src/interfaces/index.ts +++ b/src/interfaces/index.ts @@ -1,5 +1,5 @@ -import { BigNumber } from "ethers"; import { interfaces } from "@across-protocol/sdk"; +import { BigNumber } from "../utils"; export * from "./InventoryManagement"; export * from "./SpokePool"; diff --git a/src/monitor/Monitor.ts b/src/monitor/Monitor.ts index 168506624..b7ca6e791 100644 --- a/src/monitor/Monitor.ts +++ b/src/monitor/Monitor.ts @@ -17,16 +17,17 @@ import { convertFromWei, createFormatFunction, ERC20, - ethers, fillStatusArray, blockExplorerLink, blockExplorerLinks, + formatUnits, getNativeTokenAddressForChain, getGasPrice, getNativeTokenSymbol, getNetworkName, getUnfilledDeposits, mapAsync, + parseUnits, providers, toBN, toBNWei, @@ -42,11 +43,9 @@ import { MonitorClients, updateMonitorClients } from "./MonitorClientHelper"; import { MonitorConfig } from "./MonitorConfig"; import { CombinedRefunds } from "../dataworker/DataworkerUtils"; -export const REBALANCE_FINALIZE_GRACE_PERIOD = process.env.REBALANCE_FINALIZE_GRACE_PERIOD - ? Number(process.env.REBALANCE_FINALIZE_GRACE_PERIOD) - : 60 * 60; // 60 minutes, which is the length of the challenge window, so if a rebalance takes longer than this to finalize, // then its finalizing after the subsequent challenge period has started, which is sub-optimal. +export const REBALANCE_FINALIZE_GRACE_PERIOD = Number(process.env.REBALANCE_FINALIZE_GRACE_PERIOD ?? 60 * 60); // bundle frequency. export const ALL_CHAINS_NAME = "All chains"; @@ -381,8 +380,8 @@ export class Monitor { token, account, currentBalance: balances[i].toString(), - warnThreshold: ethers.utils.parseUnits(warnThreshold.toString(), decimalValues[i]), - errorThreshold: ethers.utils.parseUnits(errorThreshold.toString(), decimalValues[i]), + warnThreshold: parseUnits(warnThreshold.toString(), decimalValues[i]), + errorThreshold: parseUnits(errorThreshold.toString(), decimalValues[i]), }; }), }); @@ -397,10 +396,10 @@ export class Monitor { const decimals = decimalValues[i]; let trippedThreshold: { level: "warn" | "error"; threshold: number } | null = null; - if (warnThreshold !== null && balance.lt(ethers.utils.parseUnits(warnThreshold.toString(), decimals))) { + if (warnThreshold !== null && balance.lt(parseUnits(warnThreshold.toString(), decimals))) { trippedThreshold = { level: "warn", threshold: warnThreshold }; } - if (errorThreshold !== null && balance.lt(ethers.utils.parseUnits(errorThreshold.toString(), decimals))) { + if (errorThreshold !== null && balance.lt(parseUnits(errorThreshold.toString(), decimals))) { trippedThreshold = { level: "error", threshold: errorThreshold }; } if (trippedThreshold !== null) { @@ -418,7 +417,7 @@ export class Monitor { text: ` ${getNetworkName(chainId)} ${symbol} balance for ${blockExplorerLink( account, chainId - )} is ${ethers.utils.formatUnits(balance, decimals)}. Threshold: ${trippedThreshold.threshold}`, + )} is ${formatUnits(balance, decimals)}. Threshold: ${trippedThreshold.threshold}`, }; } } @@ -456,7 +455,7 @@ export class Monitor { token, account, currentBalance: currentBalances[i].toString(), - target: ethers.utils.parseUnits(target.toString(), decimalValues[i]), + target: parseUnits(target.toString(), decimalValues[i]), }; }), }); @@ -467,11 +466,11 @@ export class Monitor { refillEnabledBalances.map(async ({ chainId, isHubPool, token, account, target, trigger }, i) => { const currentBalance = currentBalances[i]; const decimals = decimalValues[i]; - const balanceTrigger = ethers.utils.parseUnits(trigger.toString(), decimals); + const balanceTrigger = parseUnits(trigger.toString(), decimals); const isBelowTrigger = currentBalance.lte(balanceTrigger); if (isBelowTrigger) { // Fill balance back to target, not trigger. - const balanceTarget = ethers.utils.parseUnits(target.toString(), decimals); + const balanceTarget = parseUnits(target.toString(), decimals); const deficit = balanceTarget.sub(currentBalance); let canRefill = await this.balanceAllocator.requestBalanceAllocation( chainId, @@ -541,7 +540,7 @@ export class Monitor { method: "loadEthForL2Calls", args: [], message: "Reloaded ETH in HubPool 🫡!", - mrkdwn: `Loaded ${ethers.utils.formatUnits(deficit, decimals)} ETH from ${signerAddress}.`, + mrkdwn: `Loaded ${formatUnits(deficit, decimals)} ETH from ${signerAddress}.`, value: deficit, }); } else { @@ -554,7 +553,7 @@ export class Monitor { const receipt = await tx.wait(); this.logger.info({ at: "Monitor#refillBalances", - message: `Reloaded ${ethers.utils.formatUnits( + message: `Reloaded ${formatUnits( deficit, decimals )} ${nativeSymbolForChain} for ${account} from ${signerAddress} 🫡!`, diff --git a/src/utils/BNUtils.ts b/src/utils/BNUtils.ts index 6fe5d8ad8..ffc1e49be 100644 --- a/src/utils/BNUtils.ts +++ b/src/utils/BNUtils.ts @@ -1,4 +1,8 @@ -import { BigNumber } from "ethers"; +// eslint-disable-next-line no-restricted-imports +import { BigNumber } from "@ethersproject/bignumber"; + +// eslint-disable-next-line no-restricted-imports +export * from "@ethersproject/bignumber"; export function bnComparatorDescending(a: BigNumber, b: BigNumber): -1 | 0 | 1 { if (b.gt(a)) { diff --git a/src/utils/CCTPUtils.ts b/src/utils/CCTPUtils.ts index 172ae3083..69398905e 100644 --- a/src/utils/CCTPUtils.ts +++ b/src/utils/CCTPUtils.ts @@ -1,9 +1,10 @@ import { utils } from "@across-protocol/sdk"; import { TransactionReceipt } from "@ethersproject/abstract-provider"; import axios from "axios"; -import { BigNumber, ethers } from "ethers"; +import { ethers } from "ethers"; import { CONTRACT_ADDRESSES, chainIdsToCctpDomains } from "../common"; import { EventSearchConfig, paginatedEventQuery } from "./EventUtils"; +import { BigNumber } from "./BNUtils"; import { bnZero, compareAddressesSimple } from "./SDKUtils"; import { isDefined } from "./TypeGuards"; import { getProvider } from "./ProviderUtils"; diff --git a/src/utils/SDKUtils.ts b/src/utils/SDKUtils.ts index a417c6de7..f8ccb5287 100644 --- a/src/utils/SDKUtils.ts +++ b/src/utils/SDKUtils.ts @@ -27,7 +27,10 @@ export const { fillStatusArray, fixedPointAdjustment, forEachAsync, + formatEther, + formatUnits, mapAsync, + parseUnits, filterAsync, toBN, bnToHex, diff --git a/src/utils/TokenUtils.ts b/src/utils/TokenUtils.ts index 8e8ddb99e..1ce1c7e63 100644 --- a/src/utils/TokenUtils.ts +++ b/src/utils/TokenUtils.ts @@ -1,7 +1,9 @@ +import { TOKEN_SYMBOLS_MAP } from "@across-protocol/constants"; import { constants, utils } from "@across-protocol/sdk"; import { CONTRACT_ADDRESSES } from "../common"; -import { BigNumberish, utils as ethersUtils } from "ethers"; -import { TOKEN_SYMBOLS_MAP } from "@across-protocol/constants"; +import { BigNumberish } from "./BNUtils"; +import { formatUnits } from "./SDKUtils"; + const { ZERO_ADDRESS } = constants; export const { fetchTokenInfo, getL2TokenAddresses } = utils; @@ -18,5 +20,5 @@ export function getNativeTokenAddressForChain(chainId: number): string { */ export function formatUnitsForToken(symbol: string, amount: BigNumberish): string { const decimals = (TOKEN_SYMBOLS_MAP[symbol]?.decimals as number) ?? 18; - return ethersUtils.formatUnits(amount, decimals); + return formatUnits(amount, decimals); } diff --git a/src/utils/TransactionUtils.ts b/src/utils/TransactionUtils.ts index 494256a63..086d4e47c 100644 --- a/src/utils/TransactionUtils.ts +++ b/src/utils/TransactionUtils.ts @@ -221,6 +221,6 @@ export function getTarget(targetAddress: string): } } -function scaleByNumber(amount: ethers.BigNumber, scaling: number) { +function scaleByNumber(amount: BigNumber, scaling: number) { return amount.mul(toBNWei(scaling)).div(fixedPoint); } diff --git a/src/utils/index.ts b/src/utils/index.ts index 4c3e8f0bc..a2fabbf0f 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -14,8 +14,6 @@ export { providers, utils, BaseContract, - BigNumber, - BigNumberish, Contract, ContractFactory, Event, diff --git a/test/Dataworker.buildRoots.ts b/test/Dataworker.buildRoots.ts index 9dba449f7..3af6f3c13 100644 --- a/test/Dataworker.buildRoots.ts +++ b/test/Dataworker.buildRoots.ts @@ -5,7 +5,6 @@ import { assert, bnZero, fixedPointAdjustment } from "../src/utils"; import { amountToDeposit, destinationChainId, mockTreeRoot, originChainId, repaymentChainId } from "./constants"; import { setupFastDataworker } from "./fixtures/Dataworker.Fixture"; import { - BigNumber, Contract, SignerWithAddress, buildV3SlowRelayLeaves, @@ -279,7 +278,7 @@ describe("Dataworker: Build merkle roots", async function () { const expectedRunningBalances: RunningBalances = { // Note: There should be no origin chain entry here since there were no deposits. [originChainId]: { - [l1Token_1.address]: BigNumber.from(0), + [l1Token_1.address]: bnZero, }, }; expect(expectedRunningBalances).to.deep.equal(merkleRoot1.runningBalances); diff --git a/test/InventoryClient.InventoryRebalance.ts b/test/InventoryClient.InventoryRebalance.ts index 424a9ff4f..d2d55a8cf 100644 --- a/test/InventoryClient.InventoryRebalance.ts +++ b/test/InventoryClient.InventoryRebalance.ts @@ -27,10 +27,11 @@ import { ERC20, fixedPointAdjustment as fixedPoint, getNetworkName, + parseUnits, TOKEN_SYMBOLS_MAP, } from "../src/utils"; -const toMegaWei = (num: string | number | BigNumber) => ethers.utils.parseUnits(num.toString(), 6); +const toMegaWei = (num: string | number | BigNumber) => parseUnits(num.toString(), 6); let hubPoolClient: MockHubPoolClient, adapterManager: MockAdapterManager, tokenClient: MockTokenClient; let bundleDataClient: MockBundleDataClient; diff --git a/test/InventoryClient.RefundChain.ts b/test/InventoryClient.RefundChain.ts index 9906214c4..ed34ac41f 100644 --- a/test/InventoryClient.RefundChain.ts +++ b/test/InventoryClient.RefundChain.ts @@ -19,7 +19,7 @@ import { import { ConfigStoreClient, InventoryClient } from "../src/clients"; // Tested import { CrossChainTransferClient } from "../src/clients/bridges"; import { Deposit, InventoryConfig } from "../src/interfaces"; -import { CHAIN_IDs, ZERO_ADDRESS, bnZero, getNetworkName, TOKEN_SYMBOLS_MAP } from "../src/utils"; +import { CHAIN_IDs, ZERO_ADDRESS, bnZero, getNetworkName, parseUnits, TOKEN_SYMBOLS_MAP } from "../src/utils"; import { MockAdapterManager, MockBundleDataClient, @@ -51,7 +51,7 @@ describe("InventoryClient: Refund chain selection", async function () { l2TokensForUsdc[chainId] = TOKEN_SYMBOLS_MAP["USDC.e"].addresses[chainId]; }); - const toMegaWei = (num: string | number | BigNumber) => ethers.utils.parseUnits(num.toString(), 6); + const toMegaWei = (num: string | number | BigNumber) => parseUnits(num.toString(), 6); // Configure thresholds percentages as 10% optimism, 5% polygon and 5% Arbitrum with a target being threshold +2%. const targetOverageBuffer = toWei(1); const inventoryConfig: InventoryConfig = { diff --git a/test/TransactionClient.ts b/test/TransactionClient.ts index aba9e933e..05f1dfc8b 100644 --- a/test/TransactionClient.ts +++ b/test/TransactionClient.ts @@ -1,6 +1,5 @@ -import { BigNumber } from "ethers"; import { AugmentedTransaction } from "../src/clients"; -import { isDefined, TransactionResponse, TransactionSimulationResult } from "../src/utils"; +import { BigNumber, isDefined, TransactionResponse, TransactionSimulationResult } from "../src/utils"; import { CHAIN_ID_TEST_LIST as chainIds } from "./constants"; import { MockedTransactionClient, txnClientPassResult } from "./mocks/MockTransactionClient"; import { createSpyLogger, Contract, expect, randomAddress, winston, toBN } from "./utils"; diff --git a/test/mocks/MockCrossChainTransferClient.ts b/test/mocks/MockCrossChainTransferClient.ts index 66bb70ecc..a8ca0da2a 100644 --- a/test/mocks/MockCrossChainTransferClient.ts +++ b/test/mocks/MockCrossChainTransferClient.ts @@ -1,7 +1,7 @@ -import { BigNumber } from "ethers"; +import { BigNumber, bnZero } from "../../src/utils"; import { CrossChainTransferClient } from "../../src/clients/bridges"; export class MockCrossChainTransferClient extends CrossChainTransferClient { - crossChainTransferAmount: BigNumber = BigNumber.from(0); + crossChainTransferAmount = bnZero; constructor() { super(null, null, null); } diff --git a/test/mocks/MockInventoryClient.ts b/test/mocks/MockInventoryClient.ts index 944723c3d..2c0b143a2 100644 --- a/test/mocks/MockInventoryClient.ts +++ b/test/mocks/MockInventoryClient.ts @@ -1,11 +1,12 @@ import { Deposit, InventoryConfig } from "../../src/interfaces"; import { BundleDataClient, HubPoolClient, InventoryClient, Rebalance, TokenClient } from "../../src/clients"; import { AdapterManager, CrossChainTransferClient } from "../../src/clients/bridges"; -import { BigNumber } from "ethers"; +import { BigNumber, bnZero } from "../../src/utils"; import winston from "winston"; + export class MockInventoryClient extends InventoryClient { possibleRebalances: Rebalance[] = []; - balanceOnChain: BigNumber | undefined = BigNumber.from(0); + balanceOnChain: BigNumber | undefined = bnZero; excessRunningBalancePcts: { [l1Token: string]: { [chainId: number]: BigNumber } } = {}; constructor( diff --git a/test/mocks/MockTransactionClient.ts b/test/mocks/MockTransactionClient.ts index c337a8b16..e51c86ed4 100644 --- a/test/mocks/MockTransactionClient.ts +++ b/test/mocks/MockTransactionClient.ts @@ -1,7 +1,7 @@ -import { BigNumber, ethers } from "ethers"; +import { ethers } from "ethers"; import { random } from "lodash"; import { AugmentedTransaction, TransactionClient } from "../../src/clients"; -import { TransactionResponse, TransactionSimulationResult } from "../../src/utils"; +import { BigNumber, TransactionResponse, TransactionSimulationResult } from "../../src/utils"; import { toBNWei, winston } from "../utils"; export const txnClientPassResult = "pass"; diff --git a/test/utils/utils.ts b/test/utils/utils.ts index b76f84f88..409210a51 100644 --- a/test/utils/utils.ts +++ b/test/utils/utils.ts @@ -3,14 +3,14 @@ import { TokenRolesEnum } from "@uma/common"; import { SpyTransport, bigNumberFormatter } from "@uma/logger"; import { AcrossConfigStore, FakeContract } from "@across-protocol/contracts"; import { constants, utils as sdkUtils } from "@across-protocol/sdk"; -import { BigNumber, Contract, providers } from "ethers"; +import { Contract, providers } from "ethers"; import chai, { assert, expect } from "chai"; import chaiExclude from "chai-exclude"; import sinon from "sinon"; import winston from "winston"; import { GLOBAL_CONFIG_STORE_KEYS } from "../../src/clients"; import { Deposit, DepositWithBlock, FillWithBlock, SlowFillLeaf } from "../../src/interfaces"; -import { isDefined, spreadEvent, toBN, toBNWei, toWei, utf8ToHex, ZERO_ADDRESS } from "../../src/utils"; +import { BigNumber, isDefined, spreadEvent, toBN, toBNWei, toWei, utf8ToHex, ZERO_ADDRESS } from "../../src/utils"; import { DEFAULT_BLOCK_RANGE_FOR_CHAIN, MAX_L1_TOKENS_PER_POOL_REBALANCE_LEAF, From 97cb0beb44da4c7e668442522aab5a4c5fa94116 Mon Sep 17 00:00:00 2001 From: bmzig <57361391+bmzig@users.noreply.github.com> Date: Fri, 20 Sep 2024 10:19:09 -0500 Subject: [PATCH 3/8] fix(tests): handle edge case in probabalistic tests (#1831) Signed-off-by: bennett --- test/TryMulticallClient.ts | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/test/TryMulticallClient.ts b/test/TryMulticallClient.ts index d3d4685ce..cb18079b0 100644 --- a/test/TryMulticallClient.ts +++ b/test/TryMulticallClient.ts @@ -386,11 +386,14 @@ describe("TryMulticallClient", async function () { } as AugmentedTransaction; multiCaller.enqueueTransaction(txn); } + const nBundles = nSucceed === 0 ? 0 : 1; expect(multiCaller.transactionCount()).to.equal(nTxns); const results = await multiCaller.executeTxnQueue(chainId, false); - expect(results.length).to.equal(1); - const returnData = results[0]; - expect(returnData.length).to.equal(nSucceed); + expect(results.length).to.equal(nBundles); + if (nBundles !== 0) { + const returnData = results[0]; + expect(returnData.length).to.equal(nSucceed); + } } }); it("Does not combine separate multicall bundles together", async function () { @@ -403,6 +406,7 @@ describe("TryMulticallClient", async function () { multiCaller.chunkSize[chainId] = chunkSize; let nSucceed = 0; const nTxns = 20; + const queueCopy = []; for (let j = 0; j < nTxns; ++j) { const succeed = Math.floor(Math.random() * 2); // 0 is fail, 1 is pass. nSucceed += succeed; @@ -417,10 +421,21 @@ describe("TryMulticallClient", async function () { mrkdwn: `This transaction is expected to ${succeed === 0 ? "fail" : "pass"} simulation.`, } as AugmentedTransaction; multiCaller.enqueueTransaction(txn); + queueCopy.push(succeed); + } + // Determine if all 5 transactions in a bundle failed. This assumes chunkSize | nTxns. + let emptyBundles = 0; + const nBundles = nTxns / chunkSize; + for (let i = 0; i < nBundles; i++) { + const wdow = i * chunkSize; + const sum = queueCopy.slice(wdow, wdow + chunkSize).reduce((sum, current) => sum + current, 0); + if (sum === 0) { + emptyBundles++; + } } expect(multiCaller.transactionCount()).to.equal(nTxns); const results = await multiCaller.executeTxnQueue(chainId, false); - expect(results.length).to.equal(nTxns / chunkSize); // With a chunk size of 5 and 20 transactions, 4 bundles should have been built. + expect(results.length).to.equal(nBundles - emptyBundles); // With a chunk size of 5 and 20 transactions, 4 bundles should have been built. expect(results.reduce((sum, result) => sum + result.length, 0)).to.equal(nSucceed); } }); From 41d9d7327c575bd28fac6aa9cc0b05f6245f5ef3 Mon Sep 17 00:00:00 2001 From: Paul <108695806+pxrl@users.noreply.github.com> Date: Sun, 22 Sep 2024 01:54:10 +0200 Subject: [PATCH 4/8] fix(dataworker): Don't permit out-of-order block ranges (#1832) Without this check, the code produces a Lisk block range of [6063320, 6063319] when validating the proposal associated with dispute 0x5255006f0a66f151d6a419b876dd673f94ec85a97cd3570bc54f7def7a6422a6. This originally arose because the primary RPC provider used for determining the latest block number on Lisk had an outage and began severely lagging the head of the chain, such that the proposer was not able to increment its end block number past the previous proposal's end block number due to the Lisk block buffer imposed on new proposals. --- src/dataworker/Dataworker.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/dataworker/Dataworker.ts b/src/dataworker/Dataworker.ts index a800eb1c5..215a7d224 100644 --- a/src/dataworker/Dataworker.ts +++ b/src/dataworker/Dataworker.ts @@ -734,10 +734,12 @@ export class Dataworker { }; } - const blockRangesImpliedByBundleEndBlocks = widestPossibleExpectedBlockRange.map((blockRange, index) => [ - blockRange[0], - rootBundle.bundleEvaluationBlockNumbers[index], - ]); + const blockRangesImpliedByBundleEndBlocks = widestPossibleExpectedBlockRange.map((blockRange, index) => { + // Block ranges must be coherent; otherwise the chain is soft-paused. + const [startBlock, endBlock] = [blockRange[0], rootBundle.bundleEvaluationBlockNumbers[index]]; + return endBlock > startBlock ? [startBlock, endBlock] : [endBlock, endBlock]; + }); + const mainnetBlockRange = blockRangesImpliedByBundleEndBlocks[0]; const mainnetBundleStartBlock = mainnetBlockRange[0]; const chainIds = this.clients.configStoreClient.getChainIdIndicesForBlock(mainnetBundleStartBlock); From 9cf491b2d51e3d54a23ccd93d75cf57923d5dc84 Mon Sep 17 00:00:00 2001 From: Paul <108695806+pxrl@users.noreply.github.com> Date: Mon, 23 Sep 2024 11:41:25 +0200 Subject: [PATCH 5/8] improve(TransactionUtils): Permit env-based nonce reset (#1834) Permit operators to trigger a nonce reset in case of a queue of pending transactions. --- src/utils/TransactionUtils.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/utils/TransactionUtils.ts b/src/utils/TransactionUtils.ts index 086d4e47c..eedadef5e 100644 --- a/src/utils/TransactionUtils.ts +++ b/src/utils/TransactionUtils.ts @@ -28,6 +28,8 @@ export type TransactionSimulationResult = { const { isError, isEthersError } = typeguards; +const nonceReset: { [chainId: number]: boolean } = {}; + const txnRetryErrors = new Set(["INSUFFICIENT_FUNDS", "NONCE_EXPIRED", "REPLACEMENT_UNDERPRICED"]); const expectedRpcErrorMessages = new Set(["nonce has already been used", "intrinsic gas too low"]); const txnRetryable = (error?: unknown): boolean => { @@ -61,7 +63,13 @@ export async function runTransaction( nonce: number | null = null, retriesRemaining = 2 ): Promise { - const chainId = (await contract.provider.getNetwork()).chainId; + const { provider } = contract; + const { chainId } = await provider.getNetwork(); + + if (process.env[`ACROSS_RESET_NONCE_${chainId}`] && !nonceReset[chainId]) { + nonce = await provider.getTransactionCount(await contract.signer.getAddress()); + nonceReset[chainId] = true; + } try { const priorityFeeScaler = @@ -71,7 +79,7 @@ export async function runTransaction( Number(process.env[`MAX_FEE_PER_GAS_SCALER_${chainId}`] || process.env.MAX_FEE_PER_GAS_SCALER) || DEFAULT_GAS_FEE_SCALERS[chainId]?.maxFeePerGasScaler; - const gas = await getGasPrice(contract.provider, priorityFeeScaler, maxFeePerGasScaler); + const gas = await getGasPrice(provider, priorityFeeScaler, maxFeePerGasScaler); logger.debug({ at: "TxUtil", From f749ca324ab172d37d785c00a1640d58aecb9dc0 Mon Sep 17 00:00:00 2001 From: Paul <108695806+pxrl@users.noreply.github.com> Date: Tue, 24 Sep 2024 17:08:45 +0200 Subject: [PATCH 6/8] refactor: Replace ethers Event with Log (#1830) This follows a similar change in the SDK, with the same motivations. Reverting to Log instead of Event does not sacrifice any functionality but makes it easier to migrate away from ethers v5. --- .eslintrc.js | 5 +- package.json | 2 +- src/adapter/bridges/OpStackWethBridge.ts | 6 +-- src/adapter/utils.ts | 5 +- src/clients/SpokePoolClient.ts | 9 ++-- src/clients/TokenTransferClient.ts | 7 ++- .../bridges/op-stack/OpStackAdapter.ts | 5 +- .../op-stack/OpStackBridgeInterface.ts | 13 ++--- src/clients/bridges/op-stack/WethBridge.ts | 6 +-- src/clients/bridges/utils.ts | 7 +-- src/interfaces/index.ts | 1 + src/libexec/RelayerSpokePoolIndexer.ts | 20 ++++---- src/scripts/validateRunningBalances.ts | 5 +- src/utils/CCTPUtils.ts | 3 +- src/utils/EventUtils.ts | 34 +++++--------- src/utils/index.ts | 1 - test/EventManager.ts | 24 ++-------- test/Relayer.IndexedSpokePoolClient.ts | 47 ++++++------------- yarn.lock | 8 ++-- 19 files changed, 78 insertions(+), 130 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index dfc7b7e9b..d78f335a7 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -42,7 +42,10 @@ module.exports = { "error", { patterns: [{ group: ["@ethersproject/bignumber"], message: "Use 'src/utils/BNUtils' instead" }], - paths: [{ name: "ethers", importNames: ["BigNumber"], message: "Use 'src/utils/BNUtils' instead" }], + paths: [ + { name: "ethers", importNames: ["BigNumber"], message: "Use 'src/utils/BNUtils' instead" }, + { name: "ethers", importNames: ["Event"], message: "Use Log from 'src/interfaces/Common' instead" }, + ], }, ], }, diff --git a/package.json b/package.json index 53d1763b6..5e84d3e23 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "dependencies": { "@across-protocol/constants": "^3.1.14", "@across-protocol/contracts": "^3.0.10", - "@across-protocol/sdk": "^3.1.36", + "@across-protocol/sdk": "^3.2.0", "@arbitrum/sdk": "^3.1.3", "@consensys/linea-sdk": "^0.2.1", "@defi-wonderland/smock": "^2.3.5", diff --git a/src/adapter/bridges/OpStackWethBridge.ts b/src/adapter/bridges/OpStackWethBridge.ts index e07f47906..778da1e69 100644 --- a/src/adapter/bridges/OpStackWethBridge.ts +++ b/src/adapter/bridges/OpStackWethBridge.ts @@ -5,11 +5,11 @@ import { paginatedEventQuery, Signer, Provider, - Event, ZERO_ADDRESS, TOKEN_SYMBOLS_MAP, } from "../../utils"; import { CONTRACT_ADDRESSES } from "../../common"; +import { Log } from "../../interfaces"; import { matchL2EthDepositAndWrapEvents, processEvent } from "../utils"; import { utils } from "@across-protocol/sdk"; import { BridgeTransactionDetails, BaseBridgeAdapter, BridgeEvents } from "./BaseBridgeAdapter"; @@ -67,7 +67,7 @@ export class OpStackWethBridge extends BaseBridgeAdapter { }); } - private convertEventListToBridgeEvents(events: Event[]): BridgeEvents { + private convertEventListToBridgeEvents(events: Log[]): BridgeEvents { return { [this.resolveL2TokenAddress(TOKEN_SYMBOLS_MAP.WETH.addresses[this.hubChainId])]: events.map((event) => processEvent(event, "_amount", "_to", "_from") @@ -176,7 +176,7 @@ export class OpStackWethBridge extends BaseBridgeAdapter { fromAddress: string, eventConfig: EventSearchConfig, l2Weth = this.l2Weth - ): Promise { + ): Promise { return paginatedEventQuery(l2Weth, l2Weth.filters.Deposit(fromAddress), eventConfig); } } diff --git a/src/adapter/utils.ts b/src/adapter/utils.ts index 11de44697..ebb8d263d 100644 --- a/src/adapter/utils.ts +++ b/src/adapter/utils.ts @@ -1,4 +1,3 @@ -import { Event } from "ethers"; import { TOKEN_APPROVALS_TO_FIRST_ZERO } from "../common"; import { BigNumber, @@ -13,7 +12,7 @@ import { winston, } from "../utils"; import { BridgeEvent } from "./bridges/BaseBridgeAdapter"; -import { SortableEvent } from "../interfaces"; +import { Log, SortableEvent } from "../interfaces"; import { ExpandedERC20 } from "@across-protocol/contracts"; export { @@ -55,7 +54,7 @@ export async function approveTokens( return ["*Approval transactions:*", ...approvalMarkdwn].join("\n"); } -export function processEvent(event: Event, amountField: string, toField: string, fromField: string): BridgeEvent { +export function processEvent(event: Log, amountField: string, toField: string, fromField: string): BridgeEvent { const eventSpread = spreadEventWithBlockNumber(event) as SortableEvent & { amount: BigNumber; to: string; diff --git a/src/clients/SpokePoolClient.ts b/src/clients/SpokePoolClient.ts index e34ae24a0..dc2518cb6 100644 --- a/src/clients/SpokePoolClient.ts +++ b/src/clients/SpokePoolClient.ts @@ -1,7 +1,8 @@ import assert from "assert"; import { ChildProcess, spawn } from "child_process"; -import { Contract, Event } from "ethers"; +import { Contract } from "ethers"; import { clients, utils as sdkUtils } from "@across-protocol/sdk"; +import { Log } from "../interfaces"; import { CHAIN_MAX_BLOCK_LOOKBACK, RELAYER_DEFAULT_SPOKEPOOL_INDEXER } from "../common/Constants"; import { EventSearchConfig, getNetworkName, isDefined, MakeOptional, winston } from "../utils"; import { EventsAddedMessage, EventRemovedMessage } from "../utils/SuperstructUtils"; @@ -43,8 +44,8 @@ export class IndexedSpokePoolClient extends clients.SpokePoolClient { private pendingCurrentTime: number; private pendingOldestTime: number; - private pendingEvents: Event[][]; - private pendingEventsRemoved: Event[]; + private pendingEvents: Log[][]; + private pendingEventsRemoved: Log[]; constructor( readonly logger: winston.Logger, @@ -194,7 +195,7 @@ export class IndexedSpokePoolClient extends clients.SpokePoolClient { * @param event An Ethers event instance. * @returns void */ - protected removeEvent(event: Event): boolean { + protected removeEvent(event: Log): boolean { let removed = false; const eventIdx = this.queryableEventNames.indexOf(event.event); const pendingEvents = this.pendingEvents[eventIdx]; diff --git a/src/clients/TokenTransferClient.ts b/src/clients/TokenTransferClient.ts index 7eba342c1..626a2a7ef 100644 --- a/src/clients/TokenTransferClient.ts +++ b/src/clients/TokenTransferClient.ts @@ -3,12 +3,11 @@ import { winston, assign, ERC20, - Event, Contract, paginatedEventQuery, spreadEventWithBlockNumber, } from "../utils"; -import { TokenTransfer, TransfersByChain } from "../interfaces"; +import { Log, TokenTransfer, TransfersByChain } from "../interfaces"; import { Provider } from "@ethersproject/abstract-provider"; export class TokenTransferClient { @@ -52,7 +51,7 @@ export class TokenTransferClient { this.querySendAndReceiveEvents(tokenContract, monitoredAddress, searchConfigByChainIds[chainId]) ) ); - const transferEventsPerToken: { [tokenAddress: string]: Event[][] } = Object.fromEntries( + const transferEventsPerToken: { [tokenAddress: string]: Log[][] } = Object.fromEntries( transferEventsList.map((transferEvents, i) => [tokenContracts[i].address, transferEvents]) ); @@ -89,7 +88,7 @@ export class TokenTransferClient { } // Returns outgoing and incoming transfers for the specified tokenContract and address. - querySendAndReceiveEvents(tokenContract: Contract, address: string, config: EventSearchConfig): Promise { + querySendAndReceiveEvents(tokenContract: Contract, address: string, config: EventSearchConfig): Promise { const eventFilters = [[address], [undefined, address]]; return Promise.all( eventFilters.map((eventFilter) => diff --git a/src/clients/bridges/op-stack/OpStackAdapter.ts b/src/clients/bridges/op-stack/OpStackAdapter.ts index 0091ec93b..3e963179c 100644 --- a/src/clients/bridges/op-stack/OpStackAdapter.ts +++ b/src/clients/bridges/op-stack/OpStackAdapter.ts @@ -7,7 +7,6 @@ import { BigNumberish, bnZero, TransactionResponse, - Event, checkAddressChecksum, spreadEventWithBlockNumber, assign, @@ -16,7 +15,7 @@ import { } from "../../../utils"; import { SpokePoolClient } from "../../"; import { BaseAdapter } from "../"; -import { SortableEvent, OutstandingTransfers } from "../../../interfaces"; +import { Log, SortableEvent, OutstandingTransfers } from "../../../interfaces"; import { OpStackBridge } from "./OpStackBridgeInterface"; import { WethBridge } from "./WethBridge"; import { DefaultERC20Bridge } from "./DefaultErc20Bridge"; @@ -82,7 +81,7 @@ export class OpStackAdapter extends BaseAdapter { const { l1SearchConfig, l2SearchConfig } = this.getUpdatedSearchConfigs(); const availableL1Tokens = this.filterSupportedTokens(l1Tokens); - const processEvent = (event: Event) => { + const processEvent = (event: Log) => { const eventSpread = spreadEventWithBlockNumber(event) as SortableEvent & { _amount: BigNumberish; _to: string; diff --git a/src/clients/bridges/op-stack/OpStackBridgeInterface.ts b/src/clients/bridges/op-stack/OpStackBridgeInterface.ts index 530122f0b..c546cef9f 100644 --- a/src/clients/bridges/op-stack/OpStackBridgeInterface.ts +++ b/src/clients/bridges/op-stack/OpStackBridgeInterface.ts @@ -1,12 +1,5 @@ -import { - Contract, - BigNumber, - Event, - EventSearchConfig, - Signer, - Provider, - getTranslatedTokenAddress, -} from "../../../utils"; +import { Log } from "../../../interfaces"; +import { Contract, BigNumber, EventSearchConfig, Signer, Provider, getTranslatedTokenAddress } from "../../../utils"; export interface BridgeTransactionDetails { readonly contract: Contract; @@ -14,7 +7,7 @@ export interface BridgeTransactionDetails { readonly args: unknown[]; } -export type OpStackEvents = { [l2Token: string]: Event[] }; +export type OpStackEvents = { [l2Token: string]: Log[] }; export abstract class OpStackBridge { constructor( diff --git a/src/clients/bridges/op-stack/WethBridge.ts b/src/clients/bridges/op-stack/WethBridge.ts index a605d893f..fe68671c6 100644 --- a/src/clients/bridges/op-stack/WethBridge.ts +++ b/src/clients/bridges/op-stack/WethBridge.ts @@ -7,9 +7,9 @@ import { Signer, Provider, ZERO_ADDRESS, - Event, TOKEN_SYMBOLS_MAP, } from "../../../utils"; +import { Log } from "../../../interfaces"; import { CONTRACT_ADDRESSES } from "../../../common"; import { matchL2EthDepositAndWrapEvents } from "../utils"; import { utils } from "@across-protocol/sdk"; @@ -81,7 +81,7 @@ export class WethBridge extends OpStackBridge { }; } - private convertEventListToOpStackEvents(events: Event[]): OpStackEvents { + private convertEventListToOpStackEvents(events: Log[]): OpStackEvents { return { [this.resolveL2TokenAddress(TOKEN_SYMBOLS_MAP.WETH.addresses[this.hubChainId])]: events, }; @@ -195,7 +195,7 @@ export class WethBridge extends OpStackBridge { fromAddress: string, eventConfig: EventSearchConfig, l2Weth = this.l2Weth - ): Promise { + ): Promise { return paginatedEventQuery(l2Weth, l2Weth.filters.Deposit(fromAddress), eventConfig); } } diff --git a/src/clients/bridges/utils.ts b/src/clients/bridges/utils.ts index f1ea8cb9c..ff45b6b06 100644 --- a/src/clients/bridges/utils.ts +++ b/src/clients/bridges/utils.ts @@ -1,4 +1,5 @@ -import { Contract, Event } from "ethers"; +import { Contract } from "ethers"; +import { Log } from "../../interfaces"; import { TOKEN_APPROVALS_TO_FIRST_ZERO } from "../../common"; import { BigNumber, @@ -32,9 +33,9 @@ import { * @returns List of l2EthDepositEvents followed by a l2WrapEvent. None of the * l2EthDepositEvents will match with the same l2WrapEvent. */ -export function matchL2EthDepositAndWrapEvents(l2EthDepositEvents: Event[], _l2WrapEvents: Event[]): Event[] { +export function matchL2EthDepositAndWrapEvents(l2EthDepositEvents: Log[], _l2WrapEvents: Log[]): Log[] { const l2WrapEvents = [..._l2WrapEvents]; // deep-copy because we're going to modify this in-place. - return l2EthDepositEvents.filter((l2EthDepositEvent: Event) => { + return l2EthDepositEvents.filter((l2EthDepositEvent) => { // Search from left to right to find the first following wrap event. const followingWrapEventIndex = l2WrapEvents.findIndex( (wrapEvent) => wrapEvent.blockNumber >= l2EthDepositEvent.blockNumber diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts index fb346ae95..783e38024 100644 --- a/src/interfaces/index.ts +++ b/src/interfaces/index.ts @@ -22,6 +22,7 @@ export interface OutstandingTransfers { } // Common interfaces +export type Log = interfaces.Log; export type SortableEvent = interfaces.SortableEvent; export type BigNumberForToken = interfaces.BigNumberForToken; diff --git a/src/libexec/RelayerSpokePoolIndexer.ts b/src/libexec/RelayerSpokePoolIndexer.ts index 2a1cfb630..951f528e7 100644 --- a/src/libexec/RelayerSpokePoolIndexer.ts +++ b/src/libexec/RelayerSpokePoolIndexer.ts @@ -1,8 +1,9 @@ import assert from "assert"; import minimist from "minimist"; -import { Contract, Event, EventFilter, providers as ethersProviders, utils as ethersUtils } from "ethers"; +import { Contract, EventFilter, providers as ethersProviders, utils as ethersUtils } from "ethers"; import { utils as sdkUtils } from "@across-protocol/sdk"; import * as utils from "../../scripts/utils"; +import { Log } from "../interfaces"; import { SpokePoolClientMessage } from "../clients"; import { disconnectRedisClients, @@ -18,7 +19,6 @@ import { getRedisCache, getWSProviders, Logger, - mangleEventArgs, paginatedEventQuery, sortEventsAscending, winston, @@ -73,17 +73,17 @@ function getEventFilterArgs(relayer?: string): { [event: string]: string[] } { * process (if defined). * @param blockNumber Block number up to which the update applies. * @param currentTime The SpokePool timestamp at blockNumber. - * @param events An array of Ethers Event objects to be submitted. + * @param events An array of Log objects to be submitted. * @returns void */ -function postEvents(blockNumber: number, currentTime: number, events: Event[]): void { +function postEvents(blockNumber: number, currentTime: number, events: Log[]): void { if (!isDefined(process.send) || stop) { return; } // Drop the array component of event.args and retain the named k/v pairs, // otherwise stringification tends to retain only the array. - events = sortEventsAscending(events.map(mangleEventArgs)); + events = sortEventsAscending(events); const message: SpokePoolClientMessage = { blockNumber, @@ -97,16 +97,16 @@ function postEvents(blockNumber: number, currentTime: number, events: Event[]): /** * Given an event removal notification, post the message to the parent process. - * @param event Ethers Event instance. + * @param event Log instance. * @returns void */ -function removeEvent(event: Event): void { +function removeEvent(event: Log): void { if (!isDefined(process.send) || stop) { return; } const message: SpokePoolClientMessage = { - event: JSON.stringify(mangleEventArgs(event), sdkUtils.jsonReplacerWithBigNumbers), + event: JSON.stringify(event, sdkUtils.jsonReplacerWithBigNumbers), }; process.send(JSON.stringify(message)); } @@ -127,7 +127,7 @@ async function scrapeEvents(spokePool: Contract, eventName: string, opts: Scrape let tStart: number, tStop: number; - const pollEvents = async (filter: EventFilter, searchConfig: EventSearchConfig): Promise => { + const pollEvents = async (filter: EventFilter, searchConfig: EventSearchConfig): Promise => { tStart = performance.now(); const events = await paginatedEventQuery(spokePool, filter, searchConfig); tStop = performance.now(); @@ -187,7 +187,7 @@ async function listen( eventNames.forEach((eventName) => { const filter = getEventFilter(spokePool, eventName, filterArgs[eventName]); spokePool.connect(provider).on(filter, (...rawEvent) => { - const event = rawEvent.at(-1); + const event = sdkUtils.eventToLog(rawEvent.at(-1)); if (event.removed) { eventMgr.remove(event, host); // Notify the parent immediately in case the event was already submitted. diff --git a/src/scripts/validateRunningBalances.ts b/src/scripts/validateRunningBalances.ts index 9c6c6593a..a208205cb 100644 --- a/src/scripts/validateRunningBalances.ts +++ b/src/scripts/validateRunningBalances.ts @@ -36,7 +36,6 @@ import { config, Logger, toBN, - Event, fromWei, isDefined, Contract, @@ -56,7 +55,7 @@ import { } from "../utils"; import { createDataworker } from "../dataworker"; import { getBlockForChain } from "../dataworker/DataworkerUtils"; -import { ProposedRootBundle, SpokePoolClientsByChain, SlowFillLeaf } from "../interfaces"; +import { Log, ProposedRootBundle, SpokePoolClientsByChain, SlowFillLeaf } from "../interfaces"; import { CONTRACT_ADDRESSES, constructSpokePoolClientsWithStartBlocks, updateSpokePoolClients } from "../common"; import { createConsoleTransport } from "@uma/logger"; @@ -212,7 +211,7 @@ export async function runScript(_logger: winston.Logger, baseSigner: Signer): Pr `Looking for previous net send amount between blocks ${previousBundleEndBlockForChain.toNumber()} and ${bundleEndBlockForChain.toNumber()}` ); const spokePoolAddress = spokePoolClients[leaf.chainId].spokePool.address; - let depositsToSpokePool: Event[]; + let depositsToSpokePool: Log[]; // Handle the case that L1-->L2 deposits for some chains for ETH do not emit Transfer events, but // emit other events instead. This is the case for OpStack chains which emit DepositFinalized events // including the L1 and L2 ETH (native gas token) addresses. diff --git a/src/utils/CCTPUtils.ts b/src/utils/CCTPUtils.ts index 69398905e..38d1c5170 100644 --- a/src/utils/CCTPUtils.ts +++ b/src/utils/CCTPUtils.ts @@ -2,6 +2,7 @@ import { utils } from "@across-protocol/sdk"; import { TransactionReceipt } from "@ethersproject/abstract-provider"; import axios from "axios"; import { ethers } from "ethers"; +import { Log } from "../interfaces"; import { CONTRACT_ADDRESSES, chainIdsToCctpDomains } from "../common"; import { EventSearchConfig, paginatedEventQuery } from "./EventUtils"; import { BigNumber } from "./BNUtils"; @@ -80,7 +81,7 @@ export async function retrieveOutstandingCCTPBridgeUSDCTransfers( sourceChainId: number, destinationChainId: number, fromAddress: string -): Promise { +): Promise { const sourceDomain = chainIdsToCctpDomains[sourceChainId]; const targetDestinationDomain = chainIdsToCctpDomains[destinationChainId]; diff --git a/src/utils/EventUtils.ts b/src/utils/EventUtils.ts index 561b5add8..94ae6a059 100644 --- a/src/utils/EventUtils.ts +++ b/src/utils/EventUtils.ts @@ -1,10 +1,11 @@ import assert from "assert"; import winston from "winston"; -import { Event, utils as ethersUtils } from "ethers"; +import { utils as ethersUtils } from "ethers"; +import { utils as sdkUtils } from "@across-protocol/sdk"; +import { Log } from "../interfaces"; import { getNetworkName } from "./NetworkUtils"; import { dedupArray } from "./SDKUtils"; import { isDefined } from "./TypeGuards"; -import { utils as sdkUtils } from "@across-protocol/sdk"; export type EventSearchConfig = sdkUtils.EventSearchConfig; @@ -21,17 +22,6 @@ export const { spreadEventWithBlockNumber, } = sdkUtils; -/** - * Sub out the array component of `args` to ensure it's correctly stringified before transmission. - * Stringification of an Ethers event can produce unreliable results for Event.args and it can be consolidated into an - * array, dropping the named k/v pairs. - * @param event An Ethers event. - * @returns A modified Ethers event, ensuring that the Event.args object consists of named k/v pairs. - */ -export function mangleEventArgs(event: Event): Event { - return { ...event, args: sdkUtils.spreadEvent(event.args) }; -} - /** * @notice Returns an array with the same length as the passed in Event array where each index is assigned a new index * that states its relative position to other events with the same transaction hash. If two or more of the input @@ -52,7 +42,7 @@ export function getUniqueLogIndex(events: { transactionHash: string }[]): number return logIndexesForMessage; } -type QuorumEvent = Event & { providers: string[] }; +type QuorumEvent = Log & { providers: string[] }; /** * EventManager can be used to obtain basic quorum validation of events emitted by multiple providers. @@ -82,7 +72,7 @@ export class EventManager { * @param event Event to search for. * @returns The matching event, or undefined. */ - findEvent(event: Event): QuorumEvent | undefined { + findEvent(event: Log): QuorumEvent | undefined { return this.events[event.blockNumber]?.find( (storedEvent) => storedEvent.logIndex === event.logIndex && @@ -93,11 +83,11 @@ export class EventManager { } /** - * For a given Ethers Event, identify its quorum based on the number of unique providers that have supplied it. - * @param event An Ethers Event with appended provider information. + * For a given Log, identify its quorum based on the number of unique providers that have supplied it. + * @param event A Log instance with appended provider information. * @returns The number of unique providers that reported this event. */ - getEventQuorum(event: Event): number { + getEventQuorum(event: Log): number { const storedEvent = this.findEvent(event); return isDefined(storedEvent) ? dedupArray(storedEvent.providers).length : 0; } @@ -111,7 +101,7 @@ export class EventManager { * @param provider A string uniquely identifying the provider that supplied the event. * @returns void */ - add(event: Event, provider: string): void { + add(event: Log, provider: string): void { assert(!event.removed); // If `eventHash` is not recorded in `eventHashes` then it's presumed to be a new event. If it is @@ -138,7 +128,7 @@ export class EventManager { * @param provider A string uniquely identifying the provider that supplied the event. * @returns void */ - remove(event: Event, provider: string): void { + remove(event: Log, provider: string): void { assert(event.removed); const events = this.events[event.blockNumber] ?? []; @@ -168,7 +158,7 @@ export class EventManager { * @param blockNumber Number of the latest block. * @returns void */ - tick(blockNumber: number): Event[] { + tick(blockNumber: number): Log[] { this.blockNumber = blockNumber > this.blockNumber ? blockNumber : this.blockNumber; const blockNumbers = Object.keys(this.events) .map(Number) @@ -196,7 +186,7 @@ export class EventManager { * @param event An Ethers event to be hashed. * @returns A SHA256 string derived from the event. */ - hashEvent(event: Event): string { + hashEvent(event: Log): string { const { event: eventName, blockNumber, blockHash, transactionHash, transactionIndex, logIndex, args } = event; return ethersUtils.id( `${eventName}-${blockNumber}-${blockHash}-${transactionHash}-${transactionIndex}-${logIndex}-${args.join("-")}` diff --git a/src/utils/index.ts b/src/utils/index.ts index a2fabbf0f..05af53a05 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -16,7 +16,6 @@ export { BaseContract, Contract, ContractFactory, - Event, EventFilter, Signer, Transaction, diff --git a/test/EventManager.ts b/test/EventManager.ts index e1e988452..d1fd78a82 100644 --- a/test/EventManager.ts +++ b/test/EventManager.ts @@ -1,14 +1,11 @@ -import { Event, providers, utils as ethersUtils } from "ethers"; +import { utils as ethersUtils } from "ethers"; import winston from "winston"; import { Result } from "@ethersproject/abi"; import { CHAIN_IDs } from "@across-protocol/constants"; +import { Log } from "../src/interfaces"; import { EventManager } from "../src/utils"; import { createSpyLogger, expect, randomAddress } from "./utils"; -type Block = providers.Block; -type TransactionReceipt = providers.TransactionReceipt; -type TransactionResponse = providers.TransactionResponse; - describe("EventManager: Event Handling ", async function () { const chainId = CHAIN_IDs.MAINNET; const providers = ["infura", "alchemy", "llamanodes"]; @@ -17,17 +14,8 @@ describe("EventManager: Event Handling ", async function () { const makeHash = () => ethersUtils.id(randomNumber().toString()); const makeTopic = () => ethersUtils.id(randomNumber().toString()).slice(0, 40); - // Stub getters to be used in the events. These are not used in practice. - const decodeError = new Error("Event decoding error"); - const getBlock = (): Promise => Promise.resolve({} as Block); - const getTransaction = (): Promise => Promise.resolve({} as TransactionResponse); - const getTransactionReceipt = (): Promise => Promise.resolve({} as TransactionReceipt); - const removeListener = (): void => { - return; - }; - const blockNumber = 100; - const eventTemplate: Event = { + const eventTemplate: Log = { blockNumber, transactionIndex: randomNumber(100), logIndex: randomNumber(100), @@ -39,12 +27,6 @@ describe("EventManager: Event Handling ", async function () { args: [] as Result, blockHash: makeHash(), event: "randomEvent", - eventSignature: "", - decodeError, - getBlock, - getTransaction, - getTransactionReceipt, - removeListener, }; let logger: winston.Logger; diff --git a/test/Relayer.IndexedSpokePoolClient.ts b/test/Relayer.IndexedSpokePoolClient.ts index 35ae3d936..dcc5c037b 100644 --- a/test/Relayer.IndexedSpokePoolClient.ts +++ b/test/Relayer.IndexedSpokePoolClient.ts @@ -1,17 +1,14 @@ -import { Contract, Event, providers, utils as ethersUtils } from "ethers"; +import { Contract, utils as ethersUtils } from "ethers"; import winston from "winston"; import { Result } from "@ethersproject/abi"; import { CHAIN_IDs } from "@across-protocol/constants"; import { constants, utils as sdkUtils } from "@across-protocol/sdk"; import { IndexedSpokePoolClient } from "../src/clients"; -import { EventSearchConfig, mangleEventArgs, sortEventsAscending, sortEventsAscendingInPlace } from "../src/utils"; +import { Log } from "../src/interfaces"; +import { EventSearchConfig, sortEventsAscending, sortEventsAscendingInPlace } from "../src/utils"; import { SpokePoolClientMessage } from "../src/clients/SpokePoolClient"; import { assertPromiseError, createSpyLogger, deploySpokePoolWithToken, expect, randomAddress } from "./utils"; -type Block = providers.Block; -type TransactionReceipt = providers.TransactionReceipt; -type TransactionResponse = providers.TransactionResponse; - class MockIndexedSpokePoolClient extends IndexedSpokePoolClient { // Override `protected` attribute. override indexerUpdate(rawMessage: unknown): void { @@ -30,18 +27,9 @@ describe("IndexedSpokePoolClient: Update", async function () { const makeHash = () => ethersUtils.id(randomNumber().toString()); const makeTopic = () => ethersUtils.id(randomNumber().toString()).slice(0, 40); - // Stub getters to be used in the events. These are not used in practice. - const decodeError = new Error("Event decoding error"); - const getBlock = (): Promise => Promise.resolve({} as Block); - const getTransaction = (): Promise => Promise.resolve({} as TransactionResponse); - const getTransactionReceipt = (): Promise => Promise.resolve({} as TransactionReceipt); - const removeListener = (): void => { - return; - }; - let blockNumber = 100; - const generateEvent = (event: string, blockNumber: number): Event => { + const generateEvent = (event: string, blockNumber: number): Log => { return { blockNumber, transactionIndex: randomNumber(100), @@ -54,17 +42,11 @@ describe("IndexedSpokePoolClient: Update", async function () { args: [] as Result, blockHash: makeHash(), event, - eventSignature: "", - decodeError, - getBlock, - getTransaction, - getTransactionReceipt, - removeListener, }; }; let depositId: number; - const getDepositEvent = (blockNumber: number): Event => { + const getDepositEvent = (blockNumber: number): Log => { const event = generateEvent("V3FundsDeposited", blockNumber); const args = { depositId: depositId++, @@ -81,7 +63,7 @@ describe("IndexedSpokePoolClient: Update", async function () { return { ...event, args }; }; - const getDepositRouteEvent = (blockNumber: number): Event => { + const getDepositRouteEvent = (blockNumber: number): Log => { const event = generateEvent("EnabledDepositRoute", blockNumber); const args = { originToken: randomAddress(), @@ -102,23 +84,22 @@ describe("IndexedSpokePoolClient: Update", async function () { * process.send() to submit a message to the SpokePoolClient. In this test, the SpokePoolClient * instance is immediately accessible and the message handler callback is called directly. */ - const postEvents = (blockNumber: number, currentTime: number, events: Event[]): void => { - events = sortEventsAscending(events.map(mangleEventArgs)); + const postEvents = (blockNumber: number, currentTime: number, events: Log[]): void => { const message: SpokePoolClientMessage = { blockNumber, currentTime, oldestTime, nEvents: events.length, - data: JSON.stringify(events, sdkUtils.jsonReplacerWithBigNumbers), + data: JSON.stringify(sortEventsAscending(events), sdkUtils.jsonReplacerWithBigNumbers), }; spokePoolClient.indexerUpdate(JSON.stringify(message)); }; - const removeEvent = (event: Event): void => { + const removeEvent = (event: Log): void => { event.removed = true; const message: SpokePoolClientMessage = { - event: JSON.stringify(mangleEventArgs(event), sdkUtils.jsonReplacerWithBigNumbers), + event: JSON.stringify(event, sdkUtils.jsonReplacerWithBigNumbers), }; spokePoolClient.indexerUpdate(JSON.stringify(message)); }; @@ -143,7 +124,7 @@ describe("IndexedSpokePoolClient: Update", async function () { }); it("Correctly receives and unpacks SpokePoolEventsAdded messages from indexer", async function () { - const events: Event[] = []; + const events: Log[] = []; for (let i = 0; i < 25; ++i) { events.push(getDepositEvent(blockNumber)); } @@ -175,7 +156,7 @@ describe("IndexedSpokePoolClient: Update", async function () { }); it("Correctly removes pending events that are dropped before update", async function () { - const events: Event[] = []; + const events: Log[] = []; for (let i = 0; i < 25; ++i) { events.push(getDepositEvent(blockNumber++)); } @@ -195,7 +176,7 @@ describe("IndexedSpokePoolClient: Update", async function () { }); it("Correctly removes pending events that are dropped after update", async function () { - const events: Event[] = []; + const events: Log[] = []; for (let i = 0; i < 25; ++i) { events.push(getDepositEvent(blockNumber++)); } @@ -218,7 +199,7 @@ describe("IndexedSpokePoolClient: Update", async function () { }); it("Throws on post-ingested dropped EnabledDepositRoute events", async function () { - const events: Event[] = []; + const events: Log[] = []; for (let i = 0; i < 25; ++i) { events.push(getDepositRouteEvent(blockNumber++)); } diff --git a/yarn.lock b/yarn.lock index e99fe4d7f..e5e4cbd42 100644 --- a/yarn.lock +++ b/yarn.lock @@ -50,10 +50,10 @@ axios "^1.6.2" zksync-web3 "^0.14.3" -"@across-protocol/sdk@^3.1.36": - version "3.1.36" - resolved "https://registry.yarnpkg.com/@across-protocol/sdk/-/sdk-3.1.36.tgz#8187f4771ca68f14c165033f0c6cc6e35e211919" - integrity sha512-Im9ELYj+m0WMow1zUWYerU5v+A+Tpty2jC/7pUSI2pMnHn/mdAWmMoL/EKMqpaxMFucGw6d4fUb1EvLeazBqlg== +"@across-protocol/sdk@^3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@across-protocol/sdk/-/sdk-3.2.0.tgz#bd011965c7c3ffc6c76c9e00bb9baa5f80d33c7d" + integrity sha512-ks+A9fvXMdcstedvMzV+uTAFObSuXs/4xSm11CEIMu2RfgGTQe/pCN0KSqPhW+H4v53sblxGJpsL8Y/msfH/HQ== dependencies: "@across-protocol/across-token" "^1.0.0" "@across-protocol/constants" "^3.1.14" From 3d66b33ff6c1206b29012627e11d827993d4805f Mon Sep 17 00:00:00 2001 From: Paul <108695806+pxrl@users.noreply.github.com> Date: Tue, 24 Sep 2024 19:09:32 +0200 Subject: [PATCH 7/8] fix(relayer): Log arguments are no longer an array (#1836) This should have been added as part of the previous commit. --- src/utils/EventUtils.ts | 6 +++--- test/EventManager.ts | 9 +++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/utils/EventUtils.ts b/src/utils/EventUtils.ts index 94ae6a059..1c12e8837 100644 --- a/src/utils/EventUtils.ts +++ b/src/utils/EventUtils.ts @@ -188,8 +188,8 @@ export class EventManager { */ hashEvent(event: Log): string { const { event: eventName, blockNumber, blockHash, transactionHash, transactionIndex, logIndex, args } = event; - return ethersUtils.id( - `${eventName}-${blockNumber}-${blockHash}-${transactionHash}-${transactionIndex}-${logIndex}-${args.join("-")}` - ); + const _args = Object.values(args).join("-"); + const key = `${eventName}-${blockNumber}-${blockHash}-${transactionHash}-${transactionIndex}-${logIndex}-${_args}`; + return ethersUtils.id(key); } } diff --git a/test/EventManager.ts b/test/EventManager.ts index d1fd78a82..8144d14c1 100644 --- a/test/EventManager.ts +++ b/test/EventManager.ts @@ -1,6 +1,5 @@ import { utils as ethersUtils } from "ethers"; import winston from "winston"; -import { Result } from "@ethersproject/abi"; import { CHAIN_IDs } from "@across-protocol/constants"; import { Log } from "../src/interfaces"; import { EventManager } from "../src/utils"; @@ -24,7 +23,7 @@ describe("EventManager: Event Handling ", async function () { address: randomAddress(), data: ethersUtils.id(`EventManager-random-txndata-${randomNumber()}`), topics: [makeTopic()], - args: [] as Result, + args: {}, blockHash: makeHash(), event: "randomEvent", }; @@ -116,4 +115,10 @@ describe("EventManager: Event Handling ", async function () { events = eventMgr.tick(eventTemplate.blockNumber + 1); expect(events.length).to.equal(0); }); + + it("Hashes events correctly", async function () { + const log = eventTemplate; + const hash = eventMgr.hashEvent(log); + expect(hash).to.exist; + }); }); From 247451c6dd07b51c8a1e9f60dfa8bcf81f1cf8db Mon Sep 17 00:00:00 2001 From: bmzig <57361391+bmzig@users.noreply.github.com> Date: Wed, 25 Sep 2024 08:39:23 -0500 Subject: [PATCH 8/8] improve: swap all Ovm adapters (excl. Optimism/Base) w/ generic adapter (#1824) * improve: swap all Ovm adapters (excl. Optimism/Base) w/ generic adapter Signed-off-by: bennett --------- Signed-off-by: bennett --- src/adapter/bridges/BlastBridge.ts | 2 +- src/adapter/bridges/OpStackWethBridge.ts | 2 +- src/clients/bridges/AdapterManager.ts | 68 ++++++++++++++++++------ test/generic-adapters/OpStack.ts | 4 +- 4 files changed, 55 insertions(+), 21 deletions(-) diff --git a/src/adapter/bridges/BlastBridge.ts b/src/adapter/bridges/BlastBridge.ts index 22f52c566..b303746fe 100644 --- a/src/adapter/bridges/BlastBridge.ts +++ b/src/adapter/bridges/BlastBridge.ts @@ -62,7 +62,7 @@ export class BlastBridge extends BaseBridgeAdapter { const l2Bridge = this.getL2Bridge(); const events = await paginatedEventQuery( l2Bridge, - l2Bridge.filters.ERC20BridgeFinalized(l1Token, undefined, fromAddress), + l2Bridge.filters.ERC20BridgeFinalized(undefined, l1Token, fromAddress), eventConfig ); return { diff --git a/src/adapter/bridges/OpStackWethBridge.ts b/src/adapter/bridges/OpStackWethBridge.ts index 778da1e69..80a4d7e42 100644 --- a/src/adapter/bridges/OpStackWethBridge.ts +++ b/src/adapter/bridges/OpStackWethBridge.ts @@ -138,7 +138,7 @@ export class OpStackWethBridge extends BaseBridgeAdapter { ) // If EOA sent the ETH via the AtomicDepositor, then remove any events where the // toAddress is not the EOA so we don't get confused with other users using the AtomicDepositor - .filter((event) => event.args._to === fromAddress); + .filter((event) => event.args._to === toAddress); // We only care about WETH finalization events initiated by the relayer running this rebalancer logic, so only // filter on Deposit events sent from the provided signer. We can't simply filter on `fromAddress` because diff --git a/src/clients/bridges/AdapterManager.ts b/src/clients/bridges/AdapterManager.ts index 9d8418c09..ec8a9d732 100644 --- a/src/clients/bridges/AdapterManager.ts +++ b/src/clients/bridges/AdapterManager.ts @@ -1,10 +1,16 @@ import { utils } from "@across-protocol/sdk"; -import { spokesThatHoldEthAndWeth, SUPPORTED_TOKENS } from "../../common/Constants"; +import { + spokesThatHoldEthAndWeth, + SUPPORTED_TOKENS, + CUSTOM_BRIDGE, + CANONICAL_BRIDGE, + DEFAULT_GAS_MULTIPLIER, +} from "../../common/Constants"; import { InventoryConfig, OutstandingTransfers } from "../../interfaces"; import { BigNumber, isDefined, winston, Signer, getL2TokenAddresses, TransactionResponse, assert } from "../../utils"; import { SpokePoolClient, HubPoolClient } from "../"; import { ArbitrumAdapter, PolygonAdapter, ZKSyncAdapter, LineaAdapter, OpStackAdapter, ScrollAdapter } from "./"; -import { CHAIN_IDs } from "@across-protocol/constants"; +import { CHAIN_IDs, TOKEN_SYMBOLS_MAP } from "@across-protocol/constants"; import { BaseChainAdapter } from "../../adapter"; @@ -40,6 +46,19 @@ export class AdapterManager { }; const { OPTIMISM, ARBITRUM, POLYGON, ZK_SYNC, BASE, MODE, LINEA, LISK, BLAST, REDSTONE, SCROLL, ZORA } = CHAIN_IDs; + const hubChainId = hubPoolClient.chainId; + const l1Signer = spokePoolClients[hubChainId].spokePool.signer; + const constructBridges = (chainId: number) => { + return Object.fromEntries( + SUPPORTED_TOKENS[chainId].map((symbol) => { + const l2Signer = spokePoolClients[chainId].spokePool.signer; + const l1Token = TOKEN_SYMBOLS_MAP[symbol].addresses[hubChainId]; + const bridgeConstructor = CUSTOM_BRIDGE[chainId]?.[l1Token] ?? CANONICAL_BRIDGE[chainId]; + const bridge = new bridgeConstructor(chainId, hubChainId, l1Signer, l2Signer, l1Token); + return [l1Token, bridge]; + }) + ); + }; if (this.spokePoolClients[OPTIMISM] !== undefined) { this.adapters[OPTIMISM] = new OpStackAdapter( OPTIMISM, @@ -71,51 +90,66 @@ export class AdapterManager { this.adapters[LINEA] = new LineaAdapter(logger, spokePoolClients, filterMonitoredAddresses(LINEA)); } if (this.spokePoolClients[MODE] !== undefined) { - this.adapters[MODE] = new OpStackAdapter( + this.adapters[MODE] = new BaseChainAdapter( + spokePoolClients, MODE, + hubChainId, + filterMonitoredAddresses(MODE), logger, SUPPORTED_TOKENS[MODE], - spokePoolClients, - filterMonitoredAddresses(MODE) + constructBridges(MODE), + DEFAULT_GAS_MULTIPLIER[MODE] ?? 1 ); } if (this.spokePoolClients[REDSTONE] !== undefined) { - this.adapters[REDSTONE] = new OpStackAdapter( + this.adapters[REDSTONE] = new BaseChainAdapter( + spokePoolClients, REDSTONE, + hubChainId, + filterMonitoredAddresses(REDSTONE), logger, SUPPORTED_TOKENS[REDSTONE], - spokePoolClients, - filterMonitoredAddresses(REDSTONE) + constructBridges(REDSTONE), + DEFAULT_GAS_MULTIPLIER[REDSTONE] ?? 1 ); } if (this.spokePoolClients[LISK] !== undefined) { - this.adapters[LISK] = new OpStackAdapter( + this.adapters[LISK] = new BaseChainAdapter( + spokePoolClients, LISK, + hubChainId, + filterMonitoredAddresses(LISK), logger, SUPPORTED_TOKENS[LISK], - spokePoolClients, - filterMonitoredAddresses(LISK) + constructBridges(LISK), + DEFAULT_GAS_MULTIPLIER[LISK] ?? 1 ); } if (this.spokePoolClients[BLAST] !== undefined) { - this.adapters[BLAST] = new OpStackAdapter( + this.adapters[BLAST] = new BaseChainAdapter( + spokePoolClients, BLAST, + hubChainId, + filterMonitoredAddresses(BLAST), logger, SUPPORTED_TOKENS[BLAST], - spokePoolClients, - filterMonitoredAddresses(BLAST) + constructBridges(BLAST), + DEFAULT_GAS_MULTIPLIER[BLAST] ?? 1 ); } if (this.spokePoolClients[SCROLL] !== undefined) { this.adapters[SCROLL] = new ScrollAdapter(logger, spokePoolClients, filterMonitoredAddresses(SCROLL)); } if (this.spokePoolClients[ZORA] !== undefined) { - this.adapters[ZORA] = new OpStackAdapter( + this.adapters[ZORA] = new BaseChainAdapter( + spokePoolClients, ZORA, + hubChainId, + filterMonitoredAddresses(ZORA), logger, SUPPORTED_TOKENS[ZORA], - spokePoolClients, - filterMonitoredAddresses(ZORA) + constructBridges(ZORA), + DEFAULT_GAS_MULTIPLIER[ZORA] ?? 1 ); } diff --git a/test/generic-adapters/OpStack.ts b/test/generic-adapters/OpStack.ts index 5762a3f94..18537ec74 100644 --- a/test/generic-adapters/OpStack.ts +++ b/test/generic-adapters/OpStack.ts @@ -165,14 +165,14 @@ describe("Cross Chain Adapter: OP Stack", async function () { // For EOA's, weth transfer from address should be atomic depositor address await wethBridgeContract.emitDepositFinalized(atomicDepositorAddress, monitoredEoa, 1); const emptyResult = ( - await wethBridge.queryL2BridgeFinalizationEvents(l1WethAddress, monitoredEoa, undefined, searchConfig) + await wethBridge.queryL2BridgeFinalizationEvents(l1WethAddress, monitoredEoa, monitoredEoa, searchConfig) )[l2WethAddress]; expect(emptyResult.length).to.equal(0); // Mine Deposit event now. await wethContract.connect(monitoredEoaAccount).deposit({ value: 0 }); const result = ( - await wethBridge.queryL2BridgeFinalizationEvents(l1WethAddress, monitoredEoa, undefined, searchConfig) + await wethBridge.queryL2BridgeFinalizationEvents(l1WethAddress, monitoredEoa, monitoredEoa, searchConfig) )[l2WethAddress]; expect(result.length).to.equal(1); expect(result[0].from).to.equal(atomicDepositorAddress);