Skip to content

feat(epic): add SVM client #985

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 15 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 176 additions & 0 deletions src/arch/svm/SpokeUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import { Rpc, SolanaRpcApi, Address } from "@solana/kit";

import { Deposit, FillStatus, FillWithBlock, RelayData } from "../../interfaces";
import { BigNumber, isUnsafeDepositId } from "../../utils";
import { fetchState } from "@across-protocol/contracts/dist/src/svm/clients/SvmSpoke";

type Provider = Rpc<SolanaRpcApi>;

/**
* @param spokePool SpokePool Contract instance.
* @param deposit V3Deopsit instance.
* @param repaymentChainId Optional repaymentChainId (defaults to destinationChainId).
* @returns An Ethers UnsignedTransaction instance.
*/
export function populateV3Relay(
_spokePool: unknown,
_deposit: Omit<Deposit, "messageHash">,
_relayer: string,
_repaymentChainId = _deposit.destinationChainId
): Promise<unknown> {
throw new Error("populateV3Relay: not implemented");
}

/**
* Retrieves the time from the SpokePool contract at a particular block.
* @returns The time at the specified block tag.
*/
export function getTimeAt(_spokePool: unknown, _blockNumber: number): Promise<number> {
throw new Error("getTimeAt: not implemented");
}

/**
* Retrieves the chain time at a particular block.
* @note This should be the same as getTimeAt() but can differ in test. These two functions should be consolidated.
* @returns The chain time at the specified block tag.
*/
export async function getTimestampForBlock(provider: Provider, blockNumber: number): Promise<number> {
const block = await provider.getBlock(BigInt(blockNumber)).send();
let timestamp: number;
if (!block?.blockTime) {
console.error(`Unable to resolve svm block ${blockNumber}`);
timestamp = 0; // @todo: How to handle this?
} else {
timestamp = Number(block.blockTime); // Unix timestamps fit within number.
}

return timestamp;
}

/**
* Returns the current fill deadline buffer.
* @param provider SVM Provider instance
* @param statePda Spoke Pool's State PDA
* @returns fill deadline buffer
*/
export async function getFillDeadline(provider: Provider, statePda: Address): Promise<number> {
const state = await fetchState(provider, statePda);
return state.data.fillDeadlineBuffer;
}

/**
* Finds the deposit id at a specific block number.
* @param blockTag The block number to search for the deposit ID at.
* @returns The deposit ID.
*/
export function getDepositIdAtBlock(_contract: unknown, _blockTag: number): Promise<BigNumber> {
throw new Error("getDepositIdAtBlock: not implemented");
}

/**
* xxx todo
*/
export async function getSlotForBlock(
provider: Provider,
blockNumber: bigint,
lowSlot: bigint,
_highSlot?: bigint
): Promise<bigint | undefined> {
// @todo: Factor getBlock out to SlotFinder ??
const getBlockNumber = async (slot: bigint): Promise<bigint> => {
const block = await provider
.getBlock(slot, { transactionDetails: "none", maxSupportedTransactionVersion: 0 })
.send();
return block?.blockHeight ?? BigInt(0); // @xxx Handle undefined here!
};

let highSlot = _highSlot ?? (await provider.getSlot().send());
const [blockLow = 0, blockHigh = 1_000_000_000] = await Promise.all([
getBlockNumber(lowSlot),
getBlockNumber(highSlot),
]);

if (blockLow > blockNumber || blockHigh < blockNumber) {
return undefined; // blockNumber did not occur within the specified block range.
}

// Find the lowest slot number where blockHeight is greater than the requested blockNumber.
do {
const midSlot = (highSlot + lowSlot) / BigInt(2);
const midBlock = await getBlockNumber(midSlot);

if (midBlock < blockNumber) {
lowSlot = midSlot + BigInt(1);
} else if (midBlock > blockNumber) {
highSlot = midSlot + BigInt(1); // blockNumber occurred at or earlier than midBlock.
} else {
return midSlot;
}
} while (lowSlot <= highSlot);

return undefined;
}

export function findDepositBlock(
_spokePool: unknown,
depositId: BigNumber,
_lowBlock: number,
_highBlock?: number
): Promise<number | undefined> {
// We can only perform this search when we have a safe deposit ID.
if (isUnsafeDepositId(depositId)) {
throw new Error(`Cannot binary search for depositId ${depositId}`);
}
throw new Error("findDepositBlock: not implemented");
}

/**
* Find the amount filled for a deposit at a particular block.
* @param spokePool SpokePool contract instance.
* @param relayData Deposit information that is used to complete a fill.
* @param blockTag Block tag (numeric or "latest") to query at.
* @returns The amount filled for the specified deposit at the requested block (or latest).
*/
export function relayFillStatus(
_spokePool: unknown,
_relayData: RelayData,
_blockTag?: number | "latest",
_destinationChainId?: number
): Promise<FillStatus> {
throw new Error("relayFillStatus: not implemented");
}

export function fillStatusArray(
_spokePool: unknown,
_relayData: RelayData[],
_blockTag = "processed"
): Promise<(FillStatus | undefined)[]> {
throw new Error("fillStatusArray: not implemented");
}

/**
* Find the block at which a fill was completed.
* @todo After SpokePool upgrade, this function can be simplified to use the FillStatus enum.
* @param spokePool SpokePool contract instance.
* @param relayData Deposit information that is used to complete a fill.
* @param lowBlockNumber The lower bound of the search. Must be bounded by SpokePool deployment.
* @param highBlocknumber Optional upper bound for the search.
* @returns The block number at which the relay was completed, or undefined.
*/
export function findFillBlock(
_spokePool: unknown,
_relayData: RelayData,
_lowBlockNumber: number,
_highBlockNumber?: number
): Promise<number | undefined> {
throw new Error("fillStatusArray: not implemented");
Copy link
Preview

Copilot AI Apr 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function findFillBlock throws an error with the message 'fillStatusArray: not implemented' which appears to be a copy-paste error. Please update the error message to accurately reflect the function name, e.g., 'findFillBlock: not implemented'.

Suggested change
throw new Error("fillStatusArray: not implemented");
throw new Error("findFillBlock: not implemented");

Copilot uses AI. Check for mistakes.

}

export function findFillEvent(
_spokePool: unknown,
_relayData: RelayData,
_lowBlockNumber: number,
_highBlockNumber?: number
): Promise<FillWithBlock | undefined> {
throw new Error("fillStatusArray: not implemented");
}
Loading