Skip to content

feat(svmSpokeUtils): get fill deadline buffer implementation #978

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

Merged
merged 5 commits into from
Apr 23, 2025
Merged
Show file tree
Hide file tree
Changes from 4 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
22 changes: 10 additions & 12 deletions src/arch/svm/SpokeUtils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Rpc, SolanaRpcApi } from "@solana/kit";
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>;

Expand Down Expand Up @@ -46,18 +48,14 @@ export async function getTimestampForBlock(provider: Provider, blockNumber: numb
}

/**
* Return maximum of fill deadline buffer at start and end of block range.
* @param spokePool SpokePool contract instance
* @param startBlock start block
* @param endBlock end block
* @returns maximum of fill deadline buffer at start and end block
* Returns the current fill deadline buffer.
* @param provider SVM Provider instance
* @param statePda Spoke Pool's State PDA
* @returns fill deadline buffer
*/
export function getMaxFillDeadlineInRange(
_spokePool: unknown,
_startBlock: number,
_endBlock: number
): Promise<number> {
throw new Error("getMaxFillDeadlineInRange: not implemented");
export async function getFillDeadline(provider: Provider, statePda: Address): Promise<number> {
const state = await fetchState(provider, statePda);
return state.data.fillDeadlineBuffer;
}

/**
Expand Down
4 changes: 4 additions & 0 deletions src/arch/svm/eventsClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,4 +209,8 @@ export class SvmSpokeEventsClient {

return events;
}

public getSvmSpokeAddress(): Address {
Copy link
Member

Choose a reason for hiding this comment

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

+1

return this.svmSpokeAddress;
}
}
19 changes: 18 additions & 1 deletion src/arch/svm/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { BN, BorshEventCoder, Idl } from "@coral-xyz/anchor";
import web3, { address, RpcTransport } from "@solana/kit";
import web3, { address, getProgramDerivedAddress, getU64Encoder, Address, RpcTransport } from "@solana/kit";

import { EventName, EventData, SVMEventNames } from "./types";

/**
Expand Down Expand Up @@ -64,3 +65,19 @@ export function getEventName(rawName: string): EventName {
if (Object.values(SVMEventNames).some((name) => rawName.includes(name))) return rawName as EventName;
throw new Error(`Unknown event name: ${rawName}`);
}

/**
* Returns the PDA for the State account.
* @param programId The SpokePool program ID.
* @param extraSeed An optional extra seed. Defaults to 0.
* @returns The PDA for the State account.
*/
export async function getStatePda(programId: Address, extraSeed = 0): Promise<Address> {
Copy link
Member

Choose a reason for hiding this comment

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

OOC how often is extraSeed set? I think it makes sense to have but if we don't need to use it it may be extra unneeded params

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think for state pda it'll always be zero. I removed it here: d37346b

const seedEncoder = getU64Encoder();
const encodedExtraSeed = seedEncoder.encode(extraSeed);
const [statePda] = await getProgramDerivedAddress({
programAddress: programId,
seeds: ["state", encodedExtraSeed],
});
return statePda;
}
30 changes: 22 additions & 8 deletions src/clients/SpokePoolClient/SVMSpokePoolClient.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import winston from "winston";
import { Rpc, SolanaRpcApiFromTransport, RpcTransport } from "@solana/kit";
import { Address, Rpc, SolanaRpcApiFromTransport, RpcTransport } from "@solana/kit";

import { BigNumber, DepositSearchResult, EventSearchConfig, MakeOptional } from "../../utils";
import { SvmSpokeEventsClient, SVMEventNames } from "../../arch/svm";
import {
SvmSpokeEventsClient,
SVMEventNames,
getFillDeadline,
getTimestampForBlock,
getStatePda,
} from "../../arch/svm";
import { HubPoolClient } from "../HubPoolClient";
import { knownEventNames, SpokePoolClient, SpokePoolUpdate } from "./SpokePoolClient";
import { RelayData, FillStatus } from "../../interfaces";

/**
* SvmSpokePoolClient is a client for the SVM SpokePool program. It extends the base SpokePoolClient
* and implements the abstract methods required for interacting with an SVM Spoke Pool.
Expand All @@ -19,6 +27,8 @@ export class SvmSpokePoolClient extends SpokePoolClient {
chainId: number,
deploymentSlot: bigint, // Using slot instead of block number for SVM
eventSearchConfig: MakeOptional<EventSearchConfig, "toBlock">,
protected programId: Address,
protected statePda: Address,
protected svmEventsClient: SvmSpokeEventsClient,
protected rpc: Rpc<SolanaRpcApiFromTransport<RpcTransport>>
) {
Expand All @@ -38,12 +48,16 @@ export class SvmSpokePoolClient extends SpokePoolClient {
rpc: Rpc<SolanaRpcApiFromTransport<RpcTransport>>
): Promise<SvmSpokePoolClient> {
const svmEventsClient = await SvmSpokeEventsClient.create(rpc);
const programId = svmEventsClient.getSvmSpokeAddress();
const statePda = await getStatePda(programId);
return new SvmSpokePoolClient(
logger,
hubPoolClient,
chainId,
deploymentSlot,
eventSearchConfig,
programId,
statePda,
svmEventsClient,
rpc
);
Expand Down Expand Up @@ -150,18 +164,18 @@ export class SvmSpokePoolClient extends SpokePoolClient {
}

/**
* Retrieves the maximum fill deadline buffer.
* TODO: Implement SVM equivalent, perhaps reading from a config account.
* Retrieves the fill deadline buffer fetched from the State PDA.
* @note This function assumes that fill deadline buffer is a constant value in svm environments.
*/
public getMaxFillDeadlineInRange(_startSlot: number, _endSlot: number): Promise<number> {
throw new Error("getMaxFillDeadlineInRange not implemented for SVM");
public override getMaxFillDeadlineInRange(_startSlot: number, _endSlot: number): Promise<number> {
return getFillDeadline(this.rpc, this.statePda);
}

/**
* Retrieves the timestamp for a given SVM slot number.
*/
public getTimestampForBlock(_blockNumber: number): Promise<number> {
throw new Error("getTimestampForBlock not implemented for SVM");
public override getTimestampForBlock(blockNumber: number): Promise<number> {
return getTimestampForBlock(this.rpc, blockNumber);
}

/**
Expand Down