-
Notifications
You must be signed in to change notification settings - Fork 14
/
DepositUtils.ts
144 lines (129 loc) · 5.9 KB
/
DepositUtils.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import assert from "assert";
import { SpokePoolClient } from "../clients";
import { DEFAULT_CACHING_TTL, EMPTY_MESSAGE } 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";
// 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
// of a deposit older or younger than its fixed lookback.
export enum InvalidFill {
DepositIdInvalid = 0, // Deposit ID seems invalid for origin SpokePool
DepositIdNotFound, // Deposit ID not found (bad RPC data?)
FillMismatch, // Fill does not match deposit parameters for deposit ID.
}
export type DepositSearchResult =
| { found: true; deposit: DepositWithBlock }
| { found: false; code: InvalidFill; reason: string };
/**
* Attempts to resolve a deposit for a fill. If the fill's deposit Id is within the spoke pool client's search range,
* the deposit is returned immediately. Otherwise, the deposit is queried first from the provided cache, and if it is
* not found in the cache, it is queried from the spoke pool client. If the deposit is found, it is cached before
* being returned.
* @param spokePoolClient The spoke pool client to use to query the deposit.
* @param fill The fill to resolve a deposit for.
* @param cache An optional cache to use to store the deposit. Optional.
* @returns The deposit for the fill, or undefined if the deposit could not be found.
* @throws If the fill's origin chain ID does not match the spoke pool client's chain ID.
* @throws If the spoke pool client has not been updated.
*/
export async function queryHistoricalDepositForFill(
spokePoolClient: SpokePoolClient,
fill: Fill | SlowFillRequest,
cache?: CachingMechanismInterface
): Promise<DepositSearchResult> {
if (fill.originChainId !== spokePoolClient.chainId) {
throw new Error(`OriginChainId mismatch (${fill.originChainId} != ${spokePoolClient.chainId})`);
}
// We need to update client so we know the first and last deposit ID's queried for this spoke pool client, as well
// as the global first and last deposit ID's for this spoke pool.
if (!spokePoolClient.isUpdated) {
throw new Error("SpokePoolClient must be updated before querying historical deposits");
}
const { depositId } = fill;
let { firstDepositIdForSpokePool: lowId, lastDepositIdForSpokePool: highId } = spokePoolClient;
if (depositId < lowId || depositId > highId) {
return {
found: false,
code: InvalidFill.DepositIdInvalid,
reason: `Deposit ID ${depositId} is outside of SpokePool bounds [${lowId},${highId}].`,
};
}
({ earliestDepositIdQueried: lowId, latestDepositIdQueried: highId } = spokePoolClient);
if (depositId >= lowId && depositId <= highId) {
const deposit = spokePoolClient.getDeposit(depositId);
if (isDefined(deposit) && validateFillForDeposit(fill, deposit)) {
return { found: true, deposit };
}
return {
found: false,
code: isDefined(deposit) ? InvalidFill.FillMismatch : InvalidFill.DepositIdNotFound,
reason: `Deposit ID ${depositId} not found in SpokePoolClient event buffer.`,
};
}
let deposit: DepositWithBlock, cachedDeposit: Deposit | undefined;
if (cache) {
cachedDeposit = await getDepositInCache(getDepositKey(fill), cache);
// We only want to warn and remove the cached deposit if it
// A: exists
// B: is not formed correctly
if (isDefined(cachedDeposit) && !isDepositFormedCorrectly(cachedDeposit)) {
spokePoolClient.logger.warn({
at: "[SDK]:DepositUtils#queryHistoricalDepositForFill",
message: "Cached deposit was not formed correctly, removing from cache",
fill,
cachedDeposit,
});
// By setting this value to undefined, we eventually have to pull
// the deposit from our spoke pool client. Because this new deposit
// is formed correctly, we will cache it below.
cachedDeposit = undefined;
}
}
if (isDefined(cachedDeposit)) {
deposit = cachedDeposit as DepositWithBlock;
} else {
deposit = await spokePoolClient.findDeposit(fill.depositId, fill.destinationChainId);
if (cache) {
await setDepositInCache(deposit, getCurrentTime(), cache, DEFAULT_CACHING_TTL);
}
}
if (validateFillForDeposit(fill, deposit)) {
return { found: true, deposit };
}
return {
found: false,
code: InvalidFill.FillMismatch,
reason: `Fill is not valid for ${getNetworkName(deposit.originChainId)} deposit ${depositId}`,
};
}
/**
* Determines if a message is empty or not.
* @param message The message to check.
* @returns True if the message is empty, false otherwise.
*/
export function isMessageEmpty(message = EMPTY_MESSAGE): boolean {
return message === "" || message === "0x";
}
/**
* Determines if a deposit was updated via a speed-up transaction.
* @param deposit Deposit to evaluate.
* @returns True if the deposit was updated, otherwise false.
*/
export function isDepositSpedUp(deposit: Deposit): boolean {
return isDefined(deposit.speedUpSignature) && isDefined(deposit.updatedOutputAmount);
}
/**
* Resolves the applicable message for a deposit.
* @param deposit Deposit to evaluate.
* @returns Original or updated message string, depending on whether the depositor updated the deposit.
*/
export function resolveDepositMessage(deposit: Deposit): string {
const message = isDepositSpedUp(deposit) ? deposit.updatedMessage : deposit.message;
assert(isDefined(message)); // Appease tsc about the updatedMessage being possibly undefined.
return message;
}