-
Notifications
You must be signed in to change notification settings - Fork 18
feature: Get all Invalid Fills #1085
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
1b82a73
1b8aaed
4a67c15
9727794
a54510a
e6c9da3
64b60e3
02c4d33
64bcaf4
5bb97f1
f9d939e
1be82ad
be74e90
9f8a633
4f60a53
276d648
4fa6106
cc1dfad
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,7 +7,7 @@ import { | |
relayFillStatus, | ||
getTimestampForBlock as _getTimestampForBlock, | ||
} from "../../arch/evm"; | ||
import { DepositWithBlock, FillStatus, RelayData } from "../../interfaces"; | ||
import { DepositWithBlock, FillStatus, Log, RelayData } from "../../interfaces"; | ||
import { | ||
BigNumber, | ||
DepositSearchResult, | ||
|
@@ -16,6 +16,7 @@ import { | |
MakeOptional, | ||
toBN, | ||
EvmAddress, | ||
MultipleDepositSearchResult, | ||
toAddressType, | ||
} from "../../utils"; | ||
import { | ||
|
@@ -146,28 +147,25 @@ export class EVMSpokePoolClient extends SpokePoolClient { | |
return _getTimeAt(this.spokePool, blockNumber); | ||
} | ||
|
||
public override async findDeposit(depositId: BigNumber): Promise<DepositSearchResult> { | ||
let deposit = this.getDeposit(depositId); | ||
if (deposit) { | ||
return { found: true, deposit }; | ||
} | ||
|
||
// No deposit found; revert to searching for it. | ||
const upperBound = this.latestHeightSearched || undefined; // Don't permit block 0 as the high block. | ||
private async queryDepositEvents( | ||
depositId: BigNumber | ||
): Promise<{ events: Log[]; from: number; elapsedMs: number } | { reason: string }> { | ||
const tStart = Date.now(); | ||
const upperBound = this.latestHeightSearched || undefined; | ||
const from = await findDepositBlock(this.spokePool, depositId, this.deploymentBlock, upperBound); | ||
const chain = getNetworkName(this.chainId); | ||
|
||
if (!from) { | ||
const reason = | ||
`Unable to find ${chain} depositId ${depositId}` + | ||
` within blocks [${this.deploymentBlock}, ${upperBound ?? "latest"}].`; | ||
return { found: false, code: InvalidFill.DepositIdNotFound, reason }; | ||
return { | ||
reason: `Unable to find ${chain} depositId ${depositId} within blocks [${this.deploymentBlock}, ${ | ||
upperBound ?? "latest" | ||
}].`, | ||
}; | ||
pxrl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
const to = from; | ||
const tStart = Date.now(); | ||
// Check both V3FundsDeposited and FundsDeposited events to look for a specified depositId. | ||
const { maxLookBack } = this.eventSearchConfig; | ||
const query = ( | ||
const events = ( | ||
await Promise.all([ | ||
paginatedEventQuery( | ||
this.spokePool, | ||
|
@@ -180,15 +178,35 @@ export class EVMSpokePoolClient extends SpokePoolClient { | |
{ from, to, maxLookBack } | ||
), | ||
]) | ||
).flat(); | ||
) | ||
.flat() | ||
.filter(({ args }) => args["depositId"].eq(depositId)); | ||
|
||
const tStop = Date.now(); | ||
return { events, from, elapsedMs: tStop - tStart }; | ||
} | ||
|
||
public override async findDeposit(depositId: BigNumber): Promise<DepositSearchResult> { | ||
let deposit = this.getDeposit(depositId); | ||
if (deposit) { | ||
return { found: true, deposit }; | ||
} | ||
|
||
// No deposit found; revert to searching for it. | ||
const result = await this.queryDepositEvents(depositId); | ||
|
||
if ("reason" in result) { | ||
return { found: false, code: InvalidFill.DepositIdNotFound, reason: result.reason }; | ||
} | ||
|
||
const { events: query, from, elapsedMs } = result; | ||
|
||
const event = query.find(({ args }) => args["depositId"].eq(depositId)); | ||
if (event === undefined) { | ||
return { | ||
found: false, | ||
code: InvalidFill.DepositIdNotFound, | ||
reason: `${chain} depositId ${depositId} not found at block ${from}.`, | ||
reason: `${getNetworkName(this.chainId)} depositId ${depositId} not found at block ${from}.`, | ||
}; | ||
} | ||
|
||
|
@@ -215,12 +233,76 @@ export class EVMSpokePoolClient extends SpokePoolClient { | |
at: "SpokePoolClient#findDeposit", | ||
message: "Located deposit outside of SpokePoolClient's search range", | ||
deposit, | ||
elapsedMs: tStop - tStart, | ||
elapsedMs, | ||
}); | ||
|
||
return { found: true, deposit }; | ||
} | ||
|
||
public override async findAllDeposits(depositId: BigNumber): Promise<MultipleDepositSearchResult> { | ||
// First check memory for deposits | ||
let deposits = this.getDepositsForDepositId(depositId); | ||
if (deposits.length > 0) { | ||
return { found: true, deposits }; | ||
} | ||
|
||
// If no deposits found in memory, try to find on-chain | ||
const result = await this.queryDepositEvents(depositId); | ||
if ("reason" in result) { | ||
return { found: false, code: InvalidFill.DepositIdNotFound, reason: result.reason }; | ||
} | ||
|
||
const { events, elapsedMs } = result; | ||
|
||
if (events.length === 0) { | ||
return { | ||
found: false, | ||
code: InvalidFill.DepositIdNotFound, | ||
reason: `${getNetworkName(this.chainId)} depositId ${depositId} not found at block ${result.from}.`, | ||
}; | ||
} | ||
|
||
// First do all synchronous operations | ||
deposits = events.map((event) => { | ||
const deposit = { | ||
...spreadEventWithBlockNumber(event), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This PR looks fine to me. I'd prefer if we can merge this after we do the address migration though. Once we do that, you'd need to manually cast the address args here (like |
||
inputToken: toAddressType(event.args.inputToken, event.args.originChainId), | ||
outputToken: toAddressType(event.args.outputToken, event.args.destinationChainId), | ||
depositor: toAddressType(event.args.depositor, this.chainId), | ||
recipient: toAddressType(event.args.recipient, event.args.destinationChainId), | ||
exclusiveRelayer: toAddressType(event.args.exclusiveRelayer, event.args.destinationChainId), | ||
originChainId: this.chainId, | ||
fromLiteChain: true, // To be updated immediately afterwards. | ||
toLiteChain: true, // To be updated immediately afterwards. | ||
} as DepositWithBlock; | ||
|
||
if (deposit.outputToken.isZeroAddress()) { | ||
deposit.outputToken = this.getDestinationTokenForDeposit(deposit); | ||
} | ||
deposit.fromLiteChain = this.isOriginLiteChain(deposit); | ||
deposit.toLiteChain = this.isDestinationLiteChain(deposit); | ||
|
||
return deposit; | ||
}); | ||
|
||
// Then do all async operations in parallel | ||
deposits = await Promise.all( | ||
deposits.map(async (deposit) => ({ | ||
...deposit, | ||
quoteBlockNumber: await this.getBlockNumber(Number(deposit.quoteTimestamp)), | ||
})) | ||
); | ||
|
||
this.logger.debug({ | ||
at: "SpokePoolClient#findAllDeposits", | ||
message: "Located deposits outside of SpokePoolClient's search range", | ||
deposits: deposits, | ||
elapsedMs, | ||
}); | ||
|
||
return { found: true, deposits }; | ||
} | ||
|
||
public override getTimestampForBlock(blockNumber: number): Promise<number> { | ||
return _getTimestampForBlock(this.spokePool.provider, blockNumber); | ||
} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @melisaguevara @bmzig You're both strong on the SVMSpokePoolClient - possible to take a pass over this file? |
Uh oh!
There was an error while loading. Please reload this page.