Skip to content

Commit 6b1b65a

Browse files
authored
feat(svm): Initial SpokeUtils skeleton (#960)
1 parent 8b3ed44 commit 6b1b65a

File tree

7 files changed

+205
-15
lines changed

7 files changed

+205
-15
lines changed

Diff for: package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@across-protocol/sdk",
33
"author": "UMA Team",
4-
"version": "4.1.42",
4+
"version": "4.1.43",
55
"license": "AGPL-3.0",
66
"homepage": "https://docs.across.to/reference/sdk",
77
"files": [

Diff for: src/arch/svm/SpokeUtils.ts

+178
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
import { Rpc, SolanaRpcApi } from "@solana/kit";
2+
import { Deposit, FillStatus, FillWithBlock, RelayData } from "../../interfaces";
3+
import { BigNumber, isUnsafeDepositId } from "../../utils";
4+
5+
type Provider = Rpc<SolanaRpcApi>;
6+
7+
/**
8+
* @param spokePool SpokePool Contract instance.
9+
* @param deposit V3Deopsit instance.
10+
* @param repaymentChainId Optional repaymentChainId (defaults to destinationChainId).
11+
* @returns An Ethers UnsignedTransaction instance.
12+
*/
13+
export function populateV3Relay(
14+
_spokePool: unknown,
15+
_deposit: Omit<Deposit, "messageHash">,
16+
_relayer: string,
17+
_repaymentChainId = _deposit.destinationChainId
18+
): Promise<unknown> {
19+
throw new Error("populateV3Relay: not implemented");
20+
}
21+
22+
/**
23+
* Retrieves the time from the SpokePool contract at a particular block.
24+
* @returns The time at the specified block tag.
25+
*/
26+
export function getTimeAt(_spokePool: unknown, _blockNumber: number): Promise<number> {
27+
throw new Error("getTimeAt: not implemented");
28+
}
29+
30+
/**
31+
* Retrieves the chain time at a particular block.
32+
* @note This should be the same as getTimeAt() but can differ in test. These two functions should be consolidated.
33+
* @returns The chain time at the specified block tag.
34+
*/
35+
export async function getTimestampForBlock(provider: Provider, blockNumber: number): Promise<number> {
36+
const block = await provider.getBlock(BigInt(blockNumber)).send();
37+
let timestamp: number;
38+
if (!block?.blockTime) {
39+
console.error(`Unable to resolve svm block ${blockNumber}`);
40+
timestamp = 0; // @todo: How to handle this?
41+
} else {
42+
timestamp = Number(block.blockTime); // Unix timestamps fit within number.
43+
}
44+
45+
return timestamp;
46+
}
47+
48+
/**
49+
* Return maximum of fill deadline buffer at start and end of block range.
50+
* @param spokePool SpokePool contract instance
51+
* @param startBlock start block
52+
* @param endBlock end block
53+
* @returns maximum of fill deadline buffer at start and end block
54+
*/
55+
export function getMaxFillDeadlineInRange(
56+
_spokePool: unknown,
57+
_startBlock: number,
58+
_endBlock: number
59+
): Promise<number> {
60+
throw new Error("getMaxFillDeadlineInRange: not implemented");
61+
}
62+
63+
/**
64+
* Finds the deposit id at a specific block number.
65+
* @param blockTag The block number to search for the deposit ID at.
66+
* @returns The deposit ID.
67+
*/
68+
export function getDepositIdAtBlock(_contract: unknown, _blockTag: number): Promise<BigNumber> {
69+
throw new Error("getDepositIdAtBlock: not implemented");
70+
}
71+
72+
/**
73+
* xxx todo
74+
*/
75+
export async function getSlotForBlock(
76+
provider: Provider,
77+
blockNumber: bigint,
78+
lowSlot: bigint,
79+
_highSlot?: bigint
80+
): Promise<bigint | undefined> {
81+
// @todo: Factor getBlock out to SlotFinder ??
82+
const getBlockNumber = async (slot: bigint): Promise<bigint> => {
83+
const block = await provider
84+
.getBlock(slot, { transactionDetails: "none", maxSupportedTransactionVersion: 0 })
85+
.send();
86+
return block?.blockHeight ?? BigInt(0); // @xxx Handle undefined here!
87+
};
88+
89+
let highSlot = _highSlot ?? (await provider.getSlot().send());
90+
const [blockLow = 0, blockHigh = 1_000_000_000] = await Promise.all([
91+
getBlockNumber(lowSlot),
92+
getBlockNumber(highSlot),
93+
]);
94+
95+
if (blockLow > blockNumber || blockHigh < blockNumber) {
96+
return undefined; // blockNumber did not occur within the specified block range.
97+
}
98+
99+
// Find the lowest slot number where blockHeight is greater than the requested blockNumber.
100+
do {
101+
const midSlot = (highSlot + lowSlot) / BigInt(2);
102+
const midBlock = await getBlockNumber(midSlot);
103+
104+
if (midBlock < blockNumber) {
105+
lowSlot = midSlot + BigInt(1);
106+
} else if (midBlock > blockNumber) {
107+
highSlot = midSlot + BigInt(1); // blockNumber occurred at or earlier than midBlock.
108+
} else {
109+
return midSlot;
110+
}
111+
} while (lowSlot <= highSlot);
112+
113+
return undefined;
114+
}
115+
116+
export function findDepositBlock(
117+
_spokePool: unknown,
118+
depositId: BigNumber,
119+
_lowBlock: number,
120+
_highBlock?: number
121+
): Promise<number | undefined> {
122+
// We can only perform this search when we have a safe deposit ID.
123+
if (isUnsafeDepositId(depositId)) {
124+
throw new Error(`Cannot binary search for depositId ${depositId}`);
125+
}
126+
throw new Error("findDepositBlock: not implemented");
127+
}
128+
129+
/**
130+
* Find the amount filled for a deposit at a particular block.
131+
* @param spokePool SpokePool contract instance.
132+
* @param relayData Deposit information that is used to complete a fill.
133+
* @param blockTag Block tag (numeric or "latest") to query at.
134+
* @returns The amount filled for the specified deposit at the requested block (or latest).
135+
*/
136+
export function relayFillStatus(
137+
_spokePool: unknown,
138+
_relayData: RelayData,
139+
_blockTag?: number | "latest",
140+
_destinationChainId?: number
141+
): Promise<FillStatus> {
142+
throw new Error("relayFillStatus: not implemented");
143+
}
144+
145+
export function fillStatusArray(
146+
_spokePool: unknown,
147+
_relayData: RelayData[],
148+
_blockTag = "processed"
149+
): Promise<(FillStatus | undefined)[]> {
150+
throw new Error("fillStatusArray: not implemented");
151+
}
152+
153+
/**
154+
* Find the block at which a fill was completed.
155+
* @todo After SpokePool upgrade, this function can be simplified to use the FillStatus enum.
156+
* @param spokePool SpokePool contract instance.
157+
* @param relayData Deposit information that is used to complete a fill.
158+
* @param lowBlockNumber The lower bound of the search. Must be bounded by SpokePool deployment.
159+
* @param highBlocknumber Optional upper bound for the search.
160+
* @returns The block number at which the relay was completed, or undefined.
161+
*/
162+
export function findFillBlock(
163+
_spokePool: unknown,
164+
_relayData: RelayData,
165+
_lowBlockNumber: number,
166+
_highBlockNumber?: number
167+
): Promise<number | undefined> {
168+
throw new Error("fillStatusArray: not implemented");
169+
}
170+
171+
export function findFillEvent(
172+
_spokePool: unknown,
173+
_relayData: RelayData,
174+
_lowBlockNumber: number,
175+
_highBlockNumber?: number
176+
): Promise<FillWithBlock | undefined> {
177+
throw new Error("fillStatusArray: not implemented");
178+
}

Diff for: src/arch/svm/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export const platform = "svm"; // Placeholder for actual exports.
1+
export * from "./SpokeUtils";

Diff for: src/clients/BundleDataClient/BundleDataClient.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -728,8 +728,7 @@ export class BundleDataClient {
728728
// hasn't queried. This is because this function will usually be called
729729
// in production with block ranges that were validated by
730730
// DataworkerUtils.blockRangesAreInvalidForSpokeClients.
731-
Math.min(queryBlock, spokePoolClients[deposit.destinationChainId].latestBlockSearched),
732-
deposit.destinationChainId
731+
Math.min(queryBlock, spokePoolClients[deposit.destinationChainId].latestBlockSearched)
733732
);
734733
};
735734

Diff for: src/clients/SpokePoolClient/EVMSpokePoolClient.ts

+10-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Contract, EventFilter } from "ethers";
22
import {
3+
fillStatusArray,
34
findDepositBlock,
45
getMaxFillDeadlineInRange as getMaxFillDeadline,
56
getTimeAt as _getTimeAt,
@@ -42,12 +43,15 @@ export class EVMSpokePoolClient extends SpokePoolClient {
4243
super(logger, hubPoolClient, chainId, deploymentBlock, eventSearchConfig);
4344
}
4445

45-
public override relayFillStatus(
46-
relayData: RelayData,
47-
blockTag?: number | "latest",
48-
destinationChainId?: number
49-
): Promise<FillStatus> {
50-
return relayFillStatus(this.spokePool, relayData, blockTag, destinationChainId);
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);
5155
}
5256

5357
public override getMaxFillDeadlineInRange(startBlock: number, endBlock: number): Promise<number> {

Diff for: src/clients/SpokePoolClient/SpokePoolClient.ts

+13-5
Original file line numberDiff line numberDiff line change
@@ -810,11 +810,19 @@ export abstract class SpokePoolClient extends BaseAbstractClient {
810810
/**
811811
* Retrieves the fill status for a given relay data.
812812
* @param relayData The relay data to retrieve the fill status for.
813+
* @param blockTag The block at which to query the fill status.
813814
* @returns The fill status for the given relay data.
814815
*/
815-
public abstract relayFillStatus(
816-
relayData: RelayData,
817-
blockTag?: number | "latest",
818-
destinationChainId?: number
819-
): Promise<FillStatus>;
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)[]>;
820828
}

Diff for: src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export * as arch from "./arch";
12
export * as addressAggregator from "./addressAggregator";
23
export * as lpFeeCalculator from "./lpFeeCalculator";
34
export * as pool from "./pool";

0 commit comments

Comments
 (0)