diff --git a/src/monitor/Monitor.ts b/src/monitor/Monitor.ts index 17e121887..4f2cd5514 100644 --- a/src/monitor/Monitor.ts +++ b/src/monitor/Monitor.ts @@ -27,6 +27,7 @@ import { toBN, toBNWei, winston, + TOKEN_SYMBOLS_MAP, } from "../utils"; import { MonitorClients, updateMonitorClients } from "./MonitorClientHelper"; @@ -265,18 +266,13 @@ export class Monitor { // Update current balances of all tokens on each supported chain for each relayer. async updateCurrentRelayerBalances(relayerBalanceReport: RelayerBalanceReport): Promise { const { hubPoolClient } = this.clients; - const l1Tokens = hubPoolClient.getL1Tokens(); + const _l1Tokens = hubPoolClient.getL1Tokens(); for (const relayer of this.monitorConfig.monitoredRelayers) { for (const chainId of this.monitorChains) { - const l2ToL1Tokens = Object.fromEntries( - l1Tokens - .filter(({ address: l1Token }) => hubPoolClient.l2TokenEnabledForL1Token(l1Token, chainId)) - .map((l1Token) => { - const l2Token = hubPoolClient.getL2TokenForL1TokenAtBlock(l1Token.address, chainId); - return [l2Token, l1Token]; - }) + const l1Tokens = _l1Tokens.filter(({ address: l1Token }) => + hubPoolClient.l2TokenEnabledForL1Token(l1Token, chainId) ); - + const l2ToL1Tokens = this.getL2ToL1TokenMap(l1Tokens, chainId); const l2TokenAddresses = Object.keys(l2ToL1Tokens); const tokenBalances = await this._getBalances( l2TokenAddresses.map((address) => ({ @@ -300,6 +296,32 @@ export class Monitor { } } + // Returns a dictionary of L2 token addresses on this chain to their mapped L1 token info. For example, this + // will return a dictionary for Optimism including WETH, WBTC, USDC, USDC.e, USDT entries where the key is + // the token's Optimism address and the value is the equivalent L1 token info. + protected getL2ToL1TokenMap(l1Tokens: L1Token[], chainId: number): { [l2TokenAddress: string]: L1Token } { + return Object.fromEntries( + l1Tokens + .map((l1Token) => { + // @dev l2TokenSymbols is a list of all keys in TOKEN_SYMBOLS_MAP where the hub chain address is equal to the + // l1 token address. + const l2TokenSymbols = Object.entries(TOKEN_SYMBOLS_MAP) + .filter( + ([, { addresses }]) => + addresses[this.clients.hubPoolClient.chainId]?.toLowerCase() === l1Token.address.toLowerCase() + ) + .map(([symbol]) => symbol); + + // Create an entry for all L2 tokens that share a symbol with the L1 token. This includes tokens + // like USDC which has multiple L2 tokens mapped to the same L1 token for a given chain ID. + return l2TokenSymbols + .filter((symbol) => TOKEN_SYMBOLS_MAP[symbol].addresses[chainId] !== undefined) + .map((symbol) => [TOKEN_SYMBOLS_MAP[symbol].addresses[chainId], l1Token]); + }) + .flat() + ); + } + async checkBalances(): Promise { const { monitoredBalances } = this.monitorConfig; const balances = await this._getBalances(monitoredBalances); diff --git a/test/Monitor.ts b/test/Monitor.ts index 6dd54a5da..0cd353100 100644 --- a/test/Monitor.ts +++ b/test/Monitor.ts @@ -10,7 +10,7 @@ import { import { CrossChainTransferClient } from "../src/clients/bridges"; import { spokePoolClientsToProviders } from "../src/common"; import { Dataworker } from "../src/dataworker/Dataworker"; -import { BalanceType, V3DepositWithBlock } from "../src/interfaces"; +import { BalanceType, L1Token, V3DepositWithBlock } from "../src/interfaces"; import { ALL_CHAINS_NAME, Monitor, REBALANCE_FINALIZE_GRACE_PERIOD } from "../src/monitor/Monitor"; import { MonitorConfig } from "../src/monitor/MonitorConfig"; import { MAX_UINT_VAL, getNetworkName, toBN } from "../src/utils"; @@ -31,6 +31,20 @@ import { toBNWei, } from "./utils"; +type TokenMap = { [l2TokenAddress: string]: L1Token }; + +class TestMonitor extends Monitor { + private overriddenTokenMap: { [chainId: number]: TokenMap } = {}; + + setL2ToL1TokenMap(chainId: number, map: TokenMap): void { + this.overriddenTokenMap[chainId] = map; + } + // Override internal function that calls into externally defined and hard-coded TOKEN_SYMBOLS_MAP. + protected getL2ToL1TokenMap(l1Tokens: L1Token[], chainId): TokenMap { + return this.overriddenTokenMap[chainId] ?? super.getL2ToL1TokenMap(l1Tokens, chainId); + } +} + describe("Monitor", async function () { const TEST_NETWORK_NAMES = ["Hardhat1", "Hardhat2", "unknown", ALL_CHAINS_NAME]; let l1Token: Contract, l2Token: Contract, erc20_2: Contract; @@ -154,7 +168,7 @@ describe("Monitor", async function () { adapterManager = new MockAdapterManager(null, null, null, null); adapterManager.setSupportedChains(chainIds); crossChainTransferClient = new CrossChainTransferClient(spyLogger, chainIds, adapterManager); - monitorInstance = new Monitor(spyLogger, monitorConfig, { + monitorInstance = new TestMonitor(spyLogger, monitorConfig, { bundleDataClient, configStoreClient, multiCallerClient, @@ -163,7 +177,27 @@ describe("Monitor", async function () { tokenTransferClient, crossChainTransferClient, }); - + (monitorInstance as TestMonitor).setL2ToL1TokenMap(originChainId, { + [l2Token.address]: { + symbol: "L1Token1", + address: l1Token.address, + decimals: 18, + }, + }); + (monitorInstance as TestMonitor).setL2ToL1TokenMap(destinationChainId, { + [erc20_2.address]: { + symbol: "L1Token1", + address: l1Token.address, + decimals: 18, + }, + }); + (monitorInstance as TestMonitor).setL2ToL1TokenMap(hubPoolClient.chainId, { + [l1Token.address]: { + symbol: "L1Token1", + address: l1Token.address, + decimals: 18, + }, + }); await updateAllClients(); });