From 0d64b74e364bdbfc650039502c3a17b0e0da48d5 Mon Sep 17 00:00:00 2001 From: nicholaspai <9457025+nicholaspai@users.noreply.github.com> Date: Mon, 26 Aug 2024 10:18:56 -0400 Subject: [PATCH] improve(BundleDataClient): Skip `queryHistoricalDepositForFill` function for non-infinite expiry deposits (#1769) --- src/clients/BundleDataClient.ts | 18 ++++++++-- src/common/Constants.ts | 5 ++- test/Dataworker.loadData.fill.ts | 23 ++++++++---- test/Dataworker.loadData.slowFill.ts | 36 +++++++++++++------ test/utils/utils.ts | 54 +++++++++++++++++++--------- 5 files changed, 100 insertions(+), 36 deletions(-) diff --git a/src/clients/BundleDataClient.ts b/src/clients/BundleDataClient.ts index 7dbc03e6e0..31b9eed887 100644 --- a/src/clients/BundleDataClient.ts +++ b/src/clients/BundleDataClient.ts @@ -21,7 +21,7 @@ import { isDefined, toBN, } from "../utils"; -import { Clients } from "../common"; +import { Clients, INFINITE_FILL_DEADLINE } from "../common"; import { getBlockRangeForChain, getImpliedBundleBlockRanges, @@ -869,7 +869,16 @@ export class BundleDataClient { // Since there was no deposit matching the relay hash, we need to do a historical query for an // older deposit in case the spoke pool client's lookback isn't old enough to find the matching deposit. + // We can skip this step if the fill's fill deadline is not infinite, because we can assume that the + // spoke pool clients have loaded deposits old enough to cover all fills with a non-infinite fill deadline. if (fill.blockNumber >= destinationChainBlockRange[0]) { + // Fill has a non-infinite expiry, and we can assume our spoke pool clients have old enough deposits + // to conclude that this fill is invalid if we haven't found a matching deposit in memory, so + // skip the historical query. + if (!INFINITE_FILL_DEADLINE.eq(fill.fillDeadline)) { + bundleInvalidFillsV3.push(fill); + return; + } const historicalDeposit = await queryHistoricalDepositForFill(originClient, fill); if (!historicalDeposit.found) { bundleInvalidFillsV3.push(fill); @@ -963,7 +972,12 @@ export class BundleDataClient { // Since there was no deposit matching the relay hash, we need to do a historical query for an // older deposit in case the spoke pool client's lookback isn't old enough to find the matching deposit. - if (slowFillRequest.blockNumber >= destinationChainBlockRange[0]) { + // We can skip this step if the deposit's fill deadline is not infinite, because we can assume that the + // spoke pool clients have loaded deposits old enough to cover all fills with a non-infinite fill deadline. + if ( + INFINITE_FILL_DEADLINE.eq(slowFillRequest.fillDeadline) && + slowFillRequest.blockNumber >= destinationChainBlockRange[0] + ) { const historicalDeposit = await queryHistoricalDepositForFill(originClient, slowFillRequest); if (!historicalDeposit.found) { // TODO: Invalid slow fill request. Maybe worth logging. diff --git a/src/common/Constants.ts b/src/common/Constants.ts index 982ab03e2a..b055c08f3b 100644 --- a/src/common/Constants.ts +++ b/src/common/Constants.ts @@ -1,4 +1,4 @@ -import { CHAIN_IDs, TOKEN_SYMBOLS_MAP, ethers, Signer, Provider, ZERO_ADDRESS } from "../utils"; +import { CHAIN_IDs, TOKEN_SYMBOLS_MAP, ethers, Signer, Provider, ZERO_ADDRESS, bnUint32Max } from "../utils"; import { BaseBridgeAdapter, OpStackDefaultERC20Bridge, @@ -31,6 +31,9 @@ export const CONFIG_STORE_VERSION = 4; export const RELAYER_MIN_FEE_PCT = 0.0001; +// max(uint256) - 1 +export const INFINITE_FILL_DEADLINE = bnUint32Max; + // Target ~4 hours export const MAX_RELAYER_DEPOSIT_LOOK_BACK = 4 * 60 * 60; diff --git a/test/Dataworker.loadData.fill.ts b/test/Dataworker.loadData.fill.ts index 6f7237d42a..f1bdc671cf 100644 --- a/test/Dataworker.loadData.fill.ts +++ b/test/Dataworker.loadData.fill.ts @@ -30,6 +30,7 @@ import { MockConfigStoreClient, MockHubPoolClient, MockSpokePoolClient } from ". import { interfaces, utils as sdkUtils } from "@across-protocol/sdk"; import { cloneDeep } from "lodash"; import { CombinedRefunds } from "../src/dataworker/DataworkerUtils"; +import { INFINITE_FILL_DEADLINE } from "../src/common"; let spokePool_1: Contract, erc20_1: Contract, spokePool_2: Contract, erc20_2: Contract; let l1Token_1: Contract; @@ -404,7 +405,7 @@ describe("Dataworker: Load data used in all functions", async function () { // For this test, we need to actually send a deposit on the spoke pool // because queryHistoricalDepositForFill eth_call's the contract. - // Send a deposit. + // Send a legacy deposit. const depositObject = await depositV3( spokePool_1, destinationChainId, @@ -412,7 +413,10 @@ describe("Dataworker: Load data used in all functions", async function () { erc20_1.address, amountToDeposit, erc20_2.address, - amountToDeposit + amountToDeposit, + { + fillDeadline: INFINITE_FILL_DEADLINE.toNumber(), + } ); const depositBlock = await spokePool_1.provider.getBlockNumber(); @@ -442,7 +446,8 @@ describe("Dataworker: Load data used in all functions", async function () { // For this test, we need to actually send a deposit on the spoke pool // because queryHistoricalDepositForFill eth_call's the contract. - // Send a deposit. + // Send a legacy deposit where the fill deadline is infinite, so we can test the bundle data client uses + // queryHistoricalDepositForFill to find the deposit. const depositObject = await depositV3( spokePool_1, destinationChainId, @@ -450,7 +455,10 @@ describe("Dataworker: Load data used in all functions", async function () { erc20_1.address, amountToDeposit, erc20_2.address, - amountToDeposit + amountToDeposit, + { + fillDeadline: INFINITE_FILL_DEADLINE.toNumber(), + } ); const depositBlock = await spokePool_1.provider.getBlockNumber(); // Construct a spoke pool client with a small search range that would not include the deposit. @@ -499,7 +507,7 @@ describe("Dataworker: Load data used in all functions", async function () { // For this test, we need to actually send a deposit on the spoke pool // because queryHistoricalDepositForFill eth_call's the contract. - // Send a deposit. + // Send a legacy deposit. const depositObject = await depositV3( spokePool_1, destinationChainId, @@ -507,7 +515,10 @@ describe("Dataworker: Load data used in all functions", async function () { erc20_1.address, amountToDeposit, erc20_2.address, - amountToDeposit + amountToDeposit, + { + fillDeadline: INFINITE_FILL_DEADLINE.toNumber(), + } ); const depositBlock = await spokePool_1.provider.getBlockNumber(); diff --git a/test/Dataworker.loadData.slowFill.ts b/test/Dataworker.loadData.slowFill.ts index c7f4e00725..839151ab04 100644 --- a/test/Dataworker.loadData.slowFill.ts +++ b/test/Dataworker.loadData.slowFill.ts @@ -30,6 +30,7 @@ import { getCurrentTime, Event, toBNWei, assert, ZERO_ADDRESS } from "../src/uti import { MockConfigStoreClient, MockHubPoolClient, MockSpokePoolClient } from "./mocks"; import { interfaces, utils as sdkUtils } from "@across-protocol/sdk"; import { cloneDeep } from "lodash"; +import { INFINITE_FILL_DEADLINE } from "../src/common"; describe("BundleDataClient: Slow fill handling & validation", async function () { let spokePool_1: Contract, erc20_1: Contract, spokePool_2: Contract, erc20_2: Contract; @@ -372,7 +373,7 @@ describe("BundleDataClient: Slow fill handling & validation", async function () // For this test, we need to actually send a deposit on the spoke pool // because queryHistoricalDepositForFill eth_call's the contract. - // Send a deposit. + // Send a legacy deposit. const depositObject = await depositV3( spokePool_1, destinationChainId, @@ -380,7 +381,10 @@ describe("BundleDataClient: Slow fill handling & validation", async function () erc20_1.address, amountToDeposit, erc20_2.address, - amountToDeposit + amountToDeposit, + { + fillDeadline: INFINITE_FILL_DEADLINE.toNumber(), + } ); const depositBlock = await spokePool_1.provider.getBlockNumber(); @@ -469,7 +473,7 @@ describe("BundleDataClient: Slow fill handling & validation", async function () // For this test, we need to actually send a deposit on the spoke pool // because queryHistoricalDepositForFill eth_call's the contract. - // Send a deposit. + // Send a legacy deposit. const depositObject = await depositV3( spokePool_1, destinationChainId, @@ -477,7 +481,10 @@ describe("BundleDataClient: Slow fill handling & validation", async function () erc20_1.address, amountToDeposit, erc20_2.address, - amountToDeposit + amountToDeposit, + { + fillDeadline: INFINITE_FILL_DEADLINE.toNumber(), + } ); const depositBlock = await spokePool_1.provider.getBlockNumber(); @@ -507,7 +514,7 @@ describe("BundleDataClient: Slow fill handling & validation", async function () // For this test, we need to actually send a deposit on the spoke pool // because queryHistoricalDepositForFill eth_call's the contract. - // Send a deposit. We'll set output token to a random token to invalidate the slow fill request (e.g. + // Send a legacy deposit. We'll set output token to a random token to invalidate the slow fill request (e.g. // input and output are not "equivalent" tokens) const invalidOutputToken = erc20_1; const depositObject = await depositV3( @@ -517,7 +524,10 @@ describe("BundleDataClient: Slow fill handling & validation", async function () erc20_1.address, amountToDeposit, invalidOutputToken.address, - amountToDeposit + amountToDeposit, + { + fillDeadline: INFINITE_FILL_DEADLINE.toNumber(), + } ); const depositBlock = await spokePool_1.provider.getBlockNumber(); @@ -656,7 +666,7 @@ describe("BundleDataClient: Slow fill handling & validation", async function () await mockConfigStore.update(); (spokePoolClient_1 as any).configStoreClient = mockConfigStore; (spokePoolClient_2 as any).configStoreClient = mockConfigStore; - // Send a deposit. + // Send a legacy deposit. const depositObject = await depositV3( spokePool_1, destinationChainId, @@ -664,7 +674,10 @@ describe("BundleDataClient: Slow fill handling & validation", async function () erc20_1.address, amountToDeposit, erc20_2.address, - amountToDeposit + amountToDeposit, + { + fillDeadline: INFINITE_FILL_DEADLINE.toNumber(), + } ); const depositBlock = await spokePool_1.provider.getBlockNumber(); @@ -701,7 +714,7 @@ describe("BundleDataClient: Slow fill handling & validation", async function () await mockConfigStore.update(); (spokePoolClient_1 as any).configStoreClient = mockConfigStore; (spokePoolClient_2 as any).configStoreClient = mockConfigStore; - // Send a deposit. + // Send a legacy deposit. const depositObject = await depositV3( spokePool_1, destinationChainId, @@ -709,7 +722,10 @@ describe("BundleDataClient: Slow fill handling & validation", async function () erc20_1.address, amountToDeposit, erc20_2.address, - amountToDeposit + amountToDeposit, + { + fillDeadline: INFINITE_FILL_DEADLINE.toNumber(), + } ); const depositBlock = await spokePool_1.provider.getBlockNumber(); diff --git a/test/utils/utils.ts b/test/utils/utils.ts index 171457aebb..6c46e8ad6c 100644 --- a/test/utils/utils.ts +++ b/test/utils/utils.ts @@ -18,6 +18,7 @@ import { sampleRateModel, } from "../constants"; import { SpokePoolDeploymentResult, SpyLoggerResult } from "../types"; +import { INFINITE_FILL_DEADLINE } from "../../src/common"; export { SpyTransport, @@ -286,27 +287,42 @@ export async function depositV3( const quoteTimestamp = opts.quoteTimestamp ?? spokePoolTime; const message = opts.message ?? constants.EMPTY_MESSAGE; const fillDeadline = opts.fillDeadline ?? spokePoolTime + fillDeadlineBuffer; + const isLegacyDeposit = INFINITE_FILL_DEADLINE.eq(fillDeadline); const exclusivityDeadline = opts.exclusivityDeadline ?? 0; const exclusiveRelayer = opts.exclusiveRelayer ?? ZERO_ADDRESS; const [originChainId, txnResponse] = await Promise.all([ spokePool.chainId(), - spokePool - .connect(signer) - .depositV3( - depositor, - recipient, - inputToken, - outputToken, - inputAmount, - outputAmount, - destinationChainId, - exclusiveRelayer, - quoteTimestamp, - fillDeadline, - exclusivityDeadline, - message - ), + isLegacyDeposit + ? spokePool + .connect(signer) + .depositFor( + depositor, + recipient, + inputToken, + inputAmount, + destinationChainId, + inputAmount.sub(outputAmount).mul(toWei(1)).div(inputAmount), + quoteTimestamp, + message, + 0 + ) + : spokePool + .connect(signer) + .depositV3( + depositor, + recipient, + inputToken, + outputToken, + inputAmount, + outputAmount, + destinationChainId, + exclusiveRelayer, + quoteTimestamp, + fillDeadline, + exclusivityDeadline, + message + ), ]); const txnReceipt = await txnResponse.wait(); @@ -317,7 +333,7 @@ export async function depositV3( const { blockNumber, transactionHash, transactionIndex } = txnReceipt; const { logIndex } = eventLog; - return { + const depositObject = { originChainId: Number(originChainId), blockNumber, transactionHash, @@ -325,6 +341,10 @@ export async function depositV3( logIndex, ...spreadEvent(args), }; + if (isLegacyDeposit) { + depositObject.outputToken = outputToken; + } + return depositObject; } export async function updateDeposit(