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": [ 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 012e15f8d..21b1cc1bc 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 { @@ -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, @@ -83,7 +90,7 @@ import { */ export const SLOT_DURATION_MS = 400; -type ProtoFill = Omit & { +type ProtoFill = Omit & { destinationChainId: number; recipient: SvmAddress; outputToken: SvmAddress; @@ -261,7 +268,7 @@ export async function findDeposit( */ export async function relayFillStatus( programId: Address, - relayData: RelayData, + relayData: RelayDataWithMessageHash, destinationChainId: number, svmEventsClient: SvmCpiEventsClient, atHeight?: number @@ -311,7 +318,7 @@ export async function relayFillStatus( */ export async function fillStatusArray( programId: Address, - relayData: RelayData[], + relayData: RelayDataWithMessageHash[], destinationChainId: number, svmEventsClient: SvmCpiEventsClient, atHeight?: number, @@ -394,7 +401,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, @@ -566,7 +573,7 @@ export function createTokenAccountsInstruction( export async function getFillRelayTx( spokePoolAddr: SvmAddress, solanaClient: SVMProvider, - relayData: Omit & { + relayData: Omit & { destinationChainId: number; recipient: SvmAddress; outputToken: SvmAddress; @@ -583,6 +590,7 @@ export async function getFillRelayTx( ); const program = toAddress(spokePoolAddr); + const _relayDataHash = getRelayDataHash(relayData, destinationChainId); const relayDataHash = new Uint8Array(Buffer.from(_relayDataHash.slice(2), "hex")); @@ -770,7 +778,7 @@ export const createRequestSlowFillInstruction = async ( export async function getSlowFillRequestTx( spokePoolAddr: SvmAddress, solanaClient: SVMProvider, - relayData: Omit & { + relayData: Omit & { destinationChainId: number; recipient: SvmAddress; outputToken: SvmAddress; @@ -792,6 +800,7 @@ export async function getSlowFillRequestTx( } = relayData; const program = toAddress(spokePoolAddr); + const relayDataHash = getRelayDataHash(relayData, destinationChainId); const [state, fillStatus, eventAuthority] = await Promise.all([ @@ -881,12 +890,12 @@ export async function getAssociatedTokenAddress( return associatedToken; } -export function getRelayDataHash(relayData: RelayData, destinationChainId: number): string { +export function getRelayDataHash(relayData: RelayDataWithMessageHash, 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([ @@ -901,7 +910,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/arch/svm/utils.ts b/src/arch/svm/utils.ts index 287679c42..5946c2d90 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,7 +264,7 @@ export async function getStatePda(programId: Address): Promise
{ */ export async function getFillStatusPda( programId: Address, - relayData: RelayData, + relayData: RelayDataWithMessageHash, destinationChainId: number ): Promise
{ const relayDataHash = getRelayDataHash(relayData, destinationChainId); 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 aaa6b9079..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,7 +243,7 @@ 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 { + 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 9a6ad64b9..9a3e3ee51 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"; @@ -21,7 +21,7 @@ export function getSlowFillLeafLpFeePct(leaf: SlowFillLeaf): BigNumber { * @param destinationChainId Supplementary destination chain ID required by V3 hashes. * @returns The corresponding RelayData hash. */ -export function getRelayDataHash(relayData: RelayData, destinationChainId: number): string { +export function getRelayDataHash(relayData: RelayDataWithMessageHash, destinationChainId: number): string { const abi = [ { type: "tuple", @@ -57,7 +57,7 @@ export function getRelayDataHash(relayData: RelayData, destinationChainId: numbe 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)), }; };