From 83123b56e4f7b1a5f458146e79cbd28b6aca462f Mon Sep 17 00:00:00 2001 From: Aleksa Colovic Date: Wed, 30 Jul 2025 10:47:03 +0200 Subject: [PATCH 1/6] improve: Create relayDataHash with messageHash instead of message --- src/arch/svm/SpokeUtils.ts | 35 ++++++++++++++++++++++++++++------- src/utils/SpokeUtils.ts | 6 +++++- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/arch/svm/SpokeUtils.ts b/src/arch/svm/SpokeUtils.ts index b332991d6..ef916a69a 100644 --- a/src/arch/svm/SpokeUtils.ts +++ b/src/arch/svm/SpokeUtils.ts @@ -1,6 +1,6 @@ import { MessageTransmitterClient, SvmSpokeClient, TokenMessengerMinterClient } from "@across-protocol/contracts"; import { decodeFillStatusAccount, fetchState } from "@across-protocol/contracts/dist/src/svm/clients/SvmSpoke"; -import { decodeMessageHeader, hashNonEmptyMessage } from "@across-protocol/contracts/dist/src/svm/web3-v1"; +import { decodeMessageHeader } from "@across-protocol/contracts/dist/src/svm/web3-v1"; import { intToU8Array32 } from "@across-protocol/contracts/dist/src/svm/web3-v1/conversionUtils"; import { SYSTEM_PROGRAM_ADDRESS } from "@solana-program/system"; import { @@ -47,6 +47,7 @@ import { chainIsProd, chainIsSvm, chunk, + getMessageHash, isUnsafeDepositId, keccak256, mapAsync, @@ -460,7 +461,12 @@ export async function fillRelayInstruction( `Invalid repayment address for chain ${repaymentChainId}: ${repaymentAddress.toNative()}.` ); - const _relayDataHash = getRelayDataHash(relayData, relayData.destinationChainId); + const relayDataWithMessageHash = { + ...relayData, + messageHash: getMessageHash(relayData.message), + }; + + const _relayDataHash = getRelayDataHash(relayDataWithMessageHash, relayData.destinationChainId); const relayDataHash = new Uint8Array(Buffer.from(_relayDataHash.slice(2), "hex")); const relayer = SvmAddress.from(signer.address); @@ -568,7 +574,13 @@ export async function getFillRelayTx( ); const program = toAddress(spokePoolAddr); - const _relayDataHash = getRelayDataHash(relayData, destinationChainId); + + const relayDataWithMessageHash = { + ...relayData, + messageHash: getMessageHash(relayData.message), + }; + + const _relayDataHash = getRelayDataHash(relayDataWithMessageHash, destinationChainId); const relayDataHash = new Uint8Array(Buffer.from(_relayDataHash.slice(2), "hex")); const [state, delegate] = await Promise.all([ @@ -777,7 +789,13 @@ export async function getSlowFillRequestTx( } = relayData; const program = toAddress(spokePoolAddr); - const relayDataHash = getRelayDataHash(relayData, destinationChainId); + + const relayDataWithMessageHash = { + ...relayData, + messageHash: getMessageHash(relayData.message), + }; + + const relayDataHash = getRelayDataHash(relayDataWithMessageHash, destinationChainId); const [state, fillStatus, eventAuthority] = await Promise.all([ getStatePda(program), @@ -866,12 +884,15 @@ export async function getAssociatedTokenAddress( return associatedToken; } -export function getRelayDataHash(relayData: RelayData, destinationChainId: number): string { +export function getRelayDataHash( + relayData: Omit & { messageHash: string }, + destinationChainId: number +): string { const addressEncoder = getAddressEncoder(); const uint64Encoder = getU64Encoder(); const uint32Encoder = getU32Encoder(); - assert(relayData.message.startsWith("0x"), "Message must be a hex string"); + assert(relayData.messageHash.startsWith("0x"), "Message hash must be a hex string"); const encodeAddress = (data: SdkAddress) => Uint8Array.from(addressEncoder.encode(toAddress(data))); const contentToHash = Buffer.concat([ @@ -886,7 +907,7 @@ export function getRelayDataHash(relayData: RelayData, destinationChainId: numbe arrayify(hexZeroPad(hexlify(relayData.depositId), 32)), Uint8Array.from(uint32Encoder.encode(relayData.fillDeadline)), Uint8Array.from(uint32Encoder.encode(relayData.exclusivityDeadline)), - hashNonEmptyMessage(Buffer.from(arrayify(relayData.message))), + Uint8Array.from(Buffer.from(relayData.messageHash.slice(2), "hex")), Uint8Array.from(uint64Encoder.encode(BigInt(destinationChainId))), ]); return keccak256(contentToHash); diff --git a/src/utils/SpokeUtils.ts b/src/utils/SpokeUtils.ts index 9a6ad64b9..1f4e1f074 100644 --- a/src/utils/SpokeUtils.ts +++ b/src/utils/SpokeUtils.ts @@ -52,7 +52,11 @@ export function getRelayDataHash(relayData: RelayData, destinationChainId: numbe exclusiveRelayer: relayData.exclusiveRelayer.toBytes32(), }; if (chainIsSvm(destinationChainId)) { - return svm.getRelayDataHash(relayData, destinationChainId); + const relayDataWithMessageHash = { + ...relayData, + messageHash: getMessageHash(relayData.message), + }; + return svm.getRelayDataHash(relayDataWithMessageHash, destinationChainId); } return keccak256(encodeAbiParameters(abi, [_relayData, destinationChainId])); } From a8ff1be6e5daf472e0c074187aaf2b95140e0907 Mon Sep 17 00:00:00 2001 From: Aleksa Colovic Date: Wed, 30 Jul 2025 10:48:19 +0200 Subject: [PATCH 2/6] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 110524195..44534e655 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@across-protocol/sdk", "author": "UMA Team", - "version": "4.3.29", + "version": "4.3.30", "license": "AGPL-3.0", "homepage": "https://docs.across.to/reference/sdk", "files": [ From db43cb54de99beb0ae50fa3b28e1a61fb29f18a1 Mon Sep 17 00:00:00 2001 From: Aleksa Colovic Date: Wed, 30 Jul 2025 13:32:51 +0200 Subject: [PATCH 3/6] Add messageHash params --- src/arch/svm/SpokeUtils.ts | 6 ++++-- src/arch/svm/utils.ts | 6 ++++-- src/clients/SpokePoolClient/SVMSpokePoolClient.ts | 4 ++-- src/utils/SpokeUtils.ts | 5 +++-- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/arch/svm/SpokeUtils.ts b/src/arch/svm/SpokeUtils.ts index ef916a69a..044382201 100644 --- a/src/arch/svm/SpokeUtils.ts +++ b/src/arch/svm/SpokeUtils.ts @@ -243,6 +243,7 @@ export async function findDeposit( * @param provider - SVM provider instance. * @param svmEventsClient - SVM events client for querying events. * @param atHeight - (Optional) Specific slot number to query. Defaults to the latest confirmed slot. + * @param messageHash - (Optional) Hash of the message that will be used to create relayDataHash from fill. * @returns The fill status for the deposit at the specified or current slot. */ export async function relayFillStatus( @@ -250,12 +251,13 @@ export async function relayFillStatus( relayData: RelayData, destinationChainId: number, svmEventsClient: SvmCpiEventsClient, - atHeight?: number + atHeight?: number, + messageHash?: string ): Promise { assert(chainIsSvm(destinationChainId), "Destination chain must be an SVM chain"); const provider = svmEventsClient.getRpc(); // Get fill status PDA using relayData - const fillStatusPda = await getFillStatusPda(programId, relayData, destinationChainId); + const fillStatusPda = await getFillStatusPda(programId, relayData, destinationChainId, messageHash); let toSlot = BigInt(atHeight ?? 0); // If no specific slot is requested, try fetching the current status from the PDA diff --git a/src/arch/svm/utils.ts b/src/arch/svm/utils.ts index d6c556747..06f2c70dc 100644 --- a/src/arch/svm/utils.ts +++ b/src/arch/svm/utils.ts @@ -259,14 +259,16 @@ export async function getStatePda(programId: Address): Promise
{ * @param programId The SpokePool program ID. * @param relayData The relay data to get the fill status PDA for. * @param destinationChainId The destination chain ID. + * @param messageHash Hash of the message that will be used to create relayDataHash from fill. * @returns The PDA for the fill status. */ export async function getFillStatusPda( programId: Address, relayData: RelayData, - destinationChainId: number + destinationChainId: number, + messageHash?: string ): Promise
{ - const relayDataHash = getRelayDataHash(relayData, destinationChainId); + const relayDataHash = getRelayDataHash(relayData, destinationChainId, messageHash); const uint8RelayDataHash = new Uint8Array(Buffer.from(relayDataHash.slice(2), "hex")); const [fillStatusPda] = await getProgramDerivedAddress({ programAddress: programId, diff --git a/src/clients/SpokePoolClient/SVMSpokePoolClient.ts b/src/clients/SpokePoolClient/SVMSpokePoolClient.ts index aaa6b9079..df42d1900 100644 --- a/src/clients/SpokePoolClient/SVMSpokePoolClient.ts +++ b/src/clients/SpokePoolClient/SVMSpokePoolClient.ts @@ -243,8 +243,8 @@ export class SVMSpokePoolClient extends SpokePoolClient { /** * Retrieves the fill status for a given relay data from the SVM chain. */ - public override relayFillStatus(relayData: RelayData, atHeight?: number): Promise { - return relayFillStatus(this.programId, relayData, this.chainId, this.svmEventsClient, atHeight); + public override relayFillStatus(relayData: RelayData, atHeight?: number, messageHash?: string): Promise { + return relayFillStatus(this.programId, relayData, this.chainId, this.svmEventsClient, atHeight, messageHash); } /** diff --git a/src/utils/SpokeUtils.ts b/src/utils/SpokeUtils.ts index 1f4e1f074..489797d0d 100644 --- a/src/utils/SpokeUtils.ts +++ b/src/utils/SpokeUtils.ts @@ -19,9 +19,10 @@ export function getSlowFillLeafLpFeePct(leaf: SlowFillLeaf): BigNumber { * Compute the RelayData hash for a fill. This can be used to determine the fill status. * @param relayData RelayData information that is used to complete a fill. * @param destinationChainId Supplementary destination chain ID required by V3 hashes. + * @param messageHash Hash of the message that will be used to create relayDataHash from fill. * @returns The corresponding RelayData hash. */ -export function getRelayDataHash(relayData: RelayData, destinationChainId: number): string { +export function getRelayDataHash(relayData: RelayData, destinationChainId: number, messageHash?: string): string { const abi = [ { type: "tuple", @@ -54,7 +55,7 @@ export function getRelayDataHash(relayData: RelayData, destinationChainId: numbe if (chainIsSvm(destinationChainId)) { const relayDataWithMessageHash = { ...relayData, - messageHash: getMessageHash(relayData.message), + messageHash: messageHash ?? getMessageHash(relayData.message), }; return svm.getRelayDataHash(relayDataWithMessageHash, destinationChainId); } From 0076e38a0c3546c553cd4b7d4aad6abc0f5e730d Mon Sep 17 00:00:00 2001 From: Aleksa Colovic Date: Tue, 5 Aug 2025 11:40:58 +0200 Subject: [PATCH 4/6] Require messgeHash in the RelayData --- src/arch/evm/SpokeUtils.ts | 9 ++-- src/arch/svm/SpokeUtils.ts | 53 +++++++------------ src/arch/svm/utils.ts | 9 ++-- .../SpokePoolClient/EVMSpokePoolClient.ts | 9 ++-- .../SpokePoolClient/SVMSpokePoolClient.ts | 8 +-- .../SpokePoolClient/SpokePoolClient.ts | 8 ++- src/interfaces/SpokePool.ts | 4 ++ .../chain-queries/baseQuery.ts | 6 +-- .../chain-queries/svmQuery.ts | 8 +-- src/relayFeeCalculator/relayFeeCalculator.ts | 13 +++-- src/utils/DepositUtils.ts | 3 +- src/utils/SpokeUtils.ts | 12 ++--- test/SVMSpokePoolClient.fills.ts | 4 +- test/utils/svm/utils.ts | 6 ++- 14 files changed, 77 insertions(+), 75 deletions(-) diff --git a/src/arch/evm/SpokeUtils.ts b/src/arch/evm/SpokeUtils.ts index 5559f9704..d95df4abc 100644 --- a/src/arch/evm/SpokeUtils.ts +++ b/src/arch/evm/SpokeUtils.ts @@ -6,6 +6,7 @@ import { FillStatus, FillWithBlock, RelayData, + RelayDataWithMessageHash, RelayExecutionEventInfo, SpeedUpCommon, } from "../../interfaces"; @@ -190,7 +191,7 @@ export async function findDepositBlock( */ export async function relayFillStatus( spokePool: Contract, - relayData: RelayData, + relayData: RelayDataWithMessageHash, blockTag: BlockTag = "latest", destinationChainId?: number ): Promise { @@ -213,7 +214,7 @@ export async function relayFillStatus( export async function fillStatusArray( spokePool: Contract, - relayData: RelayData[], + relayData: RelayDataWithMessageHash[], blockTag: BlockTag = "latest" ): Promise<(FillStatus | undefined)[]> { const fillStatuses = "fillStatuses"; @@ -258,7 +259,7 @@ export async function fillStatusArray( */ export async function findFillBlock( spokePool: Contract, - relayData: RelayData, + relayData: RelayDataWithMessageHash, lowBlockNumber: number, highBlockNumber?: number ): Promise { @@ -313,7 +314,7 @@ export async function findFillBlock( export async function findFillEvent( spokePool: Contract, - relayData: RelayData, + relayData: RelayDataWithMessageHash, lowBlockNumber: number, highBlockNumber?: number ): Promise { diff --git a/src/arch/svm/SpokeUtils.ts b/src/arch/svm/SpokeUtils.ts index 8adc618f7..98a9c049c 100644 --- a/src/arch/svm/SpokeUtils.ts +++ b/src/arch/svm/SpokeUtils.ts @@ -37,7 +37,14 @@ import assert from "assert"; import { arrayify, hexZeroPad, hexlify } from "ethers/lib/utils"; import { Logger } from "winston"; import { CHAIN_IDs, TOKEN_SYMBOLS_MAP } from "../../constants"; -import { DepositWithBlock, FillStatus, FillWithBlock, RelayData, RelayExecutionEventInfo } from "../../interfaces"; +import { + DepositWithBlock, + FillStatus, + FillWithBlock, + RelayData, + RelayDataWithMessageHash, + RelayExecutionEventInfo, +} from "../../interfaces"; import { BigNumber, EvmAddress, @@ -47,7 +54,6 @@ import { chainIsProd, chainIsSvm, chunk, - getMessageHash, delay, isUnsafeDepositId, keccak256, @@ -84,7 +90,7 @@ import { */ export const SLOT_DURATION_MS = 400; -type ProtoFill = Omit & { +type ProtoFill = Omit & { destinationChainId: number; recipient: SvmAddress; outputToken: SvmAddress; @@ -263,16 +269,15 @@ export async function findDeposit( */ export async function relayFillStatus( programId: Address, - relayData: RelayData, + relayData: RelayDataWithMessageHash, destinationChainId: number, svmEventsClient: SvmCpiEventsClient, - atHeight?: number, - messageHash?: string + atHeight?: number ): Promise { assert(chainIsSvm(destinationChainId), "Destination chain must be an SVM chain"); const provider = svmEventsClient.getRpc(); // Get fill status PDA using relayData - const fillStatusPda = await getFillStatusPda(programId, relayData, destinationChainId, messageHash); + const fillStatusPda = await getFillStatusPda(programId, relayData, destinationChainId); let toSlot = BigInt(atHeight ?? 0); // If no specific slot is requested, try fetching the current status from the PDA @@ -314,7 +319,7 @@ export async function relayFillStatus( */ export async function fillStatusArray( programId: Address, - relayData: RelayData[], + relayData: RelayDataWithMessageHash[], destinationChainId: number, svmEventsClient: SvmCpiEventsClient, atHeight?: number, @@ -397,7 +402,7 @@ export async function fillStatusArray( * @returns The fill event with block info, or `undefined` if not found. */ export async function findFillEvent( - relayData: RelayData, + relayData: RelayDataWithMessageHash, destinationChainId: number, svmEventsClient: SvmCpiEventsClient, fromSlot: number, @@ -478,12 +483,7 @@ export async function fillRelayInstruction( `Invalid repayment address for chain ${repaymentChainId}: ${repaymentAddress.toNative()}.` ); - const relayDataWithMessageHash = { - ...relayData, - messageHash: getMessageHash(relayData.message), - }; - - const _relayDataHash = getRelayDataHash(relayDataWithMessageHash, relayData.destinationChainId); + const _relayDataHash = getRelayDataHash(relayData, relayData.destinationChainId); const relayDataHash = new Uint8Array(Buffer.from(_relayDataHash.slice(2), "hex")); const relayer = SvmAddress.from(signer.address); @@ -574,7 +574,7 @@ export function createTokenAccountsInstruction( export async function getFillRelayTx( spokePoolAddr: SvmAddress, solanaClient: SVMProvider, - relayData: Omit & { + relayData: Omit & { destinationChainId: number; recipient: SvmAddress; outputToken: SvmAddress; @@ -592,12 +592,7 @@ export async function getFillRelayTx( const program = toAddress(spokePoolAddr); - const relayDataWithMessageHash = { - ...relayData, - messageHash: getMessageHash(relayData.message), - }; - - const _relayDataHash = getRelayDataHash(relayDataWithMessageHash, destinationChainId); + const _relayDataHash = getRelayDataHash(relayData, destinationChainId); const relayDataHash = new Uint8Array(Buffer.from(_relayDataHash.slice(2), "hex")); const [state, delegate] = await Promise.all([ @@ -784,7 +779,7 @@ export const createRequestSlowFillInstruction = async ( export async function getSlowFillRequestTx( spokePoolAddr: SvmAddress, solanaClient: SVMProvider, - relayData: Omit & { + relayData: Omit & { destinationChainId: number; recipient: SvmAddress; outputToken: SvmAddress; @@ -807,12 +802,7 @@ export async function getSlowFillRequestTx( const program = toAddress(spokePoolAddr); - const relayDataWithMessageHash = { - ...relayData, - messageHash: getMessageHash(relayData.message), - }; - - const relayDataHash = getRelayDataHash(relayDataWithMessageHash, destinationChainId); + const relayDataHash = getRelayDataHash(relayData, destinationChainId); const [state, fillStatus, eventAuthority] = await Promise.all([ getStatePda(program), @@ -901,10 +891,7 @@ export async function getAssociatedTokenAddress( return associatedToken; } -export function getRelayDataHash( - relayData: Omit & { messageHash: string }, - destinationChainId: number -): string { +export function getRelayDataHash(relayData: RelayDataWithMessageHash, destinationChainId: number): string { const addressEncoder = getAddressEncoder(); const uint64Encoder = getU64Encoder(); const uint32Encoder = getU32Encoder(); diff --git a/src/arch/svm/utils.ts b/src/arch/svm/utils.ts index 06f2c70dc..c6681acc8 100644 --- a/src/arch/svm/utils.ts +++ b/src/arch/svm/utils.ts @@ -23,7 +23,7 @@ import { import assert from "assert"; import bs58 from "bs58"; import { ethers } from "ethers"; -import { FillType, RelayData } from "../../interfaces"; +import { FillType, RelayDataWithMessageHash } from "../../interfaces"; import { BigNumber, Address as SdkAddress, biMin, getRelayDataHash, isDefined, isUint8Array } from "../../utils"; import { getTimestampForSlot } from "./SpokeUtils"; import { AttestedCCTPMessage, EventName, SVMEventNames, SVMProvider } from "./types"; @@ -264,11 +264,10 @@ export async function getStatePda(programId: Address): Promise
{ */ export async function getFillStatusPda( programId: Address, - relayData: RelayData, - destinationChainId: number, - messageHash?: string + relayData: RelayDataWithMessageHash, + destinationChainId: number ): Promise
{ - const relayDataHash = getRelayDataHash(relayData, destinationChainId, messageHash); + const relayDataHash = getRelayDataHash(relayData, destinationChainId); const uint8RelayDataHash = new Uint8Array(Buffer.from(relayDataHash.slice(2), "hex")); const [fillStatusPda] = await getProgramDerivedAddress({ programAddress: programId, diff --git a/src/clients/SpokePoolClient/EVMSpokePoolClient.ts b/src/clients/SpokePoolClient/EVMSpokePoolClient.ts index 2fae6b377..3c9ff70ac 100644 --- a/src/clients/SpokePoolClient/EVMSpokePoolClient.ts +++ b/src/clients/SpokePoolClient/EVMSpokePoolClient.ts @@ -7,7 +7,7 @@ import { relayFillStatus, getTimestampForBlock as _getTimestampForBlock, } from "../../arch/evm"; -import { DepositWithBlock, FillStatus, RelayData } from "../../interfaces"; +import { DepositWithBlock, FillStatus, RelayDataWithMessageHash } from "../../interfaces"; import { BigNumber, DepositSearchResult, @@ -48,11 +48,14 @@ export class EVMSpokePoolClient extends SpokePoolClient { this.spokePoolAddress = EvmAddress.from(spokePool.address); } - public override relayFillStatus(relayData: RelayData, atHeight?: number): Promise { + public override relayFillStatus(relayData: RelayDataWithMessageHash, atHeight?: number): Promise { return relayFillStatus(this.spokePool, relayData, atHeight, this.chainId); } - public override fillStatusArray(relayData: RelayData[], atHeight?: number): Promise<(FillStatus | undefined)[]> { + public override fillStatusArray( + relayData: RelayDataWithMessageHash[], + atHeight?: number + ): Promise<(FillStatus | undefined)[]> { return fillStatusArray(this.spokePool, relayData, atHeight); } diff --git a/src/clients/SpokePoolClient/SVMSpokePoolClient.ts b/src/clients/SpokePoolClient/SVMSpokePoolClient.ts index df42d1900..473e448b3 100644 --- a/src/clients/SpokePoolClient/SVMSpokePoolClient.ts +++ b/src/clients/SpokePoolClient/SVMSpokePoolClient.ts @@ -11,7 +11,7 @@ import { relayFillStatus, fillStatusArray, } from "../../arch/svm"; -import { FillStatus, RelayData, SortableEvent } from "../../interfaces"; +import { FillStatus, RelayDataWithMessageHash, SortableEvent } from "../../interfaces"; import { BigNumber, DepositSearchResult, @@ -243,8 +243,8 @@ export class SVMSpokePoolClient extends SpokePoolClient { /** * Retrieves the fill status for a given relay data from the SVM chain. */ - public override relayFillStatus(relayData: RelayData, atHeight?: number, messageHash?: string): Promise { - return relayFillStatus(this.programId, relayData, this.chainId, this.svmEventsClient, atHeight, messageHash); + public override relayFillStatus(relayData: RelayDataWithMessageHash, atHeight?: number): Promise { + return relayFillStatus(this.programId, relayData, this.chainId, this.svmEventsClient, atHeight); } /** @@ -254,7 +254,7 @@ export class SVMSpokePoolClient extends SpokePoolClient { * @returns The fill status for each of the given relay data. */ public fillStatusArray( - relayData: RelayData[], + relayData: RelayDataWithMessageHash[], atHeight?: number, destinationChainId?: number ): Promise<(FillStatus | undefined)[]> { diff --git a/src/clients/SpokePoolClient/SpokePoolClient.ts b/src/clients/SpokePoolClient/SpokePoolClient.ts index 792076371..e85901d58 100644 --- a/src/clients/SpokePoolClient/SpokePoolClient.ts +++ b/src/clients/SpokePoolClient/SpokePoolClient.ts @@ -40,6 +40,7 @@ import { SpeedUpWithBlock, TokensBridged, RelayExecutionEventInfo, + RelayDataWithMessageHash, } from "../../interfaces"; import { BaseAbstractClient, UpdateFailureReason } from "../BaseAbstractClient"; import { AcrossConfigStoreClient } from "../AcrossConfigStoreClient"; @@ -980,7 +981,7 @@ export abstract class SpokePoolClient extends BaseAbstractClient { * @param atHeight The height at which to query the fill status. * @returns The fill status for the given relay data. */ - public abstract relayFillStatus(relayData: RelayData, atHeight?: number): Promise; + public abstract relayFillStatus(relayData: RelayDataWithMessageHash, atHeight?: number): Promise; /** * Retrieves the fill status for an array of given relay data. @@ -988,5 +989,8 @@ export abstract class SpokePoolClient extends BaseAbstractClient { * @param atHeight The height at which to query the fill status. * @returns The fill status for each of the given relay data. */ - public abstract fillStatusArray(relayData: RelayData[], atHeight?: number): Promise<(FillStatus | undefined)[]>; + public abstract fillStatusArray( + relayData: RelayDataWithMessageHash[], + atHeight?: number + ): Promise<(FillStatus | undefined)[]>; } diff --git a/src/interfaces/SpokePool.ts b/src/interfaces/SpokePool.ts index 1fa4c3ef8..21113391a 100644 --- a/src/interfaces/SpokePool.ts +++ b/src/interfaces/SpokePool.ts @@ -3,6 +3,10 @@ import { SpokePoolClient } from "../clients"; import { BigNumber, Address, EvmAddress } from "../utils"; import { RelayerRefundLeaf } from "./HubPool"; +export interface RelayDataWithMessageHash extends RelayData { + messageHash: string; +} + export interface RelayData { originChainId: number; depositor: Address; diff --git a/src/relayFeeCalculator/chain-queries/baseQuery.ts b/src/relayFeeCalculator/chain-queries/baseQuery.ts index 101f72f44..ef8e11942 100644 --- a/src/relayFeeCalculator/chain-queries/baseQuery.ts +++ b/src/relayFeeCalculator/chain-queries/baseQuery.ts @@ -3,7 +3,7 @@ import { isL2Provider as isOptimismL2Provider } from "@eth-optimism/sdk/dist/l2- import { PopulatedTransaction, providers, VoidSigner } from "ethers"; import { Coingecko } from "../../coingecko"; import { CHAIN_IDs } from "../../constants"; -import { RelayData } from "../../interfaces"; +import { RelayData, RelayDataWithMessageHash } from "../../interfaces"; import { SpokePool, SpokePool__factory } from "../../typechain"; import { populateV3Relay } from "../../arch/evm"; import { @@ -72,7 +72,7 @@ export class QueryBase implements QueryInterface { * @returns The gas estimate for this function call (multiplied with the optional buffer). */ async getGasCosts( - relayData: RelayData & { destinationChainId: number }, + relayData: RelayDataWithMessageHash & { destinationChainId: number }, relayer = getDefaultRelayer(relayData.destinationChainId), options: Partial<{ gasPrice: BigNumberish; @@ -144,7 +144,7 @@ export class QueryBase implements QueryInterface { * @returns Estimated gas cost based on ethers.VoidSigner's gas estimation */ async getNativeGasCost( - relayData: RelayData & { destinationChainId: number }, + relayData: RelayDataWithMessageHash & { destinationChainId: number }, relayer = getDefaultRelayer(relayData.destinationChainId) ): Promise { const { recipient, outputToken, exclusiveRelayer } = relayData; diff --git a/src/relayFeeCalculator/chain-queries/svmQuery.ts b/src/relayFeeCalculator/chain-queries/svmQuery.ts index 37df270a7..297ac1b10 100644 --- a/src/relayFeeCalculator/chain-queries/svmQuery.ts +++ b/src/relayFeeCalculator/chain-queries/svmQuery.ts @@ -4,7 +4,7 @@ import { SVMProvider, SolanaVoidSigner, getFillRelayTx } from "../../arch/svm"; import { Coingecko } from "../../coingecko"; import { CHAIN_IDs } from "../../constants"; import { getGasPriceEstimate } from "../../gasPriceOracle"; -import { RelayData } from "../../interfaces"; +import { RelayDataWithMessageHash } from "../../interfaces"; import { Address, BigNumber, BigNumberish, SvmAddress, TransactionCostEstimate, toBN } from "../../utils"; import { Logger, QueryInterface, getDefaultRelayer } from "../relayFeeCalculator"; import { SymbolMappingType } from "./"; @@ -53,7 +53,7 @@ export class SvmQuery implements QueryInterface { * @returns The gas estimate for this function call (multiplied with the optional buffer). */ async getGasCosts( - relayData: RelayData & { destinationChainId: number }, + relayData: RelayDataWithMessageHash & { destinationChainId: number }, relayer = getDefaultRelayer(relayData.destinationChainId), options: Partial<{ gasPrice: BigNumberish; @@ -105,7 +105,7 @@ export class SvmQuery implements QueryInterface { * @returns Estimated gas cost in compute units */ async getNativeGasCost( - deposit: RelayData & { destinationChainId: number }, + deposit: RelayDataWithMessageHash & { destinationChainId: number }, relayer = getDefaultRelayer(deposit.destinationChainId) ): Promise { const { destinationChainId, recipient, outputToken, exclusiveRelayer } = deposit; @@ -131,7 +131,7 @@ export class SvmQuery implements QueryInterface { * @returns FillRelay transaction */ protected async getFillRelayTx( - relayData: Omit & { + relayData: Omit & { destinationChainId: number; recipient: SvmAddress; outputToken: SvmAddress; diff --git a/src/relayFeeCalculator/relayFeeCalculator.ts b/src/relayFeeCalculator/relayFeeCalculator.ts index cd71d3450..f428ba72f 100644 --- a/src/relayFeeCalculator/relayFeeCalculator.ts +++ b/src/relayFeeCalculator/relayFeeCalculator.ts @@ -5,7 +5,7 @@ import { DEFAULT_SIMULATED_RELAYER_ADDRESS_SVM, TOKEN_SYMBOLS_MAP, } from "../constants"; -import { RelayData } from "../interfaces"; +import { RelayDataWithMessageHash } from "../interfaces"; import { BigNumber, BigNumberish, @@ -33,7 +33,7 @@ import { // This needs to be implemented for every chain and passed into RelayFeeCalculator export interface QueryInterface { getGasCosts: ( - deposit: RelayData & { destinationChainId: number }, + deposit: RelayDataWithMessageHash & { destinationChainId: number }, relayer: Address, options?: Partial<{ gasPrice: BigNumberish; @@ -45,7 +45,10 @@ export interface QueryInterface { }> ) => Promise; getTokenPrice: (tokenSymbol: string) => Promise; - getNativeGasCost: (deposit: RelayData & { destinationChainId: number }, relayer: Address) => Promise; + getNativeGasCost: ( + deposit: RelayDataWithMessageHash & { destinationChainId: number }, + relayer: Address + ) => Promise; } export const expectedCapitalCostsKeys = ["lowerBound", "upperBound", "cutoff", "decimals"]; @@ -254,7 +257,7 @@ export class RelayFeeCalculator { * the correct parameters to see a full fill. */ async gasFeePercent( - deposit: RelayData & { destinationChainId: number }, + deposit: RelayDataWithMessageHash & { destinationChainId: number }, outputAmount: BigNumberish, simulateZeroFill = false, relayerAddress = getDefaultRelayer(deposit.destinationChainId), @@ -493,7 +496,7 @@ export class RelayFeeCalculator { * @returns A resulting `RelayerFeeDetails` object */ async relayerFeeDetails( - deposit: RelayData & { destinationChainId: number }, + deposit: RelayDataWithMessageHash & { destinationChainId: number }, outputAmount?: BigNumberish, simulateZeroFill = false, relayerAddress = getDefaultRelayer(deposit.destinationChainId), diff --git a/src/utils/DepositUtils.ts b/src/utils/DepositUtils.ts index e340caae2..2478751b4 100644 --- a/src/utils/DepositUtils.ts +++ b/src/utils/DepositUtils.ts @@ -10,6 +10,7 @@ import { SlowFillRequest, ConvertedRelayData, ConvertedFill, + RelayDataWithMessageHash, } from "../interfaces"; import { getMessageHash, isUnsafeDepositId } from "./SpokeUtils"; import { getNetworkName } from "./NetworkUtils"; @@ -143,7 +144,7 @@ export async function queryHistoricalDepositForFill( * note: This function should _not_ be used to query the SpokePool.fillStatuses mapping. */ export function getRelayEventKey( - data: Omit & { messageHash: string; destinationChainId: number } + data: Omit & { destinationChainId: number } ): string { return [ data.depositor, diff --git a/src/utils/SpokeUtils.ts b/src/utils/SpokeUtils.ts index 489797d0d..0a438d2f6 100644 --- a/src/utils/SpokeUtils.ts +++ b/src/utils/SpokeUtils.ts @@ -1,7 +1,7 @@ import { encodeAbiParameters, Hex, keccak256 } from "viem"; import { fixedPointAdjustment as fixedPoint } from "./common"; import { MAX_SAFE_DEPOSIT_ID, ZERO_BYTES } from "../constants"; -import { Fill, FillType, RelayData, SlowFillLeaf } from "../interfaces"; +import { Fill, FillType, RelayDataWithMessageHash, SlowFillLeaf } from "../interfaces"; import { BigNumber } from "./BigNumberUtils"; import { isMessageEmpty } from "./DepositUtils"; import { chainIsSvm } from "./NetworkUtils"; @@ -22,7 +22,7 @@ export function getSlowFillLeafLpFeePct(leaf: SlowFillLeaf): BigNumber { * @param messageHash Hash of the message that will be used to create relayDataHash from fill. * @returns The corresponding RelayData hash. */ -export function getRelayDataHash(relayData: RelayData, destinationChainId: number, messageHash?: string): string { +export function getRelayDataHash(relayData: RelayDataWithMessageHash, destinationChainId: number): string { const abi = [ { type: "tuple", @@ -53,16 +53,12 @@ export function getRelayDataHash(relayData: RelayData, destinationChainId: numbe exclusiveRelayer: relayData.exclusiveRelayer.toBytes32(), }; if (chainIsSvm(destinationChainId)) { - const relayDataWithMessageHash = { - ...relayData, - messageHash: messageHash ?? getMessageHash(relayData.message), - }; - return svm.getRelayDataHash(relayDataWithMessageHash, destinationChainId); + return svm.getRelayDataHash(relayData, destinationChainId); } return keccak256(encodeAbiParameters(abi, [_relayData, destinationChainId])); } -export function getRelayHashFromEvent(e: RelayData & { destinationChainId: number }): string { +export function getRelayHashFromEvent(e: RelayDataWithMessageHash & { destinationChainId: number }): string { return getRelayDataHash(e, e.destinationChainId); } diff --git a/test/SVMSpokePoolClient.fills.ts b/test/SVMSpokePoolClient.fills.ts index 3a82d8a79..181ffa807 100644 --- a/test/SVMSpokePoolClient.fills.ts +++ b/test/SVMSpokePoolClient.fills.ts @@ -20,6 +20,7 @@ import { EvmAddress, SvmAddress, getCurrentTime, + getMessageHash, getRandomInt, randomAddress, toAddressType, @@ -266,7 +267,7 @@ describe("SVMSpokePoolClient: Fills", function () { ); const originChainId = Number(newRelayData.originChainId); - const depositData: Omit = { + const depositData: Deposit = { depositor: toAddressType(newRelayData.depositor, originChainId), recipient: SvmAddress.from(newRelayData.recipient), exclusiveRelayer: SvmAddress.from(newRelayData.exclusiveRelayer), @@ -283,6 +284,7 @@ describe("SVMSpokePoolClient: Fills", function () { originChainId: Number(newRelayData.originChainId), depositId: BigNumber.from(newRelayData.depositId), message: hexlify(newRelayData.message), + messageHash: getMessageHash(hexlify(newRelayData.message)), }; const gasCosts = await svmQuery.getGasCosts(depositData, toAddressType(signer.address, CHAIN_IDs.SOLANA)); diff --git a/test/utils/svm/utils.ts b/test/utils/svm/utils.ts index c1ace9326..b0bcdf15a 100644 --- a/test/utils/svm/utils.ts +++ b/test/utils/svm/utils.ts @@ -50,11 +50,12 @@ import { numberToU8a32, toAddress, } from "../../../src/arch/svm"; -import { RelayData } from "../../../src/interfaces"; +import { RelayDataWithMessageHash } from "../../../src/interfaces"; import { BigNumber, EvmAddress, SvmAddress, + getMessageHash, getRandomInt, getRelayDataHash, randomAddress, @@ -458,7 +459,7 @@ export const encodeEmergencyDeleteRootBundleMessageBody = (rootBundleId: number) /** Relay Data Utils */ -export const formatRelayData = (relayData: SvmSpokeClient.RelayDataArgs): RelayData => { +export const formatRelayData = (relayData: SvmSpokeClient.RelayDataArgs): RelayDataWithMessageHash => { const originChainId = Number(relayData.originChainId); return { originChainId, @@ -473,5 +474,6 @@ export const formatRelayData = (relayData: SvmSpokeClient.RelayDataArgs): RelayD exclusivityDeadline: relayData.exclusivityDeadline, message: hexlify(relayData.message), exclusiveRelayer: SvmAddress.from(relayData.exclusiveRelayer), + messageHash: getMessageHash(hexlify(relayData.message)), }; }; From 93ef97a38007b14afa52ccf0f81806991a476941 Mon Sep 17 00:00:00 2001 From: Aleksa Colovic Date: Wed, 6 Aug 2025 10:25:55 +0200 Subject: [PATCH 5/6] Remove messageHash from params --- src/arch/svm/SpokeUtils.ts | 1 - src/arch/svm/utils.ts | 1 - src/utils/SpokeUtils.ts | 1 - 3 files changed, 3 deletions(-) diff --git a/src/arch/svm/SpokeUtils.ts b/src/arch/svm/SpokeUtils.ts index 98a9c049c..21b1cc1bc 100644 --- a/src/arch/svm/SpokeUtils.ts +++ b/src/arch/svm/SpokeUtils.ts @@ -264,7 +264,6 @@ export async function findDeposit( * @param provider - SVM provider instance. * @param svmEventsClient - SVM events client for querying events. * @param atHeight - (Optional) Specific slot number to query. Defaults to the latest confirmed slot. - * @param messageHash - (Optional) Hash of the message that will be used to create relayDataHash from fill. * @returns The fill status for the deposit at the specified or current slot. */ export async function relayFillStatus( diff --git a/src/arch/svm/utils.ts b/src/arch/svm/utils.ts index c6681acc8..07ceb5521 100644 --- a/src/arch/svm/utils.ts +++ b/src/arch/svm/utils.ts @@ -259,7 +259,6 @@ export async function getStatePda(programId: Address): Promise
{ * @param programId The SpokePool program ID. * @param relayData The relay data to get the fill status PDA for. * @param destinationChainId The destination chain ID. - * @param messageHash Hash of the message that will be used to create relayDataHash from fill. * @returns The PDA for the fill status. */ export async function getFillStatusPda( diff --git a/src/utils/SpokeUtils.ts b/src/utils/SpokeUtils.ts index 0a438d2f6..9a3e3ee51 100644 --- a/src/utils/SpokeUtils.ts +++ b/src/utils/SpokeUtils.ts @@ -19,7 +19,6 @@ export function getSlowFillLeafLpFeePct(leaf: SlowFillLeaf): BigNumber { * Compute the RelayData hash for a fill. This can be used to determine the fill status. * @param relayData RelayData information that is used to complete a fill. * @param destinationChainId Supplementary destination chain ID required by V3 hashes. - * @param messageHash Hash of the message that will be used to create relayDataHash from fill. * @returns The corresponding RelayData hash. */ export function getRelayDataHash(relayData: RelayDataWithMessageHash, destinationChainId: number): string { From 3f7eea1bad080d7db0fb378194ade148add9d630 Mon Sep 17 00:00:00 2001 From: Aleksa Colovic Date: Wed, 6 Aug 2025 10:28:22 +0200 Subject: [PATCH 6/6] Version bump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 268b5afe2..1a75bb1c0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@across-protocol/sdk", "author": "UMA Team", - "version": "4.3.37", + "version": "4.3.38", "license": "AGPL-3.0", "homepage": "https://docs.across.to/reference/sdk", "files": [