Skip to content

Commit

Permalink
refactor(SpokePoolClient): Move findDeposit to helper function file
Browse files Browse the repository at this point in the history
This function `findDeposit` is only used by `queryHistoricalDepositForFill`, a utility function. I propose moving this helper function closer to the helper function as it doesn't need to be a client level function. Though, it does make lots of spoke pool client calls but I see it just as intuitive to pass in the relevant spoke pool client into the function. The function effecitvely "finds a deposit" using a spoke pool client's memory.
  • Loading branch information
nicholaspai committed Dec 17, 2024
1 parent d794044 commit 2051c20
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 68 deletions.
70 changes: 4 additions & 66 deletions src/clients/SpokePoolClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -749,7 +749,7 @@ export class SpokePoolClient extends BaseAbstractClient {
* @param timestamp A single timestamp to be resolved via the HubPoolClient.
* @returns The block number on the HubPool chain corresponding to the supplied timestamp.
*/
protected getBlockNumber(timestamp: number): Promise<number> {
getBlockNumber(timestamp: number): Promise<number> {
return this.hubPoolClient?.getBlockNumber(timestamp) ?? Promise.resolve(MAX_BIG_INT.toNumber());
}

Expand All @@ -770,7 +770,7 @@ export class SpokePoolClient extends BaseAbstractClient {
* @param deposit The deposit to retrieve the destination token for.
* @returns The destination token.
*/
protected getDestinationTokenForDeposit(deposit: DepositWithBlock): string {
getDestinationTokenForDeposit(deposit: DepositWithBlock): string {
// If there is no rate model client return address(0).
if (!this.hubPoolClient) {
return ZERO_ADDRESS;
Expand Down Expand Up @@ -805,75 +805,13 @@ export class SpokePoolClient extends BaseAbstractClient {
return this.oldestTime;
}

async findDeposit(depositId: number, destinationChainId: number): Promise<DepositWithBlock> {
// Binary search for event search bounds. This way we can get the blocks before and after the deposit with
// deposit ID = fill.depositId and use those blocks to optimize the search for that deposit.
// Stop searches after a maximum # of searches to limit number of eth_call requests. Make an
// eth_getLogs call on the remaining block range (i.e. the [low, high] remaining from the binary
// search) to find the target deposit ID.
//
// @dev Limiting between 5-10 searches empirically performs best when there are ~300,000 deposits
// for a spoke pool and we're looking for a deposit <5 days older than HEAD.
const searchBounds = await this._getBlockRangeForDepositId(
depositId,
this.deploymentBlock,
this.latestBlockSearched,
7
);

const tStart = Date.now();
const query = await paginatedEventQuery(
this.spokePool,
this.spokePool.filters.V3FundsDeposited(null, null, null, null, null, depositId),
{
fromBlock: searchBounds.low,
toBlock: searchBounds.high,
maxBlockLookBack: this.eventSearchConfig.maxBlockLookBack,
}
);
const tStop = Date.now();

const event = query.find(({ args }) => args["depositId"] === depositId);
if (event === undefined) {
const srcChain = getNetworkName(this.chainId);
const dstChain = getNetworkName(destinationChainId);
throw new Error(
`Could not find deposit ${depositId} for ${dstChain} fill` +
` between ${srcChain} blocks [${searchBounds.low}, ${searchBounds.high}]`
);
}

const deposit = {
...spreadEventWithBlockNumber(event),
originChainId: this.chainId,
quoteBlockNumber: await this.getBlockNumber(Number(event.args["quoteTimestamp"])),
fromLiteChain: true, // To be updated immediately afterwards.
toLiteChain: true, // To be updated immediately afterwards.
} as DepositWithBlock;

if (deposit.outputToken === ZERO_ADDRESS) {
deposit.outputToken = this.getDestinationTokenForDeposit(deposit);
}
deposit.fromLiteChain = this.isOriginLiteChain(deposit);
deposit.toLiteChain = this.isDestinationLiteChain(deposit);

this.logger.debug({
at: "SpokePoolClient#findDeposit",
message: "Located V3 deposit outside of SpokePoolClient's search range",
deposit,
elapsedMs: tStop - tStart,
});

return deposit;
}

/**
* Determines whether a deposit originates from a lite chain.
* @param deposit The deposit to evaluate.
* @returns True if the deposit originates from a lite chain, false otherwise. If the hub pool client is not defined,
* this method will return false.
*/
protected isOriginLiteChain(deposit: DepositWithBlock): boolean {
isOriginLiteChain(deposit: DepositWithBlock): boolean {
return this.configStoreClient?.isChainLiteChainAtTimestamp(deposit.originChainId, deposit.quoteTimestamp) ?? false;
}

Expand All @@ -883,7 +821,7 @@ export class SpokePoolClient extends BaseAbstractClient {
* @returns True if the deposit is destined to a lite chain, false otherwise. If the hub pool client is not defined,
* this method will return false.
*/
protected isDestinationLiteChain(deposit: DepositWithBlock): boolean {
isDestinationLiteChain(deposit: DepositWithBlock): boolean {
return (
this.configStoreClient?.isChainLiteChainAtTimestamp(deposit.destinationChainId, deposit.quoteTimestamp) ?? false
);
Expand Down
73 changes: 71 additions & 2 deletions src/utils/DepositUtils.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import assert from "assert";
import { SpokePoolClient } from "../clients";
import { DEFAULT_CACHING_TTL, EMPTY_MESSAGE } from "../constants";
import { DEFAULT_CACHING_TTL, EMPTY_MESSAGE, ZERO_ADDRESS } from "../constants";
import { CachingMechanismInterface, Deposit, DepositWithBlock, Fill, SlowFillRequest } from "../interfaces";
import { getNetworkName } from "./NetworkUtils";
import { getDepositInCache, getDepositKey, setDepositInCache } from "./CachingUtils";
import { validateFillForDeposit } from "./FlowUtils";
import { getCurrentTime } from "./TimeUtils";
import { isDefined } from "./TypeGuards";
import { isDepositFormedCorrectly } from "./ValidatorUtils";
import { getBlockRangeForDepositId } from "./SpokeUtils";
import { paginatedEventQuery, spreadEventWithBlockNumber } from "./EventUtils";

// Load a deposit for a fill if the fill's deposit ID is outside this client's search range.
// This can be used by the Dataworker to determine whether to give a relayer a refund for a fill
Expand Down Expand Up @@ -107,7 +109,7 @@ export async function queryHistoricalDepositForFill(
if (isDefined(cachedDeposit)) {
deposit = cachedDeposit as DepositWithBlock;
} else {
deposit = await spokePoolClient.findDeposit(fill.depositId, fill.destinationChainId);
deposit = await findDeposit(spokePoolClient, fill.depositId, fill.destinationChainId);
if (cache) {
await setDepositInCache(deposit, getCurrentTime(), cache, DEFAULT_CACHING_TTL);
}
Expand All @@ -125,6 +127,73 @@ export async function queryHistoricalDepositForFill(
};
}

export async function findDeposit(
spokePoolClient: SpokePoolClient,
depositId: number,
destinationChainId: number
): Promise<DepositWithBlock> {
// Binary search for event search bounds. This way we can get the blocks before and after the deposit with
// deposit ID = fill.depositId and use those blocks to optimize the search for that deposit.
// Stop searches after a maximum # of searches to limit number of eth_call requests. Make an
// eth_getLogs call on the remaining block range (i.e. the [low, high] remaining from the binary
// search) to find the target deposit ID.
//
// @dev Limiting between 5-10 searches empirically performs best when there are ~300,000 deposits
// for a spoke pool and we're looking for a deposit <5 days older than HEAD.
const searchBounds = await getBlockRangeForDepositId(
depositId,
spokePoolClient.deploymentBlock,
spokePoolClient.latestBlockSearched,
7,
spokePoolClient
);

const tStart = Date.now();
const query = await paginatedEventQuery(
spokePoolClient.spokePool,
spokePoolClient.spokePool.filters.V3FundsDeposited(null, null, null, null, null, depositId),
{
fromBlock: searchBounds.low,
toBlock: searchBounds.high,
maxBlockLookBack: spokePoolClient.eventSearchConfig.maxBlockLookBack,
}
);
const tStop = Date.now();

const event = query.find(({ args }) => args["depositId"] === depositId);
if (event === undefined) {
const srcChain = getNetworkName(spokePoolClient.chainId);
const dstChain = getNetworkName(destinationChainId);
throw new Error(
`Could not find deposit ${depositId} for ${dstChain} fill` +
` between ${srcChain} blocks [${searchBounds.low}, ${searchBounds.high}]`
);
}

const deposit = {
...spreadEventWithBlockNumber(event),
originChainId: spokePoolClient.chainId,
quoteBlockNumber: await spokePoolClient.getBlockNumber(Number(event.args["quoteTimestamp"])),
fromLiteChain: true, // To be updated immediately afterwards.
toLiteChain: true, // To be updated immediately afterwards.
} as DepositWithBlock;

if (deposit.outputToken === ZERO_ADDRESS) {
deposit.outputToken = spokePoolClient.getDestinationTokenForDeposit(deposit);
}
deposit.fromLiteChain = spokePoolClient.isOriginLiteChain(deposit);
deposit.toLiteChain = spokePoolClient.isDestinationLiteChain(deposit);

spokePoolClient.logger.debug({
at: "[SDK]:DepositUtils#findDeposit",
message: "Located V3 deposit outside of SpokePoolClient's search range",
deposit,
elapsedMs: tStop - tStart,
});

return deposit;
}

/**
* Determines if a message is empty or not.
* @param message The message to check.
Expand Down

0 comments on commit 2051c20

Please sign in to comment.