@across-protocol/sdk 3.4.15 → 3.4.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/clients/BundleDataClient/BundleDataClient.d.ts +1 -1
- package/dist/cjs/clients/BundleDataClient/BundleDataClient.js +174 -123
- package/dist/cjs/clients/BundleDataClient/BundleDataClient.js.map +1 -1
- package/dist/cjs/clients/BundleDataClient/utils/FillUtils.d.ts +3 -1
- package/dist/cjs/clients/BundleDataClient/utils/FillUtils.js +33 -1
- package/dist/cjs/clients/BundleDataClient/utils/FillUtils.js.map +1 -1
- package/dist/cjs/providers/index.d.ts +1 -0
- package/dist/cjs/providers/index.js +2 -0
- package/dist/cjs/providers/index.js.map +1 -1
- package/dist/cjs/providers/mockProvider.d.ts +19 -0
- package/dist/cjs/providers/mockProvider.js +70 -0
- package/dist/cjs/providers/mockProvider.js.map +1 -0
- package/dist/esm/clients/BundleDataClient/BundleDataClient.d.ts +1 -1
- package/dist/esm/clients/BundleDataClient/BundleDataClient.js +204 -151
- package/dist/esm/clients/BundleDataClient/BundleDataClient.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/utils/FillUtils.d.ts +3 -1
- package/dist/esm/clients/BundleDataClient/utils/FillUtils.js +42 -1
- package/dist/esm/clients/BundleDataClient/utils/FillUtils.js.map +1 -1
- package/dist/esm/providers/index.d.ts +1 -0
- package/dist/esm/providers/index.js +2 -0
- package/dist/esm/providers/index.js.map +1 -1
- package/dist/esm/providers/mockProvider.d.ts +23 -0
- package/dist/esm/providers/mockProvider.js +73 -0
- package/dist/esm/providers/mockProvider.js.map +1 -0
- package/dist/types/clients/BundleDataClient/BundleDataClient.d.ts +1 -1
- package/dist/types/clients/BundleDataClient/BundleDataClient.d.ts.map +1 -1
- package/dist/types/clients/BundleDataClient/utils/FillUtils.d.ts +3 -1
- package/dist/types/clients/BundleDataClient/utils/FillUtils.d.ts.map +1 -1
- package/dist/types/providers/index.d.ts +1 -0
- package/dist/types/providers/index.d.ts.map +1 -1
- package/dist/types/providers/mockProvider.d.ts +24 -0
- package/dist/types/providers/mockProvider.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/clients/BundleDataClient/BundleDataClient.ts +98 -70
- package/src/clients/BundleDataClient/utils/FillUtils.ts +47 -2
- package/src/providers/index.ts +1 -0
- package/src/providers/mockProvider.ts +77 -0
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
getImpliedBundleBlockRanges,
|
|
31
31
|
isSlowFill,
|
|
32
32
|
mapAsync,
|
|
33
|
+
filterAsync,
|
|
33
34
|
bnUint32Max,
|
|
34
35
|
isZeroValueDeposit,
|
|
35
36
|
chainIsEvm,
|
|
@@ -48,6 +49,7 @@ import {
|
|
|
48
49
|
prettyPrintV3SpokePoolEvents,
|
|
49
50
|
V3DepositWithBlock,
|
|
50
51
|
V3FillWithBlock,
|
|
52
|
+
verifyFillRepayment,
|
|
51
53
|
} from "./utils";
|
|
52
54
|
|
|
53
55
|
// max(uint256) - 1
|
|
@@ -81,10 +83,11 @@ function updateBundleFillsV3(
|
|
|
81
83
|
fill: V3FillWithBlock,
|
|
82
84
|
lpFeePct: BigNumber,
|
|
83
85
|
repaymentChainId: number,
|
|
84
|
-
repaymentToken: string
|
|
86
|
+
repaymentToken: string,
|
|
87
|
+
repaymentAddress: string
|
|
85
88
|
): void {
|
|
86
89
|
// It is impossible to refund a deposit if the repayment chain is EVM and the relayer is a non-evm address.
|
|
87
|
-
if (chainIsEvm(
|
|
90
|
+
if (chainIsEvm(repaymentChainId) && !isValidEvmAddress(repaymentAddress)) {
|
|
88
91
|
return;
|
|
89
92
|
}
|
|
90
93
|
if (!dict?.[repaymentChainId]?.[repaymentToken]) {
|
|
@@ -96,19 +99,19 @@ function updateBundleFillsV3(
|
|
|
96
99
|
});
|
|
97
100
|
}
|
|
98
101
|
|
|
99
|
-
const bundleFill: BundleFillV3 = { ...fill, lpFeePct };
|
|
102
|
+
const bundleFill: BundleFillV3 = { ...fill, lpFeePct, relayer: repaymentAddress };
|
|
100
103
|
|
|
101
104
|
// Add all fills, slow and fast, to dictionary.
|
|
102
105
|
assign(dict, [repaymentChainId, repaymentToken, "fills"], [bundleFill]);
|
|
103
106
|
|
|
104
107
|
// All fills update the bundle LP fees.
|
|
105
108
|
const refundObj = dict[repaymentChainId][repaymentToken];
|
|
106
|
-
const realizedLpFee =
|
|
109
|
+
const realizedLpFee = bundleFill.inputAmount.mul(bundleFill.lpFeePct).div(fixedPointAdjustment);
|
|
107
110
|
refundObj.realizedLpFees = refundObj.realizedLpFees ? refundObj.realizedLpFees.add(realizedLpFee) : realizedLpFee;
|
|
108
111
|
|
|
109
112
|
// Only fast fills get refunded.
|
|
110
|
-
if (!isSlowFill(
|
|
111
|
-
const refundAmount =
|
|
113
|
+
if (!isSlowFill(bundleFill)) {
|
|
114
|
+
const refundAmount = bundleFill.inputAmount.mul(fixedPointAdjustment.sub(lpFeePct)).div(fixedPointAdjustment);
|
|
112
115
|
refundObj.totalRefundAmount = refundObj.totalRefundAmount
|
|
113
116
|
? refundObj.totalRefundAmount.add(refundAmount)
|
|
114
117
|
: refundAmount;
|
|
@@ -116,10 +119,10 @@ function updateBundleFillsV3(
|
|
|
116
119
|
// Instantiate dictionary if it doesn't exist.
|
|
117
120
|
refundObj.refunds ??= {};
|
|
118
121
|
|
|
119
|
-
if (refundObj.refunds[
|
|
120
|
-
refundObj.refunds[
|
|
122
|
+
if (refundObj.refunds[bundleFill.relayer]) {
|
|
123
|
+
refundObj.refunds[bundleFill.relayer] = refundObj.refunds[bundleFill.relayer].add(refundAmount);
|
|
121
124
|
} else {
|
|
122
|
-
refundObj.refunds[
|
|
125
|
+
refundObj.refunds[bundleFill.relayer] = refundAmount;
|
|
123
126
|
}
|
|
124
127
|
}
|
|
125
128
|
}
|
|
@@ -287,7 +290,7 @@ export class BundleDataClient {
|
|
|
287
290
|
// so as not to affect this approximate refund count.
|
|
288
291
|
const arweaveData = await this.loadArweaveData(bundleEvaluationBlockRanges);
|
|
289
292
|
if (arweaveData === undefined) {
|
|
290
|
-
combinedRefunds = this.getApproximateRefundsForBlockRange(chainIds, bundleEvaluationBlockRanges);
|
|
293
|
+
combinedRefunds = await this.getApproximateRefundsForBlockRange(chainIds, bundleEvaluationBlockRanges);
|
|
291
294
|
} else {
|
|
292
295
|
const { bundleFillsV3, expiredDepositsToRefundV3 } = arweaveData;
|
|
293
296
|
combinedRefunds = getRefundsFromBundle(bundleFillsV3, expiredDepositsToRefundV3);
|
|
@@ -308,50 +311,63 @@ export class BundleDataClient {
|
|
|
308
311
|
}
|
|
309
312
|
|
|
310
313
|
// @dev This helper function should probably be moved to the InventoryClient
|
|
311
|
-
getApproximateRefundsForBlockRange(chainIds: number[], blockRanges: number[][]): CombinedRefunds {
|
|
314
|
+
async getApproximateRefundsForBlockRange(chainIds: number[], blockRanges: number[][]): Promise<CombinedRefunds> {
|
|
312
315
|
const refundsForChain: CombinedRefunds = {};
|
|
313
316
|
for (const chainId of chainIds) {
|
|
314
317
|
if (this.spokePoolClients[chainId] === undefined) {
|
|
315
318
|
continue;
|
|
316
319
|
}
|
|
317
320
|
const chainIndex = chainIds.indexOf(chainId);
|
|
318
|
-
this.spokePoolClients[chainId]
|
|
319
|
-
.
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
return false;
|
|
323
|
-
}
|
|
321
|
+
const fillsToCount = await filterAsync(this.spokePoolClients[chainId].getFills(), async (fill) => {
|
|
322
|
+
if (fill.blockNumber < blockRanges[chainIndex][0] || fill.blockNumber > blockRanges[chainIndex][1]) {
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
324
325
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
.forEach((fill) => {
|
|
336
|
-
const matchingDeposit = this.spokePoolClients[fill.originChainId].getDeposit(fill.depositId);
|
|
337
|
-
assert(isDefined(matchingDeposit), "Deposit not found for fill.");
|
|
338
|
-
const { chainToSendRefundTo, repaymentToken } = getRefundInformationFromFill(
|
|
326
|
+
// If origin spoke pool client isn't defined, we can't validate it.
|
|
327
|
+
if (this.spokePoolClients[fill.originChainId] === undefined) {
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
const matchingDeposit = this.spokePoolClients[fill.originChainId].getDeposit(fill.depositId);
|
|
331
|
+
const hasMatchingDeposit =
|
|
332
|
+
matchingDeposit !== undefined &&
|
|
333
|
+
this.getRelayHashFromEvent(fill) === this.getRelayHashFromEvent(matchingDeposit);
|
|
334
|
+
if (hasMatchingDeposit) {
|
|
335
|
+
const validRepayment = await verifyFillRepayment(
|
|
339
336
|
fill,
|
|
340
|
-
this.
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
337
|
+
this.spokePoolClients[fill.destinationChainId].spokePool.provider,
|
|
338
|
+
matchingDeposit,
|
|
339
|
+
// @dev: to get valid repayment chain ID's, get all chain IDs for the bundle block range and remove
|
|
340
|
+
// disabled block ranges.
|
|
341
|
+
this.clients.configStoreClient
|
|
342
|
+
.getChainIdIndicesForBlock(blockRanges[0][1])
|
|
343
|
+
.filter((_chainId, i) => !isChainDisabled(blockRanges[i]))
|
|
344
344
|
);
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
}
|
|
345
|
+
if (!isDefined(validRepayment)) {
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return hasMatchingDeposit;
|
|
350
|
+
});
|
|
351
|
+
fillsToCount.forEach((fill) => {
|
|
352
|
+
const matchingDeposit = this.spokePoolClients[fill.originChainId].getDeposit(fill.depositId);
|
|
353
|
+
assert(isDefined(matchingDeposit), "Deposit not found for fill.");
|
|
354
|
+
const { chainToSendRefundTo, repaymentToken } = getRefundInformationFromFill(
|
|
355
|
+
fill,
|
|
356
|
+
this.clients.hubPoolClient,
|
|
357
|
+
blockRanges,
|
|
358
|
+
this.chainIdListForBundleEvaluationBlockNumbers,
|
|
359
|
+
matchingDeposit!.fromLiteChain // Use ! because we've already asserted that matchingDeposit is defined.
|
|
360
|
+
);
|
|
361
|
+
// Assume that lp fees are 0 for the sake of speed. In the future we could batch compute
|
|
362
|
+
// these or make hardcoded assumptions based on the origin-repayment chain direction. This might result
|
|
363
|
+
// in slight over estimations of refunds, but its not clear whether underestimating or overestimating is
|
|
364
|
+
// worst from the relayer's perspective.
|
|
365
|
+
const { relayer, inputAmount: refundAmount } = fill;
|
|
366
|
+
refundsForChain[chainToSendRefundTo] ??= {};
|
|
367
|
+
refundsForChain[chainToSendRefundTo][repaymentToken] ??= {};
|
|
368
|
+
const existingRefundAmount = refundsForChain[chainToSendRefundTo][repaymentToken][relayer] ?? bnZero;
|
|
369
|
+
refundsForChain[chainToSendRefundTo][repaymentToken][relayer] = existingRefundAmount.add(refundAmount);
|
|
370
|
+
});
|
|
355
371
|
}
|
|
356
372
|
return refundsForChain;
|
|
357
373
|
}
|
|
@@ -478,7 +494,7 @@ export class BundleDataClient {
|
|
|
478
494
|
// ok for this use case.
|
|
479
495
|
const arweaveData = await this.loadArweaveData(pendingBundleBlockRanges);
|
|
480
496
|
if (arweaveData === undefined) {
|
|
481
|
-
combinedRefunds.push(this.getApproximateRefundsForBlockRange(chainIds, pendingBundleBlockRanges));
|
|
497
|
+
combinedRefunds.push(await this.getApproximateRefundsForBlockRange(chainIds, pendingBundleBlockRanges));
|
|
482
498
|
} else {
|
|
483
499
|
const { bundleFillsV3, expiredDepositsToRefundV3 } = arweaveData;
|
|
484
500
|
combinedRefunds.push(getRefundsFromBundle(bundleFillsV3, expiredDepositsToRefundV3));
|
|
@@ -493,7 +509,7 @@ export class BundleDataClient {
|
|
|
493
509
|
// - Only look up fills sent after the pending bundle's end blocks
|
|
494
510
|
// - Skip LP fee computations and just assume the relayer is being refunded the full deposit.inputAmount
|
|
495
511
|
const start = performance.now();
|
|
496
|
-
combinedRefunds.push(this.getApproximateRefundsForBlockRange(chainIds, widestBundleBlockRanges));
|
|
512
|
+
combinedRefunds.push(await this.getApproximateRefundsForBlockRange(chainIds, widestBundleBlockRanges));
|
|
497
513
|
this.logger.debug({
|
|
498
514
|
at: "BundleDataClient#getNextBundleRefunds",
|
|
499
515
|
message: `Loading approximate refunds for next bundle in ${Math.round(performance.now() - start) / 1000}s.`,
|
|
@@ -868,16 +884,28 @@ export class BundleDataClient {
|
|
|
868
884
|
// tokens to the filler. We can't remove non-empty message deposit here in case there is a slow fill
|
|
869
885
|
// request for the deposit, we'd want to see the fill took place.
|
|
870
886
|
.filter((fill) => fill.blockNumber <= destinationChainBlockRange[1] && !isZeroValueDeposit(fill)),
|
|
871
|
-
async (
|
|
872
|
-
const relayDataHash = this.getRelayHashFromEvent(fill);
|
|
887
|
+
async (_fill) => {
|
|
873
888
|
fillCounter++;
|
|
874
|
-
|
|
889
|
+
const relayDataHash = this.getRelayHashFromEvent(_fill);
|
|
875
890
|
if (v3RelayHashes[relayDataHash]) {
|
|
876
891
|
if (!v3RelayHashes[relayDataHash].fill) {
|
|
877
892
|
assert(
|
|
878
893
|
isDefined(v3RelayHashes[relayDataHash].deposit),
|
|
879
894
|
"Deposit should exist in relay hash dictionary."
|
|
880
895
|
);
|
|
896
|
+
// `fill` will only possibly differ from `_fill` in the `relayer` field, which does not affect the
|
|
897
|
+
// relay hash, so it is safe to modify.
|
|
898
|
+
const fill = await verifyFillRepayment(
|
|
899
|
+
_fill,
|
|
900
|
+
destinationClient.spokePool.provider,
|
|
901
|
+
v3RelayHashes[relayDataHash].deposit!,
|
|
902
|
+
allChainIds
|
|
903
|
+
);
|
|
904
|
+
if (!isDefined(fill)) {
|
|
905
|
+
bundleInvalidFillsV3.push(_fill);
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
|
|
881
909
|
// At this point, the v3RelayHashes entry already existed meaning that there is a matching deposit,
|
|
882
910
|
// so this fill is validated.
|
|
883
911
|
v3RelayHashes[relayDataHash].fill = fill;
|
|
@@ -903,10 +931,10 @@ export class BundleDataClient {
|
|
|
903
931
|
}
|
|
904
932
|
|
|
905
933
|
// At this point, there is no relay hash dictionary entry for this fill, so we need to
|
|
906
|
-
// instantiate the entry.
|
|
934
|
+
// instantiate the entry. We won't modify the fill.relayer until we match it with a deposit.
|
|
907
935
|
v3RelayHashes[relayDataHash] = {
|
|
908
936
|
deposit: undefined,
|
|
909
|
-
fill:
|
|
937
|
+
fill: _fill,
|
|
910
938
|
slowFillRequest: undefined,
|
|
911
939
|
};
|
|
912
940
|
|
|
@@ -918,34 +946,34 @@ export class BundleDataClient {
|
|
|
918
946
|
// older deposit in case the spoke pool client's lookback isn't old enough to find the matching deposit.
|
|
919
947
|
// We can skip this step if the fill's fill deadline is not infinite, because we can assume that the
|
|
920
948
|
// spoke pool clients have loaded deposits old enough to cover all fills with a non-infinite fill deadline.
|
|
921
|
-
if (
|
|
949
|
+
if (_fill.blockNumber >= destinationChainBlockRange[0]) {
|
|
922
950
|
// Fill has a non-infinite expiry, and we can assume our spoke pool clients have old enough deposits
|
|
923
951
|
// to conclude that this fill is invalid if we haven't found a matching deposit in memory, so
|
|
924
952
|
// skip the historical query.
|
|
925
|
-
if (!INFINITE_FILL_DEADLINE.eq(
|
|
926
|
-
bundleInvalidFillsV3.push(
|
|
953
|
+
if (!INFINITE_FILL_DEADLINE.eq(_fill.fillDeadline)) {
|
|
954
|
+
bundleInvalidFillsV3.push(_fill);
|
|
927
955
|
return;
|
|
928
956
|
}
|
|
929
|
-
// If the fill's repayment address is not a valid EVM address and the repayment chain is an EVM chain, the fill is invalid.
|
|
930
|
-
if (chainIsEvm(fill.repaymentChainId) && !isValidEvmAddress(fill.relayer)) {
|
|
931
|
-
const fillTransaction = await originClient.spokePool.provider.getTransaction(fill.transactionHash);
|
|
932
|
-
const originRelayer = fillTransaction.from;
|
|
933
|
-
// Repayment chain is still an EVM chain, but the msg.sender is a bytes32 address, so the fill is invalid.
|
|
934
|
-
if (!isValidEvmAddress(originRelayer)) {
|
|
935
|
-
bundleInvalidFillsV3.push(fill);
|
|
936
|
-
return;
|
|
937
|
-
}
|
|
938
|
-
// Otherwise, assume the relayer to be repaid is the msg.sender.
|
|
939
|
-
fill.relayer = originRelayer;
|
|
940
|
-
}
|
|
941
957
|
// If deposit is using the deterministic relay hash feature, then the following binary search-based
|
|
942
958
|
// algorithm will not work. However, it is impossible to emit an infinite fill deadline using
|
|
943
959
|
// the unsafeDepositV3 function so there is no need to catch the special case.
|
|
944
|
-
const historicalDeposit = await queryHistoricalDepositForFill(originClient,
|
|
960
|
+
const historicalDeposit = await queryHistoricalDepositForFill(originClient, _fill);
|
|
945
961
|
if (!historicalDeposit.found) {
|
|
946
|
-
bundleInvalidFillsV3.push(
|
|
962
|
+
bundleInvalidFillsV3.push(_fill);
|
|
947
963
|
} else {
|
|
948
964
|
const matchedDeposit = historicalDeposit.deposit;
|
|
965
|
+
const fill = await verifyFillRepayment(
|
|
966
|
+
_fill,
|
|
967
|
+
destinationClient.spokePool.provider,
|
|
968
|
+
matchedDeposit,
|
|
969
|
+
allChainIds
|
|
970
|
+
);
|
|
971
|
+
if (!isDefined(fill)) {
|
|
972
|
+
bundleInvalidFillsV3.push(_fill);
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
v3RelayHashes[relayDataHash].fill = fill;
|
|
976
|
+
|
|
949
977
|
// @dev Since queryHistoricalDepositForFill validates the fill by checking individual
|
|
950
978
|
// object property values against the deposit's, we
|
|
951
979
|
// sanity check it here by comparing the full relay hashes. If there's an error here then the
|
|
@@ -1286,7 +1314,7 @@ export class BundleDataClient {
|
|
|
1286
1314
|
chainIds,
|
|
1287
1315
|
associatedDeposit!.fromLiteChain
|
|
1288
1316
|
);
|
|
1289
|
-
updateBundleFillsV3(bundleFillsV3, fill, realizedLpFeePct, chainToSendRefundTo, repaymentToken);
|
|
1317
|
+
updateBundleFillsV3(bundleFillsV3, fill, realizedLpFeePct, chainToSendRefundTo, repaymentToken, fill.relayer);
|
|
1290
1318
|
});
|
|
1291
1319
|
v3SlowFillLpFees.forEach(({ realizedLpFeePct: lpFeePct }, idx) => {
|
|
1292
1320
|
const deposit = validatedBundleSlowFills[idx];
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import _ from "lodash";
|
|
2
|
+
import { providers } from "ethers";
|
|
3
|
+
import { DepositWithBlock, Fill, FillWithBlock } from "../../../interfaces";
|
|
4
|
+
import { getBlockRangeForChain, isSlowFill, chainIsEvm, isValidEvmAddress, isDefined } from "../../../utils";
|
|
3
5
|
import { HubPoolClient } from "../../HubPoolClient";
|
|
4
6
|
|
|
5
7
|
export function getRefundInformationFromFill(
|
|
@@ -44,3 +46,46 @@ export function getRefundInformationFromFill(
|
|
|
44
46
|
repaymentToken,
|
|
45
47
|
};
|
|
46
48
|
}
|
|
49
|
+
|
|
50
|
+
// Verify that a fill sent to an EVM chain has a 20 byte address. If the fill does not, then attempt
|
|
51
|
+
// to repay the `msg.sender` of the relay transaction. Otherwise, return undefined.
|
|
52
|
+
export async function verifyFillRepayment(
|
|
53
|
+
fill: FillWithBlock,
|
|
54
|
+
destinationChainProvider: providers.Provider,
|
|
55
|
+
matchedDeposit: DepositWithBlock,
|
|
56
|
+
possibleRepaymentChainIds: number[]
|
|
57
|
+
): Promise<FillWithBlock | undefined> {
|
|
58
|
+
// Slow fills don't result in repayments so they're always valid.
|
|
59
|
+
if (isSlowFill(fill)) {
|
|
60
|
+
return fill;
|
|
61
|
+
}
|
|
62
|
+
// Lite chain deposits force repayment on origin chain.
|
|
63
|
+
const repaymentChainId = matchedDeposit.fromLiteChain ? fill.originChainId : fill.repaymentChainId;
|
|
64
|
+
// Return undefined if the requested repayment chain ID is not recognized by the hub pool.
|
|
65
|
+
if (!possibleRepaymentChainIds.includes(repaymentChainId)) {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
const updatedFill = _.cloneDeep(fill);
|
|
69
|
+
|
|
70
|
+
// If the fill requests repayment on a chain where the repayment address is not valid, attempt to find a valid
|
|
71
|
+
// repayment address, otherwise return undefined.
|
|
72
|
+
|
|
73
|
+
// Case 1: repayment chain is an EVM chain but repayment address is not a valid EVM address.
|
|
74
|
+
if (chainIsEvm(repaymentChainId) && !isValidEvmAddress(updatedFill.relayer)) {
|
|
75
|
+
// TODO: Handle case where fill was sent on non-EVM chain, in which case the following call would fail
|
|
76
|
+
// or return something unexpected. We'd want to return undefined here.
|
|
77
|
+
const fillTransaction = await destinationChainProvider.getTransaction(updatedFill.transactionHash);
|
|
78
|
+
const destinationRelayer = fillTransaction?.from;
|
|
79
|
+
// Repayment chain is still an EVM chain, but the msg.sender is a bytes32 address, so the fill is invalid.
|
|
80
|
+
if (!isDefined(destinationRelayer) || !isValidEvmAddress(destinationRelayer)) {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
// Otherwise, assume the relayer to be repaid is the msg.sender. We don't need to modify the repayment chain since
|
|
84
|
+
// the getTransaction() call would only succeed if the fill was sent on an EVM chain and therefore the msg.sender
|
|
85
|
+
// is a valid EVM address and the repayment chain is an EVM chain.
|
|
86
|
+
updatedFill.relayer = destinationRelayer;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Case 2: TODO repayment chain is an SVM chain and repayment address is not a valid SVM address.
|
|
90
|
+
return updatedFill;
|
|
91
|
+
}
|
package/src/providers/index.ts
CHANGED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { BigNumber, providers } from "ethers";
|
|
2
|
+
import { Block, BlockTag, FeeData, TransactionResponse } from "@ethersproject/abstract-provider";
|
|
3
|
+
import { bnZero } from "../utils/BigNumberUtils";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @notice Class used to test GasPriceOracle which makes ethers provider calls to the following implemented
|
|
7
|
+
* methods.
|
|
8
|
+
*/
|
|
9
|
+
export class MockedProvider extends providers.StaticJsonRpcProvider {
|
|
10
|
+
private transactions: { [hash: string]: TransactionResponse } = {};
|
|
11
|
+
|
|
12
|
+
constructor(
|
|
13
|
+
readonly stdLastBaseFeePerGas: BigNumber,
|
|
14
|
+
readonly stdMaxPriorityFeePerGas: BigNumber,
|
|
15
|
+
readonly defaultChainId = 1
|
|
16
|
+
) {
|
|
17
|
+
super(undefined, defaultChainId);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
getBlock(_blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>): Promise<Block> {
|
|
21
|
+
const mockBlock: Block = {
|
|
22
|
+
transactions: [],
|
|
23
|
+
hash: "0x",
|
|
24
|
+
parentHash: "0x",
|
|
25
|
+
number: 0,
|
|
26
|
+
nonce: "0",
|
|
27
|
+
difficulty: 0,
|
|
28
|
+
_difficulty: bnZero,
|
|
29
|
+
timestamp: 0,
|
|
30
|
+
gasLimit: bnZero,
|
|
31
|
+
gasUsed: bnZero,
|
|
32
|
+
baseFeePerGas: this.stdLastBaseFeePerGas,
|
|
33
|
+
miner: "0x",
|
|
34
|
+
extraData: "0x",
|
|
35
|
+
};
|
|
36
|
+
return Promise.resolve(mockBlock);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
40
|
+
send(method: string, _params: Array<any>): Promise<any> {
|
|
41
|
+
switch (method) {
|
|
42
|
+
case "eth_maxPriorityFeePerGas":
|
|
43
|
+
return Promise.resolve(this.stdMaxPriorityFeePerGas);
|
|
44
|
+
default:
|
|
45
|
+
throw new Error(`MockedProvider#Unimplemented method: ${method}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
getFeeData(): Promise<FeeData> {
|
|
50
|
+
return Promise.resolve({
|
|
51
|
+
lastBaseFeePerGas: this.stdLastBaseFeePerGas,
|
|
52
|
+
maxPriorityFeePerGas: this.stdMaxPriorityFeePerGas,
|
|
53
|
+
// Following fields unused in GasPrice oracle
|
|
54
|
+
maxFeePerGas: null,
|
|
55
|
+
gasPrice: null,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
getTransaction(hash: string): Promise<TransactionResponse> {
|
|
60
|
+
return Promise.resolve(this.transactions[hash]);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
getGasPrice(): Promise<BigNumber> {
|
|
64
|
+
return Promise.resolve(this.stdLastBaseFeePerGas.add(this.stdMaxPriorityFeePerGas));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
getNetwork(): Promise<{ chainId: number; name: string }> {
|
|
68
|
+
return Promise.resolve({
|
|
69
|
+
name: "mocknetwork",
|
|
70
|
+
chainId: this.defaultChainId,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
_setTransaction(hash: string, transaction: TransactionResponse) {
|
|
75
|
+
this.transactions[hash] = transaction;
|
|
76
|
+
}
|
|
77
|
+
}
|