-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathFillUtils.ts
129 lines (117 loc) · 5.1 KB
/
FillUtils.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
import _ from "lodash";
import { providers } from "ethers";
import { Deposit, DepositWithBlock, Fill, FillWithBlock } from "../../../interfaces";
import { getBlockRangeForChain, isSlowFill, toAddress, isDefined, chainIsEvm, Address } from "../../../utils";
import { HubPoolClient } from "../../HubPoolClient";
export function getRefundInformationFromFill(
fill: Fill,
hubPoolClient: HubPoolClient,
blockRangesForChains: number[][],
chainIdListForBundleEvaluationBlockNumbers: number[],
fromLiteChain: boolean
): {
chainToSendRefundTo: number;
repaymentToken: Address;
} {
// Handle slow relay where repaymentChainId = 0. Slow relays always pay recipient on destination chain.
// So, save the slow fill under the destination chain, and save the fast fill under its repayment chain.
let chainToSendRefundTo = isSlowFill(fill) ? fill.destinationChainId : fill.repaymentChainId;
// If the fill is for a deposit originating from the lite chain, the repayment chain is the origin chain
// regardless of whether it is a slow or fast fill (we ignore slow fills but this is for posterity).
// @note fill.repaymentChainId should already be set to originChainId but reset it to be safe.
if (fromLiteChain) {
chainToSendRefundTo = fill.originChainId;
}
// Save fill data and associate with repayment chain and L2 token refund should be denominated in.
const endBlockForMainnet = getBlockRangeForChain(
blockRangesForChains,
hubPoolClient.chainId,
chainIdListForBundleEvaluationBlockNumbers
)[1];
const l1TokenCounterpart = hubPoolClient.getL1TokenForL2TokenAtBlock(
fill.inputToken,
fill.originChainId,
endBlockForMainnet
);
const repaymentToken = hubPoolClient.getL2TokenForL1TokenAtBlock(
l1TokenCounterpart,
chainToSendRefundTo,
endBlockForMainnet
);
return {
chainToSendRefundTo,
repaymentToken,
};
}
export function getRepaymentChainId(fill: Fill, matchedDeposit: Deposit): number {
// Lite chain deposits force repayment on origin chain.
return matchedDeposit.fromLiteChain ? matchedDeposit.originChainId : fill.repaymentChainId;
}
export function forceDestinationRepayment(
repaymentChainId: number,
matchedDeposit: Deposit & { quoteBlockNumber: number },
hubPoolClient: HubPoolClient
): boolean {
if (!matchedDeposit.fromLiteChain) {
try {
const l1TokenCounterpart = hubPoolClient.getL1TokenForL2TokenAtBlock(
matchedDeposit.inputToken,
matchedDeposit.originChainId,
matchedDeposit.quoteBlockNumber
);
hubPoolClient.getL2TokenForL1TokenAtBlock(l1TokenCounterpart, repaymentChainId, matchedDeposit.quoteBlockNumber);
// Repayment token could be found, this is a valid repayment chain.
return false;
} catch {
// Repayment token doesn't exist on repayment chain via PoolRebalanceRoutes, impossible to repay filler there.
return true;
}
} else {
return false;
}
}
// Verify that a fill sent to an EVM chain has a 20 byte address. If the fill does not, then attempt
// to repay the `msg.sender` of the relay transaction. Otherwise, return undefined.
export async function verifyFillRepayment(
_fill: FillWithBlock,
destinationChainProvider: providers.Provider,
matchedDeposit: DepositWithBlock,
hubPoolClient: HubPoolClient
): Promise<FillWithBlock | undefined> {
const fill = _.cloneDeep(_fill);
// Slow fills don't result in repayments so they're always valid.
if (isSlowFill(fill)) {
return fill;
}
let repaymentChainId = getRepaymentChainId(fill, matchedDeposit);
// If repayment chain doesn't have a Pool Rebalance Route for the input token, then change the repayment
// chain to the destination chain.
if (forceDestinationRepayment(repaymentChainId, matchedDeposit, hubPoolClient)) {
repaymentChainId = fill.destinationChainId;
}
if (!fill.relayer.isValidEvmAddress()) {
// TODO: Handle case where fill was sent on non-EVM chain, in which case the following call would fail
// or return something unexpected. We'd want to return undefined here.
const fillTransaction = await destinationChainProvider.getTransaction(fill.transactionHash);
const destinationRelayer = fillTransaction?.from;
const replacementRelayer = toAddress(destinationRelayer, repaymentChainId, false);
// Repayment chain is still an EVM chain, but the msg.sender is a bytes32 address, so the fill is invalid.
if (!isDefined(destinationRelayer) || !replacementRelayer.isValidEvmAddress()) {
return undefined;
}
if (!matchedDeposit.fromLiteChain) {
repaymentChainId = fill.destinationChainId;
} else {
// We can't switch repayment chain for a lite chain deposit so just check whether the repayment chain,
// which should be the origin chain, is an EVM chain.
if (!chainIsEvm(repaymentChainId)) {
return undefined;
}
}
fill.relayer = replacementRelayer;
}
// Repayment address is now valid and repayment chain is either origin chain for lite chain or the destination
// chain for cases where the repayment address was invalid. Fill should be valid now.
fill.repaymentChainId = repaymentChainId;
return fill;
}