diff --git a/src/monitor/Monitor.ts b/src/monitor/Monitor.ts index 609011ac4..ff188a6e3 100644 --- a/src/monitor/Monitor.ts +++ b/src/monitor/Monitor.ts @@ -1,4 +1,3 @@ -import { utils as sdkUtils } from "@across-protocol/sdk-v2"; import { BalanceAllocator } from "../clients"; import { spokePoolClientsToProviders } from "../common"; import { @@ -186,15 +185,15 @@ export class Monitor { // Group unfilled amounts by chain id and token id. const unfilledAmountByChainAndToken: { [chainId: number]: { [tokenAddress: string]: BigNumber } } = {}; - for (const deposit of unfilledDeposits) { - const chainId = deposit.deposit.destinationChainId; - const outputToken = sdkUtils.getDepositOutputToken(deposit.deposit); + Object.entries(unfilledDeposits).forEach(([_destinationChainId, deposits]) => { + const chainId = Number(_destinationChainId); unfilledAmountByChainAndToken[chainId] ??= {}; - unfilledAmountByChainAndToken[chainId][outputToken] ??= bnZero; - unfilledAmountByChainAndToken[chainId][outputToken] = unfilledAmountByChainAndToken[chainId][outputToken].add( - deposit.unfilledAmount - ); - } + + deposits.forEach(({ deposit: { outputToken, outputAmount } }) => { + const unfilledAmount = unfilledAmountByChainAndToken[chainId][outputToken] ?? bnZero; + unfilledAmountByChainAndToken[chainId][outputToken] = unfilledAmount.add(outputAmount); + }); + }); let mrkdwn = ""; for (const [chainIdStr, amountByToken] of Object.entries(unfilledAmountByChainAndToken)) { diff --git a/src/relayer/Relayer.ts b/src/relayer/Relayer.ts index 0c8792b27..8653958e3 100644 --- a/src/relayer/Relayer.ts +++ b/src/relayer/Relayer.ts @@ -48,7 +48,10 @@ export class Relayer { const { configStoreClient, hubPoolClient, spokePoolClients, acrossApiClient } = this.clients; const { relayerTokens, ignoredAddresses, acceptInvalidFills } = this.config; - const unfilledDeposits = await getUnfilledDeposits(spokePoolClients, hubPoolClient, this.config.maxRelayerLookBack); + // Flatten unfilledDeposits for now. @todo: Process deposits in parallel by destination chain. + const unfilledDeposits = Object.values( + await getUnfilledDeposits(spokePoolClients, hubPoolClient, this.config.maxRelayerLookBack) + ).flat(); const maxVersion = configStoreClient.configStoreVersion; return sdkUtils.filterAsync(unfilledDeposits, async ({ deposit, version, invalidFills }) => { diff --git a/src/utils/FillUtils.ts b/src/utils/FillUtils.ts index 61adf0614..a5239cff5 100644 --- a/src/utils/FillUtils.ts +++ b/src/utils/FillUtils.ts @@ -259,8 +259,6 @@ export function getFillsInRange( export type RelayerUnfilledDeposit = { deposit: V3DepositWithBlock; version: number; - unfilledAmount: BigNumber; - fillCount: number; invalidFills: Fill[]; }; @@ -273,8 +271,8 @@ export async function getUnfilledDeposits( spokePoolClients: SpokePoolClientsByChain, hubPoolClient: HubPoolClient, depositLookBack?: number -): Promise { - const unfilledDeposits: RelayerUnfilledDeposit[] = []; +): Promise<{ [chainId: number]: RelayerUnfilledDeposit[] }> { + const unfilledDeposits: { [chainId: number]: RelayerUnfilledDeposit[] } = {}; const chainIds = Object.values(spokePoolClients).map(({ chainId }) => chainId); let earliestBlockNumbers = Object.values(spokePoolClients).map(({ deploymentBlock }) => deploymentBlock); @@ -291,31 +289,30 @@ export async function getUnfilledDeposits( } // Iterate over each chainId and check for unfilled deposits. - chainIds.forEach((originChainId, idx) => { - const originClient = spokePoolClients[originChainId]; + chainIds.forEach((destinationChainId, idx) => { + const destinationClient = spokePoolClients[destinationChainId]; const earliestBlockNumber = earliestBlockNumbers[idx]; - for (const destinationChain of chainIds.filter((chainId) => chainId !== originChainId)) { - // Find all unfilled deposits for the current loops originChain -> destinationChain. Note that this also - // validates that the deposit is filled "correctly" for the given deposit information. This includes validation - // of the all deposit -> relay props, the realizedLpFeePct and the origin->destination token mapping. - const destinationClient = spokePoolClients[destinationChain]; - const deposits = originClient - .getDepositsForDestinationChain(destinationChain) - .filter(sdkUtils.isV3Deposit); + unfilledDeposits[destinationChainId] = chainIds + .filter((chainId) => chainId !== destinationChainId) + .map((originChainId) => { + const originClient = spokePoolClients[originChainId]; + // Find all unfilled deposits for the current loops originChain -> destinationChain. Note that this also + // validates that the deposit is filled "correctly" for the given deposit information. This includes validation + // of the all deposit -> relay props, the realizedLpFeePct and the origin->destination token mapping. + const deposits = originClient + .getDepositsForDestinationChain(destinationChainId) + .filter(sdkUtils.isV3Deposit); - const unfilledDepositsForDestinationChain = deposits - .filter((deposit) => deposit.blockNumber >= earliestBlockNumber) - .map((deposit) => { - const version = hubPoolClient.configStoreClient.getConfigStoreVersionForTimestamp(deposit.quoteTimestamp); - return { ...destinationClient.getValidUnfilledAmountForDeposit(deposit), deposit, version }; - }); - - // Remove any deposits that have no unfilled amount and append the remaining deposits to unfilledDeposits array. - unfilledDeposits.push( - ...unfilledDepositsForDestinationChain.filter((deposit) => deposit.unfilledAmount.gt(bnZero)) - ); - } + return deposits + .filter((deposit) => deposit.blockNumber >= earliestBlockNumber) + .map((deposit) => { + const version = hubPoolClient.configStoreClient.getConfigStoreVersionForTimestamp(deposit.quoteTimestamp); + return { ...destinationClient.getValidUnfilledAmountForDeposit(deposit), deposit, version }; + }) + .filter(({ unfilledAmount }) => unfilledAmount.gt(bnZero)); + }) + .flat(); }); return unfilledDeposits; diff --git a/test/Relayer.UnfilledDeposits.ts b/test/Relayer.UnfilledDeposits.ts index 1590bdc63..711cea7e8 100644 --- a/test/Relayer.UnfilledDeposits.ts +++ b/test/Relayer.UnfilledDeposits.ts @@ -153,9 +153,8 @@ describe("Relayer: Unfilled Deposits", async function () { await Promise.all([spokePool_1, spokePool_2].map((spokePool) => spokePool.setCurrentTime(currentTime))); await updateAllClients(); - _getUnfilledDeposits = async (): Promise => { - return await getUnfilledDeposits(relayerInstance.clients.spokePoolClients, hubPoolClient); - }; + _getUnfilledDeposits = async (): Promise => + Object.values(await getUnfilledDeposits(relayerInstance.clients.spokePoolClients, hubPoolClient)).flat(); unfilledDeposits = []; const tokenBalance = await erc20_1.balanceOf(depositor.address); @@ -181,13 +180,15 @@ describe("Relayer: Unfilled Deposits", async function () { expect(unfilledDeposits) .excludingEvery(["realizedLpFeePct", "quoteBlockNumber"]) .to.deep.equal( - deposits.map((deposit) => ({ - unfilledAmount: deposit.outputAmount, - deposit, - fillCount: 0, - invalidFills: [], - version: configStoreClient.configStoreVersion, - })) + deposits + .sort((a, b) => (a.destinationChainId > b.destinationChainId ? 1 : -1)) + .map((deposit) => ({ + unfilledAmount: deposit.outputAmount, + deposit, + fillCount: 0, + invalidFills: [], + version: configStoreClient.configStoreVersion, + })) ); });