@across-protocol/sdk 4.1.42 → 4.1.44
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/addressAggregator/adapters/abstract.d.ts +15 -0
- package/dist/cjs/addressAggregator/adapters/abstract.js +83 -0
- package/dist/cjs/addressAggregator/adapters/abstract.js.map +1 -0
- package/dist/cjs/addressAggregator/adapters/bybit.d.ts +5 -9
- package/dist/cjs/addressAggregator/adapters/bybit.js +11 -12
- package/dist/cjs/addressAggregator/adapters/bybit.js.map +1 -1
- package/dist/cjs/addressAggregator/adapters/env.d.ts +5 -7
- package/dist/cjs/addressAggregator/adapters/env.js +12 -11
- package/dist/cjs/addressAggregator/adapters/env.js.map +1 -1
- package/dist/cjs/addressAggregator/adapters/file.d.ts +5 -7
- package/dist/cjs/addressAggregator/adapters/file.js +13 -12
- package/dist/cjs/addressAggregator/adapters/file.js.map +1 -1
- package/dist/cjs/addressAggregator/adapters/risklabs.d.ts +5 -9
- package/dist/cjs/addressAggregator/adapters/risklabs.js +18 -14
- package/dist/cjs/addressAggregator/adapters/risklabs.js.map +1 -1
- package/dist/cjs/addressAggregator/index.js +2 -2
- package/dist/cjs/addressAggregator/index.js.map +1 -1
- package/dist/cjs/addressAggregator/types.d.ts +9 -1
- package/dist/cjs/addressAggregator/types.js.map +1 -1
- package/dist/cjs/arch/evm/SpokeUtils.d.ts +15 -0
- package/dist/cjs/arch/evm/SpokeUtils.js +312 -0
- package/dist/cjs/arch/evm/SpokeUtils.js.map +1 -0
- package/dist/cjs/arch/evm/index.d.ts +1 -1
- package/dist/cjs/arch/evm/index.js +2 -2
- package/dist/cjs/arch/evm/index.js.map +1 -1
- package/dist/cjs/clients/BundleDataClient/BundleDataClient.js +3 -2
- package/dist/cjs/clients/BundleDataClient/BundleDataClient.js.map +1 -1
- package/dist/cjs/clients/BundleDataClient/utils/SuperstructUtils.d.ts +8 -8
- package/dist/cjs/clients/SpokePoolClient/EVMSpokePoolClient.d.ts +3 -2
- package/dist/cjs/clients/SpokePoolClient/EVMSpokePoolClient.js +11 -8
- package/dist/cjs/clients/SpokePoolClient/EVMSpokePoolClient.js.map +1 -1
- package/dist/cjs/clients/SpokePoolClient/SpokePoolClient.d.ts +2 -1
- package/dist/cjs/clients/SpokePoolClient/SpokePoolClient.js.map +1 -1
- package/dist/cjs/constants.d.ts +1 -0
- package/dist/cjs/constants.js +13 -1
- package/dist/cjs/constants.js.map +1 -1
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.js +2 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/providers/types.d.ts +2 -2
- package/dist/cjs/relayFeeCalculator/chain-queries/baseQuery.js +2 -1
- package/dist/cjs/relayFeeCalculator/chain-queries/baseQuery.js.map +1 -1
- package/dist/cjs/utils/DepositUtils.js +1 -1
- package/dist/cjs/utils/DepositUtils.js.map +1 -1
- package/dist/cjs/utils/SpokeUtils.d.ts +2 -14
- package/dist/cjs/utils/SpokeUtils.js +8 -313
- package/dist/cjs/utils/SpokeUtils.js.map +1 -1
- package/dist/cjs/utils/TokenUtils.d.ts +1 -0
- package/dist/cjs/utils/TokenUtils.js +5 -1
- package/dist/cjs/utils/TokenUtils.js.map +1 -1
- package/dist/esm/addressAggregator/adapters/abstract.d.ts +15 -0
- package/dist/esm/addressAggregator/adapters/abstract.js +80 -0
- package/dist/esm/addressAggregator/adapters/abstract.js.map +1 -0
- package/dist/esm/addressAggregator/adapters/bybit.d.ts +5 -9
- package/dist/esm/addressAggregator/adapters/bybit.js +12 -13
- package/dist/esm/addressAggregator/adapters/bybit.js.map +1 -1
- package/dist/esm/addressAggregator/adapters/env.d.ts +5 -7
- package/dist/esm/addressAggregator/adapters/env.js +12 -11
- package/dist/esm/addressAggregator/adapters/env.js.map +1 -1
- package/dist/esm/addressAggregator/adapters/file.d.ts +5 -7
- package/dist/esm/addressAggregator/adapters/file.js +14 -13
- package/dist/esm/addressAggregator/adapters/file.js.map +1 -1
- package/dist/esm/addressAggregator/adapters/risklabs.d.ts +5 -9
- package/dist/esm/addressAggregator/adapters/risklabs.js +19 -15
- package/dist/esm/addressAggregator/adapters/risklabs.js.map +1 -1
- package/dist/esm/addressAggregator/index.js +2 -2
- package/dist/esm/addressAggregator/index.js.map +1 -1
- package/dist/esm/addressAggregator/types.d.ts +9 -1
- package/dist/esm/addressAggregator/types.js.map +1 -1
- package/dist/esm/arch/evm/SpokeUtils.d.ts +58 -0
- package/dist/esm/arch/evm/SpokeUtils.js +345 -0
- package/dist/esm/arch/evm/SpokeUtils.js.map +1 -0
- package/dist/esm/arch/evm/index.d.ts +1 -1
- package/dist/esm/arch/evm/index.js +1 -1
- package/dist/esm/arch/evm/index.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/BundleDataClient.js +3 -2
- package/dist/esm/clients/BundleDataClient/BundleDataClient.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/utils/SuperstructUtils.d.ts +8 -8
- package/dist/esm/clients/SpokePoolClient/EVMSpokePoolClient.d.ts +3 -2
- package/dist/esm/clients/SpokePoolClient/EVMSpokePoolClient.js +7 -4
- package/dist/esm/clients/SpokePoolClient/EVMSpokePoolClient.js.map +1 -1
- package/dist/esm/clients/SpokePoolClient/SpokePoolClient.d.ts +9 -1
- package/dist/esm/clients/SpokePoolClient/SpokePoolClient.js.map +1 -1
- package/dist/esm/constants.d.ts +1 -0
- package/dist/esm/constants.js +12 -0
- package/dist/esm/constants.js.map +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/providers/types.d.ts +2 -2
- package/dist/esm/relayFeeCalculator/chain-queries/baseQuery.js +2 -1
- package/dist/esm/relayFeeCalculator/chain-queries/baseQuery.js.map +1 -1
- package/dist/esm/utils/DepositUtils.js +2 -1
- package/dist/esm/utils/DepositUtils.js.map +1 -1
- package/dist/esm/utils/SpokeUtils.d.ts +5 -55
- package/dist/esm/utils/SpokeUtils.js +11 -348
- package/dist/esm/utils/SpokeUtils.js.map +1 -1
- package/dist/esm/utils/TokenUtils.d.ts +1 -0
- package/dist/esm/utils/TokenUtils.js +3 -0
- package/dist/esm/utils/TokenUtils.js.map +1 -1
- package/dist/types/addressAggregator/adapters/abstract.d.ts +16 -0
- package/dist/types/addressAggregator/adapters/abstract.d.ts.map +1 -0
- package/dist/types/addressAggregator/adapters/bybit.d.ts +5 -9
- package/dist/types/addressAggregator/adapters/bybit.d.ts.map +1 -1
- package/dist/types/addressAggregator/adapters/env.d.ts +5 -7
- package/dist/types/addressAggregator/adapters/env.d.ts.map +1 -1
- package/dist/types/addressAggregator/adapters/file.d.ts +5 -7
- package/dist/types/addressAggregator/adapters/file.d.ts.map +1 -1
- package/dist/types/addressAggregator/adapters/risklabs.d.ts +5 -9
- package/dist/types/addressAggregator/adapters/risklabs.d.ts.map +1 -1
- package/dist/types/addressAggregator/types.d.ts +9 -1
- package/dist/types/addressAggregator/types.d.ts.map +1 -1
- package/dist/types/arch/evm/SpokeUtils.d.ts +59 -0
- package/dist/types/arch/evm/SpokeUtils.d.ts.map +1 -0
- package/dist/types/arch/evm/index.d.ts +1 -1
- package/dist/types/arch/evm/index.d.ts.map +1 -1
- package/dist/types/clients/BundleDataClient/BundleDataClient.d.ts.map +1 -1
- package/dist/types/clients/BundleDataClient/utils/SuperstructUtils.d.ts +8 -8
- package/dist/types/clients/SpokePoolClient/EVMSpokePoolClient.d.ts +3 -2
- package/dist/types/clients/SpokePoolClient/EVMSpokePoolClient.d.ts.map +1 -1
- package/dist/types/clients/SpokePoolClient/SpokePoolClient.d.ts +9 -1
- package/dist/types/clients/SpokePoolClient/SpokePoolClient.d.ts.map +1 -1
- package/dist/types/constants.d.ts +1 -0
- package/dist/types/constants.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/providers/types.d.ts +2 -2
- package/dist/types/relayFeeCalculator/chain-queries/baseQuery.d.ts.map +1 -1
- package/dist/types/utils/DepositUtils.d.ts.map +1 -1
- package/dist/types/utils/SpokeUtils.d.ts +5 -55
- package/dist/types/utils/SpokeUtils.d.ts.map +1 -1
- package/dist/types/utils/TokenUtils.d.ts +1 -0
- package/dist/types/utils/TokenUtils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/addressAggregator/adapters/abstract.ts +74 -0
- package/src/addressAggregator/adapters/bybit.ts +10 -11
- package/src/addressAggregator/adapters/env.ts +10 -10
- package/src/addressAggregator/adapters/file.ts +11 -12
- package/src/addressAggregator/adapters/risklabs.ts +14 -13
- package/src/addressAggregator/index.ts +2 -2
- package/src/addressAggregator/types.ts +10 -1
- package/src/arch/evm/SpokeUtils.ts +324 -0
- package/src/arch/evm/index.ts +1 -1
- package/src/clients/BundleDataClient/BundleDataClient.ts +2 -3
- package/src/clients/SpokePoolClient/EVMSpokePoolClient.ts +27 -16
- package/src/clients/SpokePoolClient/SpokePoolClient.ts +13 -5
- package/src/constants.ts +13 -0
- package/src/index.ts +1 -0
- package/src/relayFeeCalculator/chain-queries/baseQuery.ts +1 -1
- package/src/utils/DepositUtils.ts +2 -1
- package/src/utils/SpokeUtils.ts +16 -314
- package/src/utils/TokenUtils.ts +6 -0
- package/dist/cjs/addressAggregator/adapters/util.d.ts +0 -4
- package/dist/cjs/addressAggregator/adapters/util.js +0 -66
- package/dist/cjs/addressAggregator/adapters/util.js.map +0 -1
- package/dist/esm/addressAggregator/adapters/util.d.ts +0 -4
- package/dist/esm/addressAggregator/adapters/util.js +0 -60
- package/dist/esm/addressAggregator/adapters/util.js.map +0 -1
- package/dist/types/addressAggregator/adapters/util.d.ts +0 -5
- package/dist/types/addressAggregator/adapters/util.d.ts.map +0 -1
- package/src/addressAggregator/adapters/util.ts +0 -47
|
@@ -1,37 +1,36 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { array, defaulted, string } from "superstruct";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { AdapterOptions } from "../types";
|
|
4
|
+
import { AbstractAdapter } from "./abstract";
|
|
5
5
|
|
|
6
6
|
const fileConfig = defaulted(array(string()), []);
|
|
7
7
|
|
|
8
|
-
export class AddressList
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
this.name = `fs:${path}`;
|
|
8
|
+
export class AddressList extends AbstractAdapter {
|
|
9
|
+
constructor(opts?: AdapterOptions) {
|
|
10
|
+
const { path = "addresses.json" } = opts ?? {};
|
|
11
|
+
super(opts?.name ?? `fs:${path}`, path, opts);
|
|
13
12
|
}
|
|
14
13
|
|
|
15
|
-
async update(
|
|
14
|
+
async update(): Promise<string[]> {
|
|
16
15
|
let data: string;
|
|
17
16
|
try {
|
|
18
17
|
data = await readFile(this.path, { encoding: "utf8" });
|
|
19
18
|
} catch (err) {
|
|
20
|
-
return
|
|
19
|
+
return this.error(err);
|
|
21
20
|
}
|
|
22
21
|
|
|
23
22
|
if (!data) {
|
|
24
|
-
return
|
|
23
|
+
return this.error("No addresses found");
|
|
25
24
|
}
|
|
26
25
|
|
|
27
26
|
let addresses: unknown;
|
|
28
27
|
try {
|
|
29
28
|
addresses = JSON.parse(data);
|
|
30
29
|
if (!fileConfig.is(addresses)) {
|
|
31
|
-
return
|
|
30
|
+
return this.error("Address format validation failure.");
|
|
32
31
|
}
|
|
33
32
|
} catch (err) {
|
|
34
|
-
return
|
|
33
|
+
return this.error(err);
|
|
35
34
|
}
|
|
36
35
|
|
|
37
36
|
return Promise.resolve(addresses);
|
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
import { array, string } from "superstruct";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { AdapterOptions } from "../types";
|
|
3
|
+
import { AbstractAdapter } from "./abstract";
|
|
4
4
|
|
|
5
5
|
const RESPONSE_TYPE = array(string());
|
|
6
6
|
const DEFAULT_NAME = "Risk Labs";
|
|
7
7
|
const DEFAULT_URL = "https://blacklist.risklabs.foundation/api/blacklist";
|
|
8
8
|
|
|
9
|
-
export class AddressList
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
constructor(
|
|
14
|
-
readonly name = DEFAULT_NAME,
|
|
15
|
-
readonly url = DEFAULT_URL
|
|
16
|
-
) {}
|
|
9
|
+
export class AddressList extends AbstractAdapter {
|
|
10
|
+
constructor(opts?: AdapterOptions) {
|
|
11
|
+
super(opts?.name ?? DEFAULT_NAME, opts?.path ?? DEFAULT_URL, opts);
|
|
12
|
+
}
|
|
17
13
|
|
|
18
|
-
async update(
|
|
19
|
-
|
|
14
|
+
async update(): Promise<string[]> {
|
|
15
|
+
let response: unknown;
|
|
16
|
+
try {
|
|
17
|
+
response = await this.fetch(this.name, this.path, this.timeout, this.retries);
|
|
18
|
+
} catch (err) {
|
|
19
|
+
return this.error(err);
|
|
20
|
+
}
|
|
20
21
|
|
|
21
22
|
if (!RESPONSE_TYPE.is(response)) {
|
|
22
|
-
return
|
|
23
|
+
return this.error("Failed to validate response");
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
return response;
|
|
@@ -25,7 +25,7 @@ export class AddressAggregator {
|
|
|
25
25
|
|
|
26
26
|
const allAddresses = await mapAsync(this.adapters, async (adapter) => {
|
|
27
27
|
const invalidAddresses: string[] = [];
|
|
28
|
-
const addresses = (await adapter.update(
|
|
28
|
+
const addresses = (await adapter.update())
|
|
29
29
|
.map((address) => {
|
|
30
30
|
try {
|
|
31
31
|
return ethersUtils.getAddress(address.toLowerCase());
|
|
@@ -66,7 +66,7 @@ export class AddressAggregator {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
async function run(): Promise<number> {
|
|
69
|
-
const addressList = new AddressAggregator([new adapters.risklabs.AddressList()]);
|
|
69
|
+
const addressList = new AddressAggregator([new adapters.risklabs.AddressList({ throwOnError: true })]);
|
|
70
70
|
|
|
71
71
|
const addresses = await addressList.update();
|
|
72
72
|
console.log(`Retrieved ${addresses.size} addresses: ${JSON.stringify(Array.from(addresses), null, 2)}`);
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import { Logger } from "../utils";
|
|
2
2
|
|
|
3
|
+
export type AdapterOptions = {
|
|
4
|
+
name?: string;
|
|
5
|
+
path?: string;
|
|
6
|
+
retries?: number;
|
|
7
|
+
timeout?: number;
|
|
8
|
+
throwOnError?: boolean;
|
|
9
|
+
logger?: Logger;
|
|
10
|
+
};
|
|
11
|
+
|
|
3
12
|
export interface AddressListAdapter {
|
|
4
13
|
readonly name: string;
|
|
5
|
-
update(
|
|
14
|
+
update(): Promise<string[]>;
|
|
6
15
|
}
|
|
7
16
|
|
|
8
17
|
export const INVALID_ADDRESS = "";
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import assert from "assert";
|
|
2
|
+
import { BytesLike, Contract, PopulatedTransaction, providers } from "ethers";
|
|
3
|
+
import { CHAIN_IDs } from "../../constants";
|
|
4
|
+
import { Deposit, FillStatus, FillWithBlock, RelayData } from "../../interfaces";
|
|
5
|
+
import {
|
|
6
|
+
bnUint32Max,
|
|
7
|
+
BigNumber,
|
|
8
|
+
toBN,
|
|
9
|
+
bnZero,
|
|
10
|
+
chunk,
|
|
11
|
+
getMessageHash,
|
|
12
|
+
getRelayDataHash,
|
|
13
|
+
isDefined,
|
|
14
|
+
isUnsafeDepositId,
|
|
15
|
+
isZeroAddress,
|
|
16
|
+
getDepositRelayData,
|
|
17
|
+
getNetworkName,
|
|
18
|
+
paginatedEventQuery,
|
|
19
|
+
spreadEventWithBlockNumber,
|
|
20
|
+
toBytes32,
|
|
21
|
+
} from "../../utils";
|
|
22
|
+
|
|
23
|
+
type BlockTag = providers.BlockTag;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param spokePool SpokePool Contract instance.
|
|
27
|
+
* @param deposit V3Deopsit instance.
|
|
28
|
+
* @param repaymentChainId Optional repaymentChainId (defaults to destinationChainId).
|
|
29
|
+
* @returns An Ethers UnsignedTransaction instance.
|
|
30
|
+
*/
|
|
31
|
+
export function populateV3Relay(
|
|
32
|
+
spokePool: Contract,
|
|
33
|
+
deposit: Omit<Deposit, "messageHash">,
|
|
34
|
+
relayer: string,
|
|
35
|
+
repaymentChainId = deposit.destinationChainId
|
|
36
|
+
): Promise<PopulatedTransaction> {
|
|
37
|
+
const relayData = getDepositRelayData(deposit);
|
|
38
|
+
|
|
39
|
+
if (isDefined(deposit.speedUpSignature)) {
|
|
40
|
+
assert(isDefined(deposit.updatedRecipient) && !isZeroAddress(deposit.updatedRecipient));
|
|
41
|
+
assert(isDefined(deposit.updatedOutputAmount));
|
|
42
|
+
assert(isDefined(deposit.updatedMessage));
|
|
43
|
+
return spokePool.populateTransaction.fillRelayWithUpdatedDeposit(
|
|
44
|
+
relayData,
|
|
45
|
+
repaymentChainId,
|
|
46
|
+
toBytes32(relayer),
|
|
47
|
+
deposit.updatedOutputAmount,
|
|
48
|
+
toBytes32(deposit.updatedRecipient),
|
|
49
|
+
deposit.updatedMessage,
|
|
50
|
+
deposit.speedUpSignature,
|
|
51
|
+
{ from: relayer }
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return spokePool.populateTransaction.fillRelay(relayData, repaymentChainId, toBytes32(relayer), { from: relayer });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Retrieves the time from the SpokePool contract at a particular block.
|
|
60
|
+
* @returns The time at the specified block tag.
|
|
61
|
+
*/
|
|
62
|
+
export async function getTimeAt(spokePool: Contract, blockNumber: number): Promise<number> {
|
|
63
|
+
const currentTime = await spokePool.getCurrentTime({ blockTag: blockNumber });
|
|
64
|
+
assert(BigNumber.isBigNumber(currentTime) && currentTime.lt(bnUint32Max));
|
|
65
|
+
return currentTime.toNumber();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Retrieves the chain time at a particular block.
|
|
70
|
+
* @note This should be the same as getTimeAt() but can differ in test. These two functions should be consolidated.
|
|
71
|
+
* @returns The chain time at the specified block tag.
|
|
72
|
+
*/
|
|
73
|
+
export async function getTimestampForBlock(provider: providers.Provider, blockNumber: number): Promise<number> {
|
|
74
|
+
const block = await provider.getBlock(blockNumber);
|
|
75
|
+
return block.timestamp;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Return maximum of fill deadline buffer at start and end of block range.
|
|
80
|
+
* @param spokePool SpokePool contract instance
|
|
81
|
+
* @param startBlock start block
|
|
82
|
+
* @param endBlock end block
|
|
83
|
+
* @returns maximum of fill deadline buffer at start and end block
|
|
84
|
+
*/
|
|
85
|
+
export async function getMaxFillDeadlineInRange(
|
|
86
|
+
spokePool: Contract,
|
|
87
|
+
startBlock: number,
|
|
88
|
+
endBlock: number
|
|
89
|
+
): Promise<number> {
|
|
90
|
+
const fillDeadlineBuffers = await Promise.all([
|
|
91
|
+
spokePool.fillDeadlineBuffer({ blockTag: startBlock }),
|
|
92
|
+
spokePool.fillDeadlineBuffer({ blockTag: endBlock }),
|
|
93
|
+
]);
|
|
94
|
+
return Math.max(fillDeadlineBuffers[0], fillDeadlineBuffers[1]);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Finds the deposit id at a specific block number.
|
|
99
|
+
* @param blockTag The block number to search for the deposit ID at.
|
|
100
|
+
* @returns The deposit ID.
|
|
101
|
+
*/
|
|
102
|
+
export async function getDepositIdAtBlock(contract: Contract, blockTag: number): Promise<BigNumber> {
|
|
103
|
+
const _depositIdAtBlock = await contract.numberOfDeposits({ blockTag });
|
|
104
|
+
const depositIdAtBlock = toBN(_depositIdAtBlock);
|
|
105
|
+
// Sanity check to ensure that the deposit ID is greater than or equal to zero.
|
|
106
|
+
if (depositIdAtBlock.lt(bnZero)) {
|
|
107
|
+
throw new Error("Invalid deposit count");
|
|
108
|
+
}
|
|
109
|
+
return depositIdAtBlock;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export async function findDepositBlock(
|
|
113
|
+
spokePool: Contract,
|
|
114
|
+
depositId: BigNumber,
|
|
115
|
+
lowBlock: number,
|
|
116
|
+
highBlock?: number
|
|
117
|
+
): Promise<number | undefined> {
|
|
118
|
+
// We can only perform this search when we have a safe deposit ID.
|
|
119
|
+
if (isUnsafeDepositId(depositId)) {
|
|
120
|
+
throw new Error(`Cannot binary search for depositId ${depositId}`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
highBlock ??= await spokePool.provider.getBlockNumber();
|
|
124
|
+
assert(highBlock > lowBlock, `Block numbers out of range (${lowBlock} >= ${highBlock})`);
|
|
125
|
+
|
|
126
|
+
// Make sure the deposit occurred within the block range supplied by the caller.
|
|
127
|
+
const [nDepositsLow, nDepositsHigh] = (
|
|
128
|
+
await Promise.all([
|
|
129
|
+
spokePool.numberOfDeposits({ blockTag: lowBlock }),
|
|
130
|
+
spokePool.numberOfDeposits({ blockTag: highBlock }),
|
|
131
|
+
])
|
|
132
|
+
).map((n) => toBN(n));
|
|
133
|
+
|
|
134
|
+
if (nDepositsLow.gt(depositId) || nDepositsHigh.lte(depositId)) {
|
|
135
|
+
return undefined; // Deposit did not occur within the specified block range.
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Find the lowest block number where numberOfDeposits is greater than the requested depositId.
|
|
139
|
+
do {
|
|
140
|
+
const midBlock = Math.floor((highBlock + lowBlock) / 2);
|
|
141
|
+
const nDeposits = toBN(await spokePool.numberOfDeposits({ blockTag: midBlock }));
|
|
142
|
+
|
|
143
|
+
if (nDeposits.gt(depositId)) {
|
|
144
|
+
highBlock = midBlock; // depositId occurred at or earlier than midBlock.
|
|
145
|
+
} else {
|
|
146
|
+
lowBlock = midBlock + 1; // depositId occurred later than midBlock.
|
|
147
|
+
}
|
|
148
|
+
} while (lowBlock < highBlock);
|
|
149
|
+
|
|
150
|
+
return lowBlock;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Find the amount filled for a deposit at a particular block.
|
|
155
|
+
* @param spokePool SpokePool contract instance.
|
|
156
|
+
* @param relayData Deposit information that is used to complete a fill.
|
|
157
|
+
* @param blockTag Block tag (numeric or "latest") to query at.
|
|
158
|
+
* @returns The amount filled for the specified deposit at the requested block (or latest).
|
|
159
|
+
*/
|
|
160
|
+
export async function relayFillStatus(
|
|
161
|
+
spokePool: Contract,
|
|
162
|
+
relayData: RelayData,
|
|
163
|
+
blockTag?: number | "latest",
|
|
164
|
+
destinationChainId?: number
|
|
165
|
+
): Promise<FillStatus> {
|
|
166
|
+
destinationChainId ??= await spokePool.chainId();
|
|
167
|
+
assert(isDefined(destinationChainId));
|
|
168
|
+
|
|
169
|
+
const hash = getRelayDataHash(relayData, destinationChainId);
|
|
170
|
+
const _fillStatus = await spokePool.fillStatuses(hash, { blockTag });
|
|
171
|
+
const fillStatus = Number(_fillStatus);
|
|
172
|
+
|
|
173
|
+
if (![FillStatus.Unfilled, FillStatus.RequestedSlowFill, FillStatus.Filled].includes(fillStatus)) {
|
|
174
|
+
const { originChainId, depositId } = relayData;
|
|
175
|
+
throw new Error(
|
|
176
|
+
`relayFillStatus: Unexpected fillStatus for ${originChainId} deposit ${depositId.toString()} (${fillStatus})`
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return fillStatus;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export async function fillStatusArray(
|
|
184
|
+
spokePool: Contract,
|
|
185
|
+
relayData: RelayData[],
|
|
186
|
+
blockTag: BlockTag = "latest"
|
|
187
|
+
): Promise<(FillStatus | undefined)[]> {
|
|
188
|
+
const fillStatuses = "fillStatuses";
|
|
189
|
+
const destinationChainId = await spokePool.chainId();
|
|
190
|
+
|
|
191
|
+
const queries = relayData.map((relayData) => {
|
|
192
|
+
const hash = getRelayDataHash(relayData, destinationChainId);
|
|
193
|
+
return spokePool.interface.encodeFunctionData(fillStatuses, [hash]);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Chunk the hashes into appropriate sizes to avoid death by rpc.
|
|
197
|
+
const chunkSize = 250;
|
|
198
|
+
const chunkedQueries = chunk(queries, chunkSize);
|
|
199
|
+
|
|
200
|
+
const multicalls = await Promise.all(
|
|
201
|
+
chunkedQueries.map((queries) => spokePool.callStatic.multicall(queries, { blockTag }))
|
|
202
|
+
);
|
|
203
|
+
const status = multicalls
|
|
204
|
+
.map((multicall: BytesLike[]) =>
|
|
205
|
+
multicall.map((result) => spokePool.interface.decodeFunctionResult(fillStatuses, result)[0])
|
|
206
|
+
)
|
|
207
|
+
.flat();
|
|
208
|
+
|
|
209
|
+
const bnUnfilled = toBN(FillStatus.Unfilled);
|
|
210
|
+
const bnFilled = toBN(FillStatus.Filled);
|
|
211
|
+
|
|
212
|
+
return status.map((status: unknown) => {
|
|
213
|
+
return BigNumber.isBigNumber(status) && status.gte(bnUnfilled) && status.lte(bnFilled)
|
|
214
|
+
? status.toNumber()
|
|
215
|
+
: undefined;
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Find the block at which a fill was completed.
|
|
221
|
+
* @todo After SpokePool upgrade, this function can be simplified to use the FillStatus enum.
|
|
222
|
+
* @param spokePool SpokePool contract instance.
|
|
223
|
+
* @param relayData Deposit information that is used to complete a fill.
|
|
224
|
+
* @param lowBlockNumber The lower bound of the search. Must be bounded by SpokePool deployment.
|
|
225
|
+
* @param highBlocknumber Optional upper bound for the search.
|
|
226
|
+
* @returns The block number at which the relay was completed, or undefined.
|
|
227
|
+
*/
|
|
228
|
+
export async function findFillBlock(
|
|
229
|
+
spokePool: Contract,
|
|
230
|
+
relayData: RelayData,
|
|
231
|
+
lowBlockNumber: number,
|
|
232
|
+
highBlockNumber?: number
|
|
233
|
+
): Promise<number | undefined> {
|
|
234
|
+
const { provider } = spokePool;
|
|
235
|
+
highBlockNumber ??= await provider.getBlockNumber();
|
|
236
|
+
assert(highBlockNumber > lowBlockNumber, `Block numbers out of range (${lowBlockNumber} >= ${highBlockNumber})`);
|
|
237
|
+
|
|
238
|
+
// In production the chainId returned from the provider matches 1:1 with the actual chainId. Querying the provider
|
|
239
|
+
// object saves an RPC query because the chainId is cached by StaticJsonRpcProvider instances. In hre, the SpokePool
|
|
240
|
+
// may be configured with a different chainId than what is returned by the provider.
|
|
241
|
+
const destinationChainId = Object.values(CHAIN_IDs).includes(relayData.originChainId)
|
|
242
|
+
? (await provider.getNetwork()).chainId
|
|
243
|
+
: Number(await spokePool.chainId());
|
|
244
|
+
assert(
|
|
245
|
+
relayData.originChainId !== destinationChainId,
|
|
246
|
+
`Origin & destination chain IDs must not be equal (${destinationChainId})`
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
// Make sure the relay was completed within the block range supplied by the caller.
|
|
250
|
+
const [initialFillStatus, finalFillStatus] = (
|
|
251
|
+
await Promise.all([
|
|
252
|
+
relayFillStatus(spokePool, relayData, lowBlockNumber, destinationChainId),
|
|
253
|
+
relayFillStatus(spokePool, relayData, highBlockNumber, destinationChainId),
|
|
254
|
+
])
|
|
255
|
+
).map(Number);
|
|
256
|
+
|
|
257
|
+
if (finalFillStatus !== FillStatus.Filled) {
|
|
258
|
+
return undefined; // Wasn't filled within the specified block range.
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Was filled earlier than the specified lowBlock. This is an error by the caller.
|
|
262
|
+
if (initialFillStatus === FillStatus.Filled) {
|
|
263
|
+
const { depositId, originChainId } = relayData;
|
|
264
|
+
const [srcChain, dstChain] = [getNetworkName(originChainId), getNetworkName(destinationChainId)];
|
|
265
|
+
throw new Error(`${srcChain} deposit ${depositId.toString()} filled on ${dstChain} before block ${lowBlockNumber}`);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Find the leftmost block where filledAmount equals the deposit amount.
|
|
269
|
+
do {
|
|
270
|
+
const midBlockNumber = Math.floor((highBlockNumber + lowBlockNumber) / 2);
|
|
271
|
+
const fillStatus = await relayFillStatus(spokePool, relayData, midBlockNumber, destinationChainId);
|
|
272
|
+
|
|
273
|
+
if (fillStatus === FillStatus.Filled) {
|
|
274
|
+
highBlockNumber = midBlockNumber;
|
|
275
|
+
} else {
|
|
276
|
+
lowBlockNumber = midBlockNumber + 1;
|
|
277
|
+
}
|
|
278
|
+
} while (lowBlockNumber < highBlockNumber);
|
|
279
|
+
|
|
280
|
+
return lowBlockNumber;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export async function findFillEvent(
|
|
284
|
+
spokePool: Contract,
|
|
285
|
+
relayData: RelayData,
|
|
286
|
+
lowBlockNumber: number,
|
|
287
|
+
highBlockNumber?: number
|
|
288
|
+
): Promise<FillWithBlock | undefined> {
|
|
289
|
+
const blockNumber = await findFillBlock(spokePool, relayData, lowBlockNumber, highBlockNumber);
|
|
290
|
+
if (!blockNumber) return undefined;
|
|
291
|
+
|
|
292
|
+
// We can hardcode this to 0 to instruct paginatedEventQuery to make a single request for the same block number.
|
|
293
|
+
const maxBlockLookBack = 0;
|
|
294
|
+
const [fromBlock, toBlock] = [blockNumber, blockNumber];
|
|
295
|
+
|
|
296
|
+
const query = (
|
|
297
|
+
await Promise.all([
|
|
298
|
+
paginatedEventQuery(
|
|
299
|
+
spokePool,
|
|
300
|
+
spokePool.filters.FilledRelay(null, null, null, null, null, relayData.originChainId, relayData.depositId),
|
|
301
|
+
{ fromBlock, toBlock, maxBlockLookBack }
|
|
302
|
+
),
|
|
303
|
+
paginatedEventQuery(
|
|
304
|
+
spokePool,
|
|
305
|
+
spokePool.filters.FilledV3Relay(null, null, null, null, null, relayData.originChainId, relayData.depositId),
|
|
306
|
+
{ fromBlock, toBlock, maxBlockLookBack }
|
|
307
|
+
),
|
|
308
|
+
])
|
|
309
|
+
).flat();
|
|
310
|
+
if (query.length === 0) throw new Error(`Failed to find fill event at block ${blockNumber}`);
|
|
311
|
+
const event = query[0];
|
|
312
|
+
// In production the chainId returned from the provider matches 1:1 with the actual chainId. Querying the provider
|
|
313
|
+
// object saves an RPC query because the chainId is cached by StaticJsonRpcProvider instances. In hre, the SpokePool
|
|
314
|
+
// may be configured with a different chainId than what is returned by the provider.
|
|
315
|
+
const destinationChainId = Object.values(CHAIN_IDs).includes(relayData.originChainId)
|
|
316
|
+
? (await spokePool.provider.getNetwork()).chainId
|
|
317
|
+
: Number(await spokePool.chainId());
|
|
318
|
+
const fill = {
|
|
319
|
+
...spreadEventWithBlockNumber(event),
|
|
320
|
+
destinationChainId,
|
|
321
|
+
messageHash: getMessageHash(event.args.message),
|
|
322
|
+
} as FillWithBlock;
|
|
323
|
+
return fill;
|
|
324
|
+
}
|
package/src/arch/evm/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export * from "./SpokeUtils";
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
DepositWithBlock,
|
|
21
21
|
} from "../../interfaces";
|
|
22
22
|
import { SpokePoolClient } from "..";
|
|
23
|
+
import { findFillEvent } from "../../arch/evm";
|
|
23
24
|
import {
|
|
24
25
|
BigNumber,
|
|
25
26
|
bnZero,
|
|
@@ -37,7 +38,6 @@ import {
|
|
|
37
38
|
mapAsync,
|
|
38
39
|
bnUint32Max,
|
|
39
40
|
isZeroValueDeposit,
|
|
40
|
-
findFillEvent,
|
|
41
41
|
isZeroValueFillOrSlowFillRequest,
|
|
42
42
|
chainIsEvm,
|
|
43
43
|
isValidEvmAddress,
|
|
@@ -728,8 +728,7 @@ export class BundleDataClient {
|
|
|
728
728
|
// hasn't queried. This is because this function will usually be called
|
|
729
729
|
// in production with block ranges that were validated by
|
|
730
730
|
// DataworkerUtils.blockRangesAreInvalidForSpokeClients.
|
|
731
|
-
Math.min(queryBlock, spokePoolClients[deposit.destinationChainId].latestBlockSearched)
|
|
732
|
-
deposit.destinationChainId
|
|
731
|
+
Math.min(queryBlock, spokePoolClients[deposit.destinationChainId].latestBlockSearched)
|
|
733
732
|
);
|
|
734
733
|
};
|
|
735
734
|
|
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import { Contract, EventFilter } from "ethers";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
fillStatusArray,
|
|
4
|
+
findDepositBlock,
|
|
5
|
+
getMaxFillDeadlineInRange as getMaxFillDeadline,
|
|
6
|
+
getTimeAt as _getTimeAt,
|
|
7
|
+
relayFillStatus,
|
|
8
|
+
getTimestampForBlock as _getTimestampForBlock,
|
|
9
|
+
} from "../../arch/evm";
|
|
10
|
+
import { DepositWithBlock, FillStatus, RelayData } from "../../interfaces";
|
|
11
|
+
import {
|
|
12
|
+
BigNumber,
|
|
13
|
+
DepositSearchResult,
|
|
14
|
+
getNetworkName,
|
|
15
|
+
InvalidFill,
|
|
16
|
+
isZeroAddress,
|
|
17
|
+
MakeOptional,
|
|
18
|
+
toBN,
|
|
19
|
+
} from "../../utils";
|
|
3
20
|
import {
|
|
4
21
|
EventSearchConfig,
|
|
5
22
|
paginatedEventQuery,
|
|
@@ -10,15 +27,6 @@ import { isUpdateFailureReason } from "../BaseAbstractClient";
|
|
|
10
27
|
import { knownEventNames, SpokePoolClient, SpokePoolUpdate } from "./SpokePoolClient";
|
|
11
28
|
import winston from "winston";
|
|
12
29
|
import { HubPoolClient } from "../HubPoolClient";
|
|
13
|
-
import {
|
|
14
|
-
findDepositBlock,
|
|
15
|
-
getMaxFillDeadlineInRange as getMaxFillDeadline,
|
|
16
|
-
getTimeAt as _getTimeAt,
|
|
17
|
-
relayFillStatus,
|
|
18
|
-
isZeroAddress,
|
|
19
|
-
getTimestampForBlock as _getTimestampForBlock,
|
|
20
|
-
} from "../../utils/SpokeUtils";
|
|
21
|
-
import { DepositWithBlock, FillStatus, RelayData } from "../../interfaces";
|
|
22
30
|
|
|
23
31
|
/**
|
|
24
32
|
* An EVM-specific SpokePoolClient.
|
|
@@ -35,12 +43,15 @@ export class EVMSpokePoolClient extends SpokePoolClient {
|
|
|
35
43
|
super(logger, hubPoolClient, chainId, deploymentBlock, eventSearchConfig);
|
|
36
44
|
}
|
|
37
45
|
|
|
38
|
-
public override relayFillStatus(
|
|
39
|
-
relayData
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
46
|
+
public override relayFillStatus(relayData: RelayData, blockTag?: number | "latest"): Promise<FillStatus> {
|
|
47
|
+
return relayFillStatus(this.spokePool, relayData, blockTag, this.chainId);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public override fillStatusArray(
|
|
51
|
+
relayData: RelayData[],
|
|
52
|
+
blockTag?: number | "latest"
|
|
53
|
+
): Promise<(FillStatus | undefined)[]> {
|
|
54
|
+
return fillStatusArray(this.spokePool, relayData, blockTag);
|
|
44
55
|
}
|
|
45
56
|
|
|
46
57
|
public override getMaxFillDeadlineInRange(startBlock: number, endBlock: number): Promise<number> {
|
|
@@ -810,11 +810,19 @@ export abstract class SpokePoolClient extends BaseAbstractClient {
|
|
|
810
810
|
/**
|
|
811
811
|
* Retrieves the fill status for a given relay data.
|
|
812
812
|
* @param relayData The relay data to retrieve the fill status for.
|
|
813
|
+
* @param blockTag The block at which to query the fill status.
|
|
813
814
|
* @returns The fill status for the given relay data.
|
|
814
815
|
*/
|
|
815
|
-
public abstract relayFillStatus(
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
816
|
+
public abstract relayFillStatus(relayData: RelayData, blockTag?: number | "latest"): Promise<FillStatus>;
|
|
817
|
+
|
|
818
|
+
/**
|
|
819
|
+
* Retrieves the fill status for an array of given relay data.
|
|
820
|
+
* @param relayData The array relay data to retrieve the fill status for.
|
|
821
|
+
* @param blockTag The block at which to query the fill status.
|
|
822
|
+
* @returns The fill status for each of the given relay data.
|
|
823
|
+
*/
|
|
824
|
+
public abstract fillStatusArray(
|
|
825
|
+
relayData: RelayData[],
|
|
826
|
+
blockTag?: number | "latest"
|
|
827
|
+
): Promise<(FillStatus | undefined)[]>;
|
|
820
828
|
}
|
package/src/constants.ts
CHANGED
|
@@ -66,6 +66,19 @@ export const BRIDGED_USDC_SYMBOLS = [
|
|
|
66
66
|
TOKEN_SYMBOLS_MAP.USDzC.symbol,
|
|
67
67
|
];
|
|
68
68
|
|
|
69
|
+
export const STABLE_COIN_SYMBOLS = [
|
|
70
|
+
...BRIDGED_USDC_SYMBOLS,
|
|
71
|
+
TOKEN_SYMBOLS_MAP.USDB.symbol,
|
|
72
|
+
TOKEN_SYMBOLS_MAP.USDC.symbol,
|
|
73
|
+
TOKEN_SYMBOLS_MAP.USDT.symbol,
|
|
74
|
+
TOKEN_SYMBOLS_MAP.DAI.symbol,
|
|
75
|
+
TOKEN_SYMBOLS_MAP["TATARA-USDC"].symbol,
|
|
76
|
+
TOKEN_SYMBOLS_MAP["TATARA-USDT"].symbol,
|
|
77
|
+
TOKEN_SYMBOLS_MAP["TATARA-USDS"].symbol,
|
|
78
|
+
TOKEN_SYMBOLS_MAP.GHO.symbol,
|
|
79
|
+
TOKEN_SYMBOLS_MAP.WGHO.symbol,
|
|
80
|
+
];
|
|
81
|
+
|
|
69
82
|
export const CUSTOM_GAS_TOKENS = {
|
|
70
83
|
[CHAIN_IDs.POLYGON]: "MATIC",
|
|
71
84
|
[CHAIN_IDs.POLYGON_AMOY]: "MATIC",
|
package/src/index.ts
CHANGED
|
@@ -6,10 +6,10 @@ import { Coingecko } from "../../coingecko";
|
|
|
6
6
|
import { CHAIN_IDs, DEFAULT_SIMULATED_RELAYER_ADDRESS } from "../../constants";
|
|
7
7
|
import { Deposit } from "../../interfaces";
|
|
8
8
|
import { SpokePool, SpokePool__factory } from "../../typechain";
|
|
9
|
+
import { populateV3Relay } from "../../arch/evm";
|
|
9
10
|
import {
|
|
10
11
|
BigNumberish,
|
|
11
12
|
TransactionCostEstimate,
|
|
12
|
-
populateV3Relay,
|
|
13
13
|
BigNumber,
|
|
14
14
|
toBNWei,
|
|
15
15
|
bnZero,
|
|
@@ -2,10 +2,10 @@ import assert from "assert";
|
|
|
2
2
|
import { SpokePoolClient } from "../clients";
|
|
3
3
|
import { DEFAULT_CACHING_TTL, EMPTY_MESSAGE, UNDEFINED_MESSAGE_HASH, ZERO_BYTES } from "../constants";
|
|
4
4
|
import { CachingMechanismInterface, Deposit, DepositWithBlock, Fill, RelayData, SlowFillRequest } from "../interfaces";
|
|
5
|
+
import { getMessageHash, isUnsafeDepositId } from "./SpokeUtils";
|
|
5
6
|
import { getNetworkName } from "./NetworkUtils";
|
|
6
7
|
import { bnZero } from "./BigNumberUtils";
|
|
7
8
|
import { getDepositInCache, getDepositKey, setDepositInCache } from "./CachingUtils";
|
|
8
|
-
import { getMessageHash, isUnsafeDepositId } from "./SpokeUtils";
|
|
9
9
|
import { getCurrentTime } from "./TimeUtils";
|
|
10
10
|
import { isDefined } from "./TypeGuards";
|
|
11
11
|
import { isDepositFormedCorrectly } from "./ValidatorUtils";
|
|
@@ -37,6 +37,7 @@ export type DepositSearchResult =
|
|
|
37
37
|
* @throws If the fill's origin chain ID does not match the spoke pool client's chain ID.
|
|
38
38
|
* @throws If the spoke pool client has not been updated.
|
|
39
39
|
*/
|
|
40
|
+
// @todo relocate
|
|
40
41
|
export async function queryHistoricalDepositForFill(
|
|
41
42
|
spokePoolClient: SpokePoolClient,
|
|
42
43
|
fill: Fill | SlowFillRequest,
|