@across-protocol/sdk 4.0.0-beta.3 → 4.0.0-beta.30
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 +5 -4
- package/dist/cjs/clients/BundleDataClient/BundleDataClient.js +340 -174
- package/dist/cjs/clients/BundleDataClient/BundleDataClient.js.map +1 -1
- package/dist/cjs/clients/BundleDataClient/utils/DataworkerUtils.d.ts +1 -2
- package/dist/cjs/clients/BundleDataClient/utils/DataworkerUtils.js +1 -2
- package/dist/cjs/clients/BundleDataClient/utils/DataworkerUtils.js.map +1 -1
- package/dist/cjs/clients/BundleDataClient/utils/FillUtils.d.ts +5 -1
- package/dist/cjs/clients/BundleDataClient/utils/FillUtils.js +47 -1
- package/dist/cjs/clients/BundleDataClient/utils/FillUtils.js.map +1 -1
- package/dist/cjs/clients/BundleDataClient/utils/SuperstructUtils.d.ts +4 -4
- package/dist/cjs/clients/SpokePoolClient.d.ts +1 -0
- package/dist/cjs/clients/SpokePoolClient.js +13 -4
- package/dist/cjs/clients/SpokePoolClient.js.map +1 -1
- package/dist/cjs/clients/mocks/MockSpokePoolClient.d.ts +2 -1
- package/dist/cjs/clients/mocks/MockSpokePoolClient.js +11 -0
- package/dist/cjs/clients/mocks/MockSpokePoolClient.js.map +1 -1
- package/dist/cjs/constants.d.ts +1 -1
- package/dist/cjs/constants.js +2 -2
- package/dist/cjs/constants.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/cjs/utils/AddressUtils.d.ts +2 -0
- package/dist/cjs/utils/AddressUtils.js +19 -1
- package/dist/cjs/utils/AddressUtils.js.map +1 -1
- package/dist/cjs/utils/CachingUtils.js +1 -1
- package/dist/cjs/utils/CachingUtils.js.map +1 -1
- package/dist/cjs/utils/DepositUtils.d.ts +2 -1
- package/dist/cjs/utils/DepositUtils.js +13 -4
- package/dist/cjs/utils/DepositUtils.js.map +1 -1
- package/dist/cjs/utils/EventUtils.js +21 -0
- package/dist/cjs/utils/EventUtils.js.map +1 -1
- package/dist/cjs/utils/NetworkUtils.d.ts +1 -0
- package/dist/cjs/utils/NetworkUtils.js +6 -1
- package/dist/cjs/utils/NetworkUtils.js.map +1 -1
- package/dist/cjs/utils/SpokeUtils.d.ts +1 -0
- package/dist/cjs/utils/SpokeUtils.js +18 -11
- package/dist/cjs/utils/SpokeUtils.js.map +1 -1
- package/dist/cjs/utils/common.d.ts +1 -0
- package/dist/cjs/utils/common.js +2 -1
- package/dist/cjs/utils/common.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/BundleDataClient.d.ts +5 -4
- package/dist/esm/clients/BundleDataClient/BundleDataClient.js +410 -208
- package/dist/esm/clients/BundleDataClient/BundleDataClient.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/utils/DataworkerUtils.d.ts +1 -2
- package/dist/esm/clients/BundleDataClient/utils/DataworkerUtils.js +2 -3
- package/dist/esm/clients/BundleDataClient/utils/DataworkerUtils.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/utils/FillUtils.d.ts +5 -1
- package/dist/esm/clients/BundleDataClient/utils/FillUtils.js +54 -1
- package/dist/esm/clients/BundleDataClient/utils/FillUtils.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/utils/SuperstructUtils.d.ts +4 -4
- package/dist/esm/clients/SpokePoolClient.d.ts +8 -0
- package/dist/esm/clients/SpokePoolClient.js +20 -4
- package/dist/esm/clients/SpokePoolClient.js.map +1 -1
- package/dist/esm/clients/mocks/MockSpokePoolClient.d.ts +2 -1
- package/dist/esm/clients/mocks/MockSpokePoolClient.js +11 -0
- package/dist/esm/clients/mocks/MockSpokePoolClient.js.map +1 -1
- package/dist/esm/constants.d.ts +1 -1
- package/dist/esm/constants.js +2 -2
- package/dist/esm/constants.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/esm/utils/AddressUtils.d.ts +2 -0
- package/dist/esm/utils/AddressUtils.js +25 -0
- package/dist/esm/utils/AddressUtils.js.map +1 -1
- package/dist/esm/utils/CachingUtils.js +1 -1
- package/dist/esm/utils/CachingUtils.js.map +1 -1
- package/dist/esm/utils/DepositUtils.d.ts +2 -1
- package/dist/esm/utils/DepositUtils.js +14 -5
- package/dist/esm/utils/DepositUtils.js.map +1 -1
- package/dist/esm/utils/EventUtils.js +29 -1
- package/dist/esm/utils/EventUtils.js.map +1 -1
- package/dist/esm/utils/NetworkUtils.d.ts +6 -0
- package/dist/esm/utils/NetworkUtils.js +10 -0
- package/dist/esm/utils/NetworkUtils.js.map +1 -1
- package/dist/esm/utils/SpokeUtils.d.ts +1 -0
- package/dist/esm/utils/SpokeUtils.js +17 -11
- package/dist/esm/utils/SpokeUtils.js.map +1 -1
- package/dist/esm/utils/common.d.ts +1 -0
- package/dist/esm/utils/common.js +1 -0
- package/dist/esm/utils/common.js.map +1 -1
- package/dist/types/clients/BundleDataClient/BundleDataClient.d.ts +5 -4
- package/dist/types/clients/BundleDataClient/BundleDataClient.d.ts.map +1 -1
- package/dist/types/clients/BundleDataClient/utils/DataworkerUtils.d.ts +1 -2
- package/dist/types/clients/BundleDataClient/utils/DataworkerUtils.d.ts.map +1 -1
- package/dist/types/clients/BundleDataClient/utils/FillUtils.d.ts +5 -1
- package/dist/types/clients/BundleDataClient/utils/FillUtils.d.ts.map +1 -1
- package/dist/types/clients/BundleDataClient/utils/SuperstructUtils.d.ts +4 -4
- package/dist/types/clients/SpokePoolClient.d.ts +8 -0
- package/dist/types/clients/SpokePoolClient.d.ts.map +1 -1
- package/dist/types/clients/mocks/MockSpokePoolClient.d.ts +2 -1
- package/dist/types/clients/mocks/MockSpokePoolClient.d.ts.map +1 -1
- package/dist/types/constants.d.ts +1 -1
- package/dist/types/constants.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/dist/types/utils/AddressUtils.d.ts +2 -0
- package/dist/types/utils/AddressUtils.d.ts.map +1 -1
- package/dist/types/utils/DepositUtils.d.ts +2 -1
- package/dist/types/utils/DepositUtils.d.ts.map +1 -1
- package/dist/types/utils/EventUtils.d.ts.map +1 -1
- package/dist/types/utils/NetworkUtils.d.ts +6 -0
- package/dist/types/utils/NetworkUtils.d.ts.map +1 -1
- package/dist/types/utils/SpokeUtils.d.ts +1 -0
- package/dist/types/utils/SpokeUtils.d.ts.map +1 -1
- package/dist/types/utils/common.d.ts +1 -0
- package/dist/types/utils/common.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/clients/BundleDataClient/BundleDataClient.ts +413 -184
- package/src/clients/BundleDataClient/utils/DataworkerUtils.ts +0 -8
- package/src/clients/BundleDataClient/utils/FillUtils.ts +66 -2
- package/src/clients/SpokePoolClient.ts +19 -6
- package/src/clients/mocks/MockSpokePoolClient.ts +14 -0
- package/src/constants.ts +3 -3
- package/src/providers/index.ts +1 -0
- package/src/providers/mockProvider.ts +77 -0
- package/src/utils/AddressUtils.ts +26 -0
- package/src/utils/CachingUtils.ts +1 -1
- package/src/utils/DepositUtils.ts +14 -5
- package/src/utils/EventUtils.ts +29 -1
- package/src/utils/NetworkUtils.ts +11 -0
- package/src/utils/SpokeUtils.ts +27 -13
- package/src/utils/common.ts +2 -0
|
@@ -14,7 +14,6 @@ import {
|
|
|
14
14
|
import {
|
|
15
15
|
bnZero,
|
|
16
16
|
AnyObject,
|
|
17
|
-
groupObjectCountsByTwoProps,
|
|
18
17
|
fixedPointAdjustment,
|
|
19
18
|
count2DDictionaryValues,
|
|
20
19
|
count3DDictionaryValues,
|
|
@@ -26,7 +25,6 @@ import {
|
|
|
26
25
|
updateRunningBalance,
|
|
27
26
|
updateRunningBalanceForDeposit,
|
|
28
27
|
} from "./PoolRebalanceUtils";
|
|
29
|
-
import { V3FillWithBlock } from "./shims";
|
|
30
28
|
import { AcrossConfigStoreClient } from "../../AcrossConfigStoreClient";
|
|
31
29
|
import { HubPoolClient } from "../../HubPoolClient";
|
|
32
30
|
import { buildPoolRebalanceLeafTree } from "./MerkleTreeUtils";
|
|
@@ -83,7 +81,6 @@ export function getRefundsFromBundle(
|
|
|
83
81
|
export function prettyPrintV3SpokePoolEvents(
|
|
84
82
|
bundleDepositsV3: BundleDepositsV3,
|
|
85
83
|
bundleFillsV3: BundleFillsV3,
|
|
86
|
-
bundleInvalidFillsV3: V3FillWithBlock[],
|
|
87
84
|
bundleSlowFillsV3: BundleSlowFills,
|
|
88
85
|
expiredDepositsToRefundV3: ExpiredDepositsToRefundV3,
|
|
89
86
|
unexecutableSlowFills: BundleExcessSlowFills
|
|
@@ -94,11 +91,6 @@ export function prettyPrintV3SpokePoolEvents(
|
|
|
94
91
|
bundleSlowFillsV3: count2DDictionaryValues(bundleSlowFillsV3),
|
|
95
92
|
expiredDepositsToRefundV3: count2DDictionaryValues(expiredDepositsToRefundV3),
|
|
96
93
|
unexecutableSlowFills: count2DDictionaryValues(unexecutableSlowFills),
|
|
97
|
-
allInvalidFillsInRangeByDestinationChainAndRelayer: groupObjectCountsByTwoProps(
|
|
98
|
-
bundleInvalidFillsV3,
|
|
99
|
-
"destinationChainId",
|
|
100
|
-
(fill) => `${fill.relayer}`
|
|
101
|
-
),
|
|
102
94
|
};
|
|
103
95
|
}
|
|
104
96
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import _ from "lodash";
|
|
2
|
+
import { providers } from "ethers";
|
|
3
|
+
import { Deposit, 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,65 @@ export function getRefundInformationFromFill(
|
|
|
44
46
|
repaymentToken,
|
|
45
47
|
};
|
|
46
48
|
}
|
|
49
|
+
|
|
50
|
+
export function getRepaymentChainId(fill: Fill, matchedDeposit: Deposit): number {
|
|
51
|
+
// Lite chain deposits force repayment on origin chain.
|
|
52
|
+
return matchedDeposit.fromLiteChain ? fill.originChainId : fill.repaymentChainId;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function isEvmRepaymentValid(
|
|
56
|
+
fill: Fill,
|
|
57
|
+
repaymentChainId: number,
|
|
58
|
+
possibleRepaymentChainIds: number[] = []
|
|
59
|
+
): boolean {
|
|
60
|
+
// Slow fills don't result in repayments so they're always valid.
|
|
61
|
+
if (isSlowFill(fill)) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
// Return undefined if the requested repayment chain ID is not in a passed in set of eligible chains. This can
|
|
65
|
+
// be used by the caller to narrow the chains to those that are not disabled in the config store.
|
|
66
|
+
if (possibleRepaymentChainIds.length > 0 && !possibleRepaymentChainIds.includes(repaymentChainId)) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
return chainIsEvm(repaymentChainId) && isValidEvmAddress(fill.relayer);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Verify that a fill sent to an EVM chain has a 20 byte address. If the fill does not, then attempt
|
|
73
|
+
// to repay the `msg.sender` of the relay transaction. Otherwise, return undefined.
|
|
74
|
+
export async function verifyFillRepayment(
|
|
75
|
+
_fill: FillWithBlock,
|
|
76
|
+
destinationChainProvider: providers.Provider,
|
|
77
|
+
matchedDeposit: DepositWithBlock,
|
|
78
|
+
possibleRepaymentChainIds: number[] = []
|
|
79
|
+
): Promise<FillWithBlock | undefined> {
|
|
80
|
+
const fill = _.cloneDeep(_fill);
|
|
81
|
+
|
|
82
|
+
const repaymentChainId = getRepaymentChainId(fill, matchedDeposit);
|
|
83
|
+
const validEvmRepayment = isEvmRepaymentValid(fill, repaymentChainId, possibleRepaymentChainIds);
|
|
84
|
+
|
|
85
|
+
// Case 1: Repayment chain is EVM and repayment address is valid EVM address.
|
|
86
|
+
if (validEvmRepayment) {
|
|
87
|
+
return fill;
|
|
88
|
+
}
|
|
89
|
+
// Case 2: Repayment chain is EVM but repayment address is not a valid EVM address. Attempt to switch repayment
|
|
90
|
+
// address to msg.sender of relay transaction.
|
|
91
|
+
else if (chainIsEvm(repaymentChainId) && !isValidEvmAddress(fill.relayer)) {
|
|
92
|
+
// TODO: Handle case where fill was sent on non-EVM chain, in which case the following call would fail
|
|
93
|
+
// or return something unexpected. We'd want to return undefined here.
|
|
94
|
+
const fillTransaction = await destinationChainProvider.getTransaction(fill.transactionHash);
|
|
95
|
+
const destinationRelayer = fillTransaction?.from;
|
|
96
|
+
// Repayment chain is still an EVM chain, but the msg.sender is a bytes32 address, so the fill is invalid.
|
|
97
|
+
if (!isDefined(destinationRelayer) || !isValidEvmAddress(destinationRelayer)) {
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
// Otherwise, assume the relayer to be repaid is the msg.sender. We don't need to modify the repayment chain since
|
|
101
|
+
// the getTransaction() call would only succeed if the fill was sent on an EVM chain and therefore the msg.sender
|
|
102
|
+
// is a valid EVM address and the repayment chain is an EVM chain.
|
|
103
|
+
fill.relayer = destinationRelayer;
|
|
104
|
+
return fill;
|
|
105
|
+
}
|
|
106
|
+
// Case 3: Repayment chain is not an EVM chain, must be invalid.
|
|
107
|
+
else {
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -135,6 +135,18 @@ export class SpokePoolClient extends BaseAbstractClient {
|
|
|
135
135
|
return Object.values(this.depositHashes).filter((deposit) => deposit.destinationChainId === destinationChainId);
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Retrieves a list of duplicate deposits matching the given deposit's deposit hash.
|
|
140
|
+
* @notice A duplicate is considered any deposit sent after the original deposit with the same deposit hash.
|
|
141
|
+
* @param deposit The deposit to find duplicates for.
|
|
142
|
+
* @returns A list of duplicate deposits. Does NOT include the original deposit
|
|
143
|
+
* unless the original deposit is a duplicate.
|
|
144
|
+
*/
|
|
145
|
+
private _getDuplicateDeposits(deposit: DepositWithBlock): DepositWithBlock[] {
|
|
146
|
+
const depositHash = this.getDepositHash(deposit);
|
|
147
|
+
return this.duplicateDepositHashes[depositHash] ?? [];
|
|
148
|
+
}
|
|
149
|
+
|
|
138
150
|
/**
|
|
139
151
|
* Returns a list of all deposits including any duplicate ones. Designed only to be used in use cases where
|
|
140
152
|
* all deposits are required, regardless of duplicates. For example, the Dataworker can use this to refund
|
|
@@ -144,9 +156,10 @@ export class SpokePoolClient extends BaseAbstractClient {
|
|
|
144
156
|
*/
|
|
145
157
|
public getDepositsForDestinationChainWithDuplicates(destinationChainId: number): DepositWithBlock[] {
|
|
146
158
|
const deposits = this.getDepositsForDestinationChain(destinationChainId);
|
|
147
|
-
const duplicateDeposits =
|
|
148
|
-
|
|
149
|
-
|
|
159
|
+
const duplicateDeposits = deposits.reduce((acc, deposit) => {
|
|
160
|
+
const duplicates = this._getDuplicateDeposits(deposit);
|
|
161
|
+
return acc.concat(duplicates);
|
|
162
|
+
}, [] as DepositWithBlock[]);
|
|
150
163
|
return sortEventsAscendingInPlace(deposits.concat(duplicateDeposits.flat()));
|
|
151
164
|
}
|
|
152
165
|
|
|
@@ -331,7 +344,7 @@ export class SpokePoolClient extends BaseAbstractClient {
|
|
|
331
344
|
|
|
332
345
|
this.logger.debug({
|
|
333
346
|
at: "SpokePoolClient::getDepositForFill",
|
|
334
|
-
message: `Rejected fill for ${getNetworkName(fill.originChainId)} deposit ${fill.depositId}.`,
|
|
347
|
+
message: `Rejected fill for ${getNetworkName(fill.originChainId)} deposit ${fill.depositId.toString()}.`,
|
|
335
348
|
reason: match.reason,
|
|
336
349
|
deposit,
|
|
337
350
|
fill,
|
|
@@ -415,7 +428,7 @@ export class SpokePoolClient extends BaseAbstractClient {
|
|
|
415
428
|
* @note This hash takes the form of: `${depositId}-${originChainId}`.
|
|
416
429
|
*/
|
|
417
430
|
public getDepositHash(event: { depositId: BigNumber; originChainId: number }): string {
|
|
418
|
-
return `${event.depositId}-${event.originChainId}`;
|
|
431
|
+
return `${event.depositId.toString()}-${event.originChainId}`;
|
|
419
432
|
}
|
|
420
433
|
|
|
421
434
|
/**
|
|
@@ -833,7 +846,7 @@ export class SpokePoolClient extends BaseAbstractClient {
|
|
|
833
846
|
const srcChain = getNetworkName(this.chainId);
|
|
834
847
|
const dstChain = getNetworkName(destinationChainId);
|
|
835
848
|
throw new Error(
|
|
836
|
-
`Could not find deposit ${depositId} for ${dstChain} fill` +
|
|
849
|
+
`Could not find deposit ${depositId.toString()} for ${dstChain} fill` +
|
|
837
850
|
` between ${srcChain} blocks [${searchBounds.low}, ${searchBounds.high}]`
|
|
838
851
|
);
|
|
839
852
|
}
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
FillWithBlock,
|
|
14
14
|
SlowFillLeaf,
|
|
15
15
|
SpeedUp,
|
|
16
|
+
TokensBridged,
|
|
16
17
|
} from "../../interfaces";
|
|
17
18
|
import { toBN, toBNWei, getCurrentTime, randomAddress, BigNumber, bnZero, bnOne, bnMax } from "../../utils";
|
|
18
19
|
import { SpokePoolClient, SpokePoolUpdate } from "../SpokePoolClient";
|
|
@@ -224,6 +225,19 @@ export class MockSpokePoolClient extends SpokePoolClient {
|
|
|
224
225
|
});
|
|
225
226
|
}
|
|
226
227
|
|
|
228
|
+
setTokensBridged(tokensBridged: TokensBridged): Log {
|
|
229
|
+
const event = "TokensBridged";
|
|
230
|
+
const topics = [tokensBridged.chainId, tokensBridged.leafId, tokensBridged.l2TokenAddress];
|
|
231
|
+
const args = { ...tokensBridged };
|
|
232
|
+
|
|
233
|
+
return this.eventManager.generateEvent({
|
|
234
|
+
event,
|
|
235
|
+
address: this.spokePool.address,
|
|
236
|
+
topics: topics.map((topic) => topic.toString()),
|
|
237
|
+
args,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
227
241
|
requestV3SlowFill(request: SlowFillRequestWithBlock): Log {
|
|
228
242
|
const event = "RequestedV3SlowFill";
|
|
229
243
|
|
package/src/constants.ts
CHANGED
|
@@ -26,9 +26,11 @@ export const SECONDS_PER_YEAR = 31557600; // 365.25 days per year.
|
|
|
26
26
|
*/
|
|
27
27
|
export const HUBPOOL_CHAIN_ID = 1;
|
|
28
28
|
|
|
29
|
-
// List of versions where certain UMIP features were deprecated
|
|
29
|
+
// List of versions where certain UMIP features were deprecated or activated
|
|
30
30
|
export const TRANSFER_THRESHOLD_MAX_CONFIG_STORE_VERSION = 1;
|
|
31
31
|
|
|
32
|
+
export const PRE_FILL_MIN_CONFIG_STORE_VERSION = 5;
|
|
33
|
+
|
|
32
34
|
// A hardcoded identifier used, by default, to tag all Arweave records.
|
|
33
35
|
export const ARWEAVE_TAG_APP_NAME = "across-protocol";
|
|
34
36
|
|
|
@@ -55,8 +57,6 @@ export const DEFAULT_ARWEAVE_STORAGE_ADDRESS = "Z6hjBM8FHu90lYWB8o5jR1dfX92FlV2W
|
|
|
55
57
|
|
|
56
58
|
export const EMPTY_MESSAGE = "0x";
|
|
57
59
|
|
|
58
|
-
export const EMPTY_MESSAGE_HASH = ethersConstants.HashZero;
|
|
59
|
-
|
|
60
60
|
export const BRIDGED_USDC_SYMBOLS = [
|
|
61
61
|
TOKEN_SYMBOLS_MAP["USDC.e"].symbol,
|
|
62
62
|
TOKEN_SYMBOLS_MAP.USDbC.symbol,
|
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
|
+
}
|
|
@@ -38,3 +38,29 @@ export function compareAddressesSimple(addressA?: string, addressB?: string): bo
|
|
|
38
38
|
}
|
|
39
39
|
return addressA.toLowerCase() === addressB.toLowerCase();
|
|
40
40
|
}
|
|
41
|
+
|
|
42
|
+
// Converts an input (assumed to be) bytes32 string into a bytes20 string.
|
|
43
|
+
// If the input is not a bytes32 but is less than type(uint160).max, then this function
|
|
44
|
+
// will still succeed.
|
|
45
|
+
// Throws an error if the string as an unsigned integer is greater than type(uint160).max.
|
|
46
|
+
export function toAddress(hexString: string): string {
|
|
47
|
+
// rawAddress is the address which is not properly checksummed.
|
|
48
|
+
const rawAddress = utils.hexZeroPad(utils.hexStripZeros(hexString), 20);
|
|
49
|
+
return utils.getAddress(rawAddress);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function isValidEvmAddress(address: string): boolean {
|
|
53
|
+
if (utils.isAddress(address)) {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
// We may throw an error here if hexZeroPadFails. This will happen if the address to pad is greater than 20 bytes long, indicating
|
|
57
|
+
// that the address had less than 12 leading zero bytes.
|
|
58
|
+
// We may also throw at getAddress if the input cannot be converted into a checksummed EVM address for some reason.
|
|
59
|
+
// For both cases, this indicates that the address cannot be casted as a bytes20 EVM address, so we should return false.
|
|
60
|
+
try {
|
|
61
|
+
const evmAddress = utils.hexZeroPad(utils.hexStripZeros(address), 20);
|
|
62
|
+
return utils.isAddress(utils.getAddress(evmAddress));
|
|
63
|
+
} catch (_e) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -52,5 +52,5 @@ export async function setDepositInCache(
|
|
|
52
52
|
*/
|
|
53
53
|
export function getDepositKey(bridgeEvent: Deposit | Fill | SlowFillRequest): string {
|
|
54
54
|
const relayHash = getRelayHashFromEvent(bridgeEvent);
|
|
55
|
-
return `deposit_${bridgeEvent.originChainId}_${bridgeEvent.depositId}_${relayHash}`;
|
|
55
|
+
return `deposit_${bridgeEvent.originChainId}_${bridgeEvent.depositId.toString()}_${relayHash}`;
|
|
56
56
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import assert from "assert";
|
|
2
2
|
import { SpokePoolClient } from "../clients";
|
|
3
|
-
import { DEFAULT_CACHING_TTL, EMPTY_MESSAGE,
|
|
3
|
+
import { DEFAULT_CACHING_TTL, EMPTY_MESSAGE, ZERO_BYTES } from "../constants";
|
|
4
4
|
import { CachingMechanismInterface, Deposit, DepositWithBlock, Fill, SlowFillRequest } from "../interfaces";
|
|
5
5
|
import { getNetworkName } from "./NetworkUtils";
|
|
6
6
|
import { getDepositInCache, getDepositKey, setDepositInCache } from "./CachingUtils";
|
|
7
7
|
import { validateFillForDeposit } from "./FlowUtils";
|
|
8
|
+
import { isUnsafeDepositId } from "./SpokeUtils";
|
|
8
9
|
import { getCurrentTime } from "./TimeUtils";
|
|
9
10
|
import { isDefined } from "./TypeGuards";
|
|
10
11
|
import { isDepositFormedCorrectly } from "./ValidatorUtils";
|
|
@@ -17,6 +18,7 @@ export enum InvalidFill {
|
|
|
17
18
|
DepositIdInvalid = 0, // Deposit ID seems invalid for origin SpokePool
|
|
18
19
|
DepositIdNotFound, // Deposit ID not found (bad RPC data?)
|
|
19
20
|
FillMismatch, // Fill does not match deposit parameters for deposit ID.
|
|
21
|
+
DepositIdOutOfRange, // Fill is for a deterministic deposit.
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
export type DepositSearchResult =
|
|
@@ -40,6 +42,13 @@ export async function queryHistoricalDepositForFill(
|
|
|
40
42
|
fill: Fill | SlowFillRequest,
|
|
41
43
|
cache?: CachingMechanismInterface
|
|
42
44
|
): Promise<DepositSearchResult> {
|
|
45
|
+
if (isUnsafeDepositId(fill.depositId)) {
|
|
46
|
+
return {
|
|
47
|
+
found: false,
|
|
48
|
+
code: InvalidFill.DepositIdOutOfRange,
|
|
49
|
+
reason: `Cannot find historical deposit for fill with unsafe deposit ID ${fill.depositId}.`,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
43
52
|
if (fill.originChainId !== spokePoolClient.chainId) {
|
|
44
53
|
throw new Error(`OriginChainId mismatch (${fill.originChainId} != ${spokePoolClient.chainId})`);
|
|
45
54
|
}
|
|
@@ -56,7 +65,7 @@ export async function queryHistoricalDepositForFill(
|
|
|
56
65
|
return {
|
|
57
66
|
found: false,
|
|
58
67
|
code: InvalidFill.DepositIdInvalid,
|
|
59
|
-
reason: `Deposit ID ${depositId} is outside of SpokePool bounds [${lowId},${highId}].`,
|
|
68
|
+
reason: `Deposit ID ${depositId.toString()} is outside of SpokePool bounds [${lowId},${highId}].`,
|
|
60
69
|
};
|
|
61
70
|
}
|
|
62
71
|
|
|
@@ -73,14 +82,14 @@ export async function queryHistoricalDepositForFill(
|
|
|
73
82
|
return {
|
|
74
83
|
found: false,
|
|
75
84
|
code: InvalidFill.FillMismatch,
|
|
76
|
-
reason: `Fill for ${originChain} deposit ID ${depositId} is invalid (${match.reason}).`,
|
|
85
|
+
reason: `Fill for ${originChain} deposit ID ${depositId.toString()} is invalid (${match.reason}).`,
|
|
77
86
|
};
|
|
78
87
|
}
|
|
79
88
|
|
|
80
89
|
return {
|
|
81
90
|
found: false,
|
|
82
91
|
code: InvalidFill.DepositIdNotFound,
|
|
83
|
-
reason: `${originChain} deposit ID ${depositId} not found in SpokePoolClient event buffer.`,
|
|
92
|
+
reason: `${originChain} deposit ID ${depositId.toString()} not found in SpokePoolClient event buffer.`,
|
|
84
93
|
};
|
|
85
94
|
}
|
|
86
95
|
|
|
@@ -151,7 +160,7 @@ export function isMessageEmpty(message = EMPTY_MESSAGE): boolean {
|
|
|
151
160
|
}
|
|
152
161
|
|
|
153
162
|
export function isFillOrSlowFillRequestMessageEmpty(message: string): boolean {
|
|
154
|
-
return isMessageEmpty(message) || message ===
|
|
163
|
+
return isMessageEmpty(message) || message === ZERO_BYTES;
|
|
155
164
|
}
|
|
156
165
|
|
|
157
166
|
/**
|
package/src/utils/EventUtils.ts
CHANGED
|
@@ -3,11 +3,27 @@ import { Result } from "@ethersproject/abi";
|
|
|
3
3
|
import { Contract, Event, EventFilter } from "ethers";
|
|
4
4
|
import { Log, SortableEvent } from "../interfaces";
|
|
5
5
|
import { delay } from "./common";
|
|
6
|
-
import { isDefined, toBN, BigNumberish } from "./";
|
|
6
|
+
import { isDefined, toBN, BigNumberish, toAddress } from "./";
|
|
7
7
|
|
|
8
8
|
const maxRetries = 3;
|
|
9
9
|
const retrySleepTime = 10;
|
|
10
10
|
|
|
11
|
+
// Event fields which changed from an `address` to `bytes32` after the SVM contract upgrade.
|
|
12
|
+
const knownExtendedAddressFields = [
|
|
13
|
+
// TokensBridged
|
|
14
|
+
"l2TokenAddress",
|
|
15
|
+
// FundsDeposited/FilledRelay/RequestedSlowFill
|
|
16
|
+
"inputToken",
|
|
17
|
+
"outputToken",
|
|
18
|
+
"depositor",
|
|
19
|
+
"recipient",
|
|
20
|
+
"exclusiveRelayer",
|
|
21
|
+
// FilledRelay
|
|
22
|
+
"relayer",
|
|
23
|
+
// RequestedSpeedUpDeposit
|
|
24
|
+
"updatedRecipient",
|
|
25
|
+
];
|
|
26
|
+
|
|
11
27
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
12
28
|
export function spreadEvent(args: Result | Record<string, unknown>): { [key: string]: any } {
|
|
13
29
|
const keys = Object.keys(args).filter((key: string) => isNaN(+key)); // Extract non-numeric keys.
|
|
@@ -67,6 +83,18 @@ export function spreadEvent(args: Result | Record<string, unknown>): { [key: str
|
|
|
67
83
|
returnedObject.depositId = toBN(returnedObject.depositId as BigNumberish);
|
|
68
84
|
}
|
|
69
85
|
|
|
86
|
+
// Truncate all fields which may be bytes32 into a bytes20 string.
|
|
87
|
+
for (const field of knownExtendedAddressFields) {
|
|
88
|
+
if (isDefined(returnedObject[field])) {
|
|
89
|
+
let address = String(returnedObject[field]);
|
|
90
|
+
try {
|
|
91
|
+
address = toAddress(address);
|
|
92
|
+
// eslint-disable-next-line no-empty
|
|
93
|
+
} catch (_) {}
|
|
94
|
+
returnedObject[field] = address;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
70
98
|
return returnedObject;
|
|
71
99
|
}
|
|
72
100
|
|
|
@@ -123,6 +123,17 @@ export function chainIsL1(chainId: number): boolean {
|
|
|
123
123
|
return [CHAIN_IDs.MAINNET, CHAIN_IDs.SEPOLIA].includes(chainId);
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
/**
|
|
127
|
+
* Determines whether a chain ID runs on an EVM-like execution layer.
|
|
128
|
+
* @param chainId Chain ID to evaluate.
|
|
129
|
+
* @returns True if chain corresponding to chainId has an EVM-like execution layer.
|
|
130
|
+
*/
|
|
131
|
+
export function chainIsEvm(chainId: number): boolean {
|
|
132
|
+
chainId;
|
|
133
|
+
// TODO: Fix when we support non-EVM chains.
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
|
|
126
137
|
/**
|
|
127
138
|
* Determines whether a chain ID has the capacity for having its USDC bridged via CCTP.
|
|
128
139
|
* @param chainId Chain ID to evaluate.
|
package/src/utils/SpokeUtils.ts
CHANGED
|
@@ -5,6 +5,8 @@ import { Deposit, Fill, FillStatus, FillWithBlock, RelayData, SlowFillRequest }
|
|
|
5
5
|
import { SpokePoolClient } from "../clients";
|
|
6
6
|
import { chunk } from "./ArrayUtils";
|
|
7
7
|
import { BigNumber, toBN, bnOne, bnZero } from "./BigNumberUtils";
|
|
8
|
+
import { keccak256 } from "./common";
|
|
9
|
+
import { isMessageEmpty } from "./DepositUtils";
|
|
8
10
|
import { isDefined } from "./TypeGuards";
|
|
9
11
|
import { getNetworkName } from "./NetworkUtils";
|
|
10
12
|
import { paginatedEventQuery, spreadEventWithBlockNumber } from "./EventUtils";
|
|
@@ -154,7 +156,7 @@ export async function getBlockRangeForDepositId(
|
|
|
154
156
|
// // targetId = 2 <- pass (does not trigger this error)
|
|
155
157
|
// // targetId = 3 <- pass (does not trigger this error)
|
|
156
158
|
throw new Error(
|
|
157
|
-
`Target depositId is less than the initial low block (${targetDepositId} > ${lowestDepositIdInRange})`
|
|
159
|
+
`Target depositId is less than the initial low block (${targetDepositId.toString()} > ${lowestDepositIdInRange})`
|
|
158
160
|
);
|
|
159
161
|
}
|
|
160
162
|
|
|
@@ -209,7 +211,7 @@ export async function getBlockRangeForDepositId(
|
|
|
209
211
|
export async function getDepositIdAtBlock(contract: Contract, blockTag: number): Promise<BigNumber> {
|
|
210
212
|
const _depositIdAtBlock = await contract.numberOfDeposits({ blockTag });
|
|
211
213
|
const depositIdAtBlock = toBN(_depositIdAtBlock);
|
|
212
|
-
// Sanity check to ensure that the deposit ID is
|
|
214
|
+
// Sanity check to ensure that the deposit ID is greater than or equal to zero.
|
|
213
215
|
if (depositIdAtBlock.lt(bnZero)) {
|
|
214
216
|
throw new Error("Invalid deposit count");
|
|
215
217
|
}
|
|
@@ -223,26 +225,34 @@ export async function getDepositIdAtBlock(contract: Contract, blockTag: number):
|
|
|
223
225
|
* @returns The corresponding RelayData hash.
|
|
224
226
|
*/
|
|
225
227
|
export function getRelayDataHash(relayData: RelayData, destinationChainId: number): string {
|
|
228
|
+
const _relayData = {
|
|
229
|
+
...relayData,
|
|
230
|
+
depositor: ethersUtils.hexZeroPad(relayData.depositor, 32),
|
|
231
|
+
recipient: ethersUtils.hexZeroPad(relayData.recipient, 32),
|
|
232
|
+
inputToken: ethersUtils.hexZeroPad(relayData.inputToken, 32),
|
|
233
|
+
outputToken: ethersUtils.hexZeroPad(relayData.outputToken, 32),
|
|
234
|
+
exclusiveRelayer: ethersUtils.hexZeroPad(relayData.exclusiveRelayer, 32),
|
|
235
|
+
};
|
|
226
236
|
return ethersUtils.keccak256(
|
|
227
237
|
ethersUtils.defaultAbiCoder.encode(
|
|
228
238
|
[
|
|
229
239
|
"tuple(" +
|
|
230
|
-
"
|
|
231
|
-
"
|
|
232
|
-
"
|
|
233
|
-
"
|
|
234
|
-
"
|
|
240
|
+
"bytes32 depositor," +
|
|
241
|
+
"bytes32 recipient," +
|
|
242
|
+
"bytes32 exclusiveRelayer," +
|
|
243
|
+
"bytes32 inputToken," +
|
|
244
|
+
"bytes32 outputToken," +
|
|
235
245
|
"uint256 inputAmount," +
|
|
236
246
|
"uint256 outputAmount," +
|
|
237
247
|
"uint256 originChainId," +
|
|
238
|
-
"
|
|
248
|
+
"uint256 depositId," +
|
|
239
249
|
"uint32 fillDeadline," +
|
|
240
250
|
"uint32 exclusivityDeadline," +
|
|
241
251
|
"bytes message" +
|
|
242
252
|
")",
|
|
243
253
|
"uint256 destinationChainId",
|
|
244
254
|
],
|
|
245
|
-
[
|
|
255
|
+
[_relayData, destinationChainId]
|
|
246
256
|
)
|
|
247
257
|
);
|
|
248
258
|
}
|
|
@@ -281,7 +291,9 @@ export async function relayFillStatus(
|
|
|
281
291
|
|
|
282
292
|
if (![FillStatus.Unfilled, FillStatus.RequestedSlowFill, FillStatus.Filled].includes(fillStatus)) {
|
|
283
293
|
const { originChainId, depositId } = relayData;
|
|
284
|
-
throw new Error(
|
|
294
|
+
throw new Error(
|
|
295
|
+
`relayFillStatus: Unexpected fillStatus for ${originChainId} deposit ${depositId.toString()} (${fillStatus})`
|
|
296
|
+
);
|
|
285
297
|
}
|
|
286
298
|
|
|
287
299
|
return fillStatus;
|
|
@@ -345,7 +357,6 @@ export async function findFillBlock(
|
|
|
345
357
|
// In production the chainId returned from the provider matches 1:1 with the actual chainId. Querying the provider
|
|
346
358
|
// object saves an RPC query becasue the chainId is cached by StaticJsonRpcProvider instances. In hre, the SpokePool
|
|
347
359
|
// may be configured with a different chainId than what is returned by the provider.
|
|
348
|
-
// @todo Sub out actual chain IDs w/ CHAIN_IDs constants
|
|
349
360
|
const destinationChainId = Object.values(CHAIN_IDs).includes(relayData.originChainId)
|
|
350
361
|
? (await provider.getNetwork()).chainId
|
|
351
362
|
: Number(await spokePool.chainId());
|
|
@@ -370,7 +381,7 @@ export async function findFillBlock(
|
|
|
370
381
|
if (initialFillStatus === FillStatus.Filled) {
|
|
371
382
|
const { depositId, originChainId } = relayData;
|
|
372
383
|
const [srcChain, dstChain] = [getNetworkName(originChainId), getNetworkName(destinationChainId)];
|
|
373
|
-
throw new Error(`${srcChain} deposit ${depositId} filled on ${dstChain} before block ${lowBlockNumber}`);
|
|
384
|
+
throw new Error(`${srcChain} deposit ${depositId.toString()} filled on ${dstChain} before block ${lowBlockNumber}`);
|
|
374
385
|
}
|
|
375
386
|
|
|
376
387
|
// Find the leftmost block where filledAmount equals the deposit amount.
|
|
@@ -411,7 +422,6 @@ export async function findFillEvent(
|
|
|
411
422
|
// In production the chainId returned from the provider matches 1:1 with the actual chainId. Querying the provider
|
|
412
423
|
// object saves an RPC query becasue the chainId is cached by StaticJsonRpcProvider instances. In hre, the SpokePool
|
|
413
424
|
// may be configured with a different chainId than what is returned by the provider.
|
|
414
|
-
// @todo Sub out actual chain IDs w/ CHAIN_IDs constants
|
|
415
425
|
const destinationChainId = Object.values(CHAIN_IDs).includes(relayData.originChainId)
|
|
416
426
|
? (await spokePool.provider.getNetwork()).chainId
|
|
417
427
|
: Number(await spokePool.chainId());
|
|
@@ -426,3 +436,7 @@ export async function findFillEvent(
|
|
|
426
436
|
export function isZeroAddress(address: string): boolean {
|
|
427
437
|
return address === ZERO_ADDRESS || address === ZERO_BYTES;
|
|
428
438
|
}
|
|
439
|
+
|
|
440
|
+
export function getMessageHash(message: string): string {
|
|
441
|
+
return isMessageEmpty(message) ? ZERO_BYTES : keccak256(message);
|
|
442
|
+
}
|
package/src/utils/common.ts
CHANGED
|
@@ -7,6 +7,8 @@ export type Decimalish = string | number | Decimal;
|
|
|
7
7
|
export const AddressZero = ethers.constants.AddressZero;
|
|
8
8
|
export const MAX_BIG_INT = BigNumber.from(Number.MAX_SAFE_INTEGER.toString());
|
|
9
9
|
|
|
10
|
+
export const { keccak256 } = ethers.utils;
|
|
11
|
+
|
|
10
12
|
/**
|
|
11
13
|
* toBNWei.
|
|
12
14
|
*
|