@across-protocol/sdk 4.1.63-beta.0 → 4.1.63-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/dist/cjs/arch/svm/SpokeUtils.d.ts +3 -3
  2. package/dist/cjs/arch/svm/SpokeUtils.js +37 -41
  3. package/dist/cjs/arch/svm/SpokeUtils.js.map +1 -1
  4. package/dist/cjs/arch/svm/constants.d.ts +1 -0
  5. package/dist/cjs/arch/svm/constants.js +3 -1
  6. package/dist/cjs/arch/svm/constants.js.map +1 -1
  7. package/dist/cjs/arch/svm/eventsClient.d.ts +6 -1
  8. package/dist/cjs/arch/svm/eventsClient.js +64 -0
  9. package/dist/cjs/arch/svm/eventsClient.js.map +1 -1
  10. package/dist/cjs/arch/svm/utils.js +2 -2
  11. package/dist/cjs/arch/svm/utils.js.map +1 -1
  12. package/dist/cjs/clients/BundleDataClient/utils/SuperstructUtils.d.ts +26 -26
  13. package/dist/cjs/constants.d.ts +1 -0
  14. package/dist/cjs/constants.js +2 -1
  15. package/dist/cjs/constants.js.map +1 -1
  16. package/dist/cjs/interfaces/SpokePool.d.ts +6 -0
  17. package/dist/cjs/interfaces/SpokePool.js.map +1 -1
  18. package/dist/cjs/relayFeeCalculator/chain-queries/baseQuery.js +5 -4
  19. package/dist/cjs/relayFeeCalculator/chain-queries/baseQuery.js.map +1 -1
  20. package/dist/cjs/relayFeeCalculator/chain-queries/factory.js +1 -1
  21. package/dist/cjs/relayFeeCalculator/chain-queries/factory.js.map +1 -1
  22. package/dist/cjs/relayFeeCalculator/chain-queries/svmQuery.d.ts +6 -0
  23. package/dist/cjs/relayFeeCalculator/chain-queries/svmQuery.js +69 -34
  24. package/dist/cjs/relayFeeCalculator/chain-queries/svmQuery.js.map +1 -1
  25. package/dist/cjs/relayFeeCalculator/relayFeeCalculator.d.ts +2 -0
  26. package/dist/cjs/relayFeeCalculator/relayFeeCalculator.js +9 -3
  27. package/dist/cjs/relayFeeCalculator/relayFeeCalculator.js.map +1 -1
  28. package/dist/cjs/utils/SpokeUtils.js.map +1 -1
  29. package/dist/esm/arch/svm/SpokeUtils.d.ts +3 -3
  30. package/dist/esm/arch/svm/SpokeUtils.js +26 -30
  31. package/dist/esm/arch/svm/SpokeUtils.js.map +1 -1
  32. package/dist/esm/arch/svm/constants.d.ts +1 -0
  33. package/dist/esm/arch/svm/constants.js +1 -0
  34. package/dist/esm/arch/svm/constants.js.map +1 -1
  35. package/dist/esm/arch/svm/eventsClient.d.ts +21 -1
  36. package/dist/esm/arch/svm/eventsClient.js +80 -1
  37. package/dist/esm/arch/svm/eventsClient.js.map +1 -1
  38. package/dist/esm/arch/svm/utils.js +3 -3
  39. package/dist/esm/arch/svm/utils.js.map +1 -1
  40. package/dist/esm/clients/BundleDataClient/utils/SuperstructUtils.d.ts +26 -26
  41. package/dist/esm/constants.d.ts +1 -0
  42. package/dist/esm/constants.js +1 -0
  43. package/dist/esm/constants.js.map +1 -1
  44. package/dist/esm/interfaces/SpokePool.d.ts +6 -0
  45. package/dist/esm/interfaces/SpokePool.js.map +1 -1
  46. package/dist/esm/relayFeeCalculator/chain-queries/baseQuery.js +6 -5
  47. package/dist/esm/relayFeeCalculator/chain-queries/baseQuery.js.map +1 -1
  48. package/dist/esm/relayFeeCalculator/chain-queries/factory.js +3 -3
  49. package/dist/esm/relayFeeCalculator/chain-queries/factory.js.map +1 -1
  50. package/dist/esm/relayFeeCalculator/chain-queries/svmQuery.d.ts +18 -0
  51. package/dist/esm/relayFeeCalculator/chain-queries/svmQuery.js +83 -36
  52. package/dist/esm/relayFeeCalculator/chain-queries/svmQuery.js.map +1 -1
  53. package/dist/esm/relayFeeCalculator/relayFeeCalculator.d.ts +12 -1
  54. package/dist/esm/relayFeeCalculator/relayFeeCalculator.js +9 -4
  55. package/dist/esm/relayFeeCalculator/relayFeeCalculator.js.map +1 -1
  56. package/dist/esm/utils/SpokeUtils.js.map +1 -1
  57. package/dist/types/arch/svm/SpokeUtils.d.ts +3 -3
  58. package/dist/types/arch/svm/SpokeUtils.d.ts.map +1 -1
  59. package/dist/types/arch/svm/constants.d.ts +1 -0
  60. package/dist/types/arch/svm/constants.d.ts.map +1 -1
  61. package/dist/types/arch/svm/eventsClient.d.ts +21 -1
  62. package/dist/types/arch/svm/eventsClient.d.ts.map +1 -1
  63. package/dist/types/arch/svm/utils.d.ts.map +1 -1
  64. package/dist/types/clients/BundleDataClient/utils/SuperstructUtils.d.ts +26 -26
  65. package/dist/types/constants.d.ts +1 -0
  66. package/dist/types/constants.d.ts.map +1 -1
  67. package/dist/types/interfaces/SpokePool.d.ts +6 -0
  68. package/dist/types/interfaces/SpokePool.d.ts.map +1 -1
  69. package/dist/types/relayFeeCalculator/chain-queries/baseQuery.d.ts.map +1 -1
  70. package/dist/types/relayFeeCalculator/chain-queries/factory.d.ts.map +1 -1
  71. package/dist/types/relayFeeCalculator/chain-queries/svmQuery.d.ts +18 -0
  72. package/dist/types/relayFeeCalculator/chain-queries/svmQuery.d.ts.map +1 -1
  73. package/dist/types/relayFeeCalculator/relayFeeCalculator.d.ts +12 -1
  74. package/dist/types/relayFeeCalculator/relayFeeCalculator.d.ts.map +1 -1
  75. package/package.json +1 -1
  76. package/src/arch/svm/SpokeUtils.ts +43 -43
  77. package/src/arch/svm/constants.ts +1 -0
  78. package/src/arch/svm/eventsClient.ts +114 -1
  79. package/src/arch/svm/utils.ts +4 -4
  80. package/src/constants.ts +1 -0
  81. package/src/interfaces/SpokePool.ts +7 -0
  82. package/src/relayFeeCalculator/chain-queries/baseQuery.ts +6 -6
  83. package/src/relayFeeCalculator/chain-queries/factory.ts +3 -3
  84. package/src/relayFeeCalculator/chain-queries/svmQuery.ts +59 -27
  85. package/src/relayFeeCalculator/relayFeeCalculator.ts +15 -3
  86. package/src/utils/SpokeUtils.ts +2 -2
@@ -1,38 +1,41 @@
1
- import {
2
- SvmAddress,
3
- getTokenInfo,
4
- BigNumber,
5
- isDefined,
6
- isUnsafeDepositId,
7
- toAddressType,
8
- toBN,
9
- getMessageHash,
10
- keccak256,
11
- chainIsSvm,
12
- chunk,
13
- } from "../../utils";
14
1
  import { SvmSpokeClient } from "@across-protocol/contracts";
15
- import { getStatePda, SvmCpiEventsClient, getFillStatusPda, unwrapEventData, getEventAuthority } from "./";
16
- import { Deposit, FillStatus, FillWithBlock, RelayData } from "../../interfaces";
2
+ import { decodeFillStatusAccount, fetchState } from "@across-protocol/contracts/dist/src/svm/clients/SvmSpoke";
3
+ import { hashNonEmptyMessage } from "@across-protocol/contracts/dist/src/svm/web3-v1";
17
4
  import {
18
- TOKEN_PROGRAM_ADDRESS,
19
5
  ASSOCIATED_TOKEN_PROGRAM_ADDRESS,
6
+ TOKEN_PROGRAM_ADDRESS,
20
7
  getApproveCheckedInstruction,
21
8
  } from "@solana-program/token";
22
9
  import {
23
10
  Address,
24
- some,
25
11
  address,
26
- getProgramDerivedAddress,
27
- fetchEncodedAccounts,
28
12
  fetchEncodedAccount,
13
+ fetchEncodedAccounts,
14
+ getAddressEncoder,
15
+ getProgramDerivedAddress,
16
+ getU32Encoder,
17
+ getU64Encoder,
18
+ some,
29
19
  type TransactionSigner,
30
20
  } from "@solana/kit";
31
21
  import assert from "assert";
22
+ import { arrayify, hexZeroPad, hexlify } from "ethers/lib/utils";
32
23
  import { Logger } from "winston";
33
- import { fetchState, decodeFillStatusAccount } from "@across-protocol/contracts/dist/src/svm/clients/SvmSpoke";
34
- import { SVMEventNames, SVMProvider } from "./types";
35
24
  import { CHAIN_IDs } from "../../constants";
25
+ import { Deposit, FillStatus, FillWithBlock, RelayData } from "../../interfaces";
26
+ import {
27
+ BigNumber,
28
+ SvmAddress,
29
+ chainIsSvm,
30
+ chunk,
31
+ getTokenInfo,
32
+ isDefined,
33
+ isUnsafeDepositId,
34
+ keccak256,
35
+ toAddressType,
36
+ } from "../../utils";
37
+ import { SvmCpiEventsClient, getEventAuthority, getFillStatusPda, getStatePda, unwrapEventData } from "./";
38
+ import { SVMEventNames, SVMProvider } from "./types";
36
39
 
37
40
  /**
38
41
  * @param spokePool SpokePool Contract instance.
@@ -412,29 +415,26 @@ export async function getAssociatedTokenAddress(
412
415
  }
413
416
 
414
417
  export function getRelayDataHash(relayData: RelayData, destinationChainId: number): string {
415
- const toBuffer = (hex: string, byteLength: number, littleEndian: boolean = true) => {
416
- const buffer = Buffer.from(hex.slice(2), "hex");
417
- if (buffer.length < byteLength) {
418
- const zeroPad = Buffer.alloc(byteLength);
419
- buffer.copy(zeroPad, byteLength - buffer.length);
420
- return littleEndian ? zeroPad.reverse() : zeroPad;
421
- }
422
- return littleEndian ? buffer.slice(0, byteLength).reverse() : buffer.slice(0, byteLength);
423
- };
418
+ const addressEncoder = getAddressEncoder();
419
+ const uint64Encoder = getU64Encoder();
420
+ const uint32Encoder = getU32Encoder();
421
+
422
+ assert(relayData.message.startsWith("0x"), "Message must be a hex string");
423
+
424
424
  const contentToHash = Buffer.concat([
425
- toBuffer(relayData.depositor, 32, false),
426
- toBuffer(relayData.recipient, 32, false),
427
- toBuffer(relayData.exclusiveRelayer, 32, false),
428
- toBuffer(relayData.inputToken, 32, false),
429
- toBuffer(relayData.outputToken, 32, false),
430
- toBuffer(relayData.inputAmount.toHexString(), 8),
431
- toBuffer(relayData.outputAmount.toHexString(), 8),
432
- toBuffer(toBN(relayData.originChainId).toHexString(), 8),
433
- toBuffer(relayData.depositId.toHexString(), 32, false),
434
- toBuffer(toBN(relayData.fillDeadline).toHexString(), 4),
435
- toBuffer(toBN(relayData.exclusivityDeadline).toHexString(), 4),
436
- toBuffer(getMessageHash(relayData.message), 32, false),
437
- toBuffer(toBN(destinationChainId).toHexString(), 8),
425
+ Uint8Array.from(addressEncoder.encode(SvmAddress.from(relayData.depositor, "base16").toV2Address())),
426
+ Uint8Array.from(addressEncoder.encode(SvmAddress.from(relayData.recipient, "base16").toV2Address())),
427
+ Uint8Array.from(addressEncoder.encode(SvmAddress.from(relayData.exclusiveRelayer, "base16").toV2Address())),
428
+ Uint8Array.from(addressEncoder.encode(SvmAddress.from(relayData.inputToken, "base16").toV2Address())),
429
+ Uint8Array.from(addressEncoder.encode(SvmAddress.from(relayData.outputToken, "base16").toV2Address())),
430
+ Uint8Array.from(uint64Encoder.encode(BigInt(relayData.inputAmount.toString()))),
431
+ Uint8Array.from(uint64Encoder.encode(BigInt(relayData.outputAmount.toString()))),
432
+ Uint8Array.from(uint64Encoder.encode(BigInt(relayData.originChainId.toString()))),
433
+ arrayify(hexZeroPad(hexlify(relayData.depositId), 32)),
434
+ Uint8Array.from(uint32Encoder.encode(relayData.fillDeadline)),
435
+ Uint8Array.from(uint32Encoder.encode(relayData.exclusivityDeadline)),
436
+ hashNonEmptyMessage(Buffer.from(arrayify(relayData.message))),
437
+ Uint8Array.from(uint64Encoder.encode(BigInt(destinationChainId))),
438
438
  ]);
439
439
  return keccak256(contentToHash);
440
440
  }
@@ -1 +1,2 @@
1
+ export { SYSTEM_PROGRAM_ADDRESS as SVM_DEFAULT_ADDRESS } from "@solana-program/system";
1
2
  export const SVM_SPOKE_SEED = BigInt(0);
@@ -2,9 +2,16 @@ import { Idl } from "@coral-xyz/anchor";
2
2
  import { getDeployedAddress, SvmSpokeIdl } from "@across-protocol/contracts";
3
3
  import { getSolanaChainId } from "@across-protocol/contracts/dist/src/svm/web3-v1";
4
4
  import web3, { Address, Commitment, GetSignaturesForAddressApi, GetTransactionApi, Signature } from "@solana/kit";
5
- import { bs58 } from "../../utils";
5
+ import { bs58, chainIsSvm, getMessageHash } from "../../utils";
6
6
  import { EventName, EventWithData, SVMProvider } from "./types";
7
7
  import { decodeEvent, isDevnet } from "./utils";
8
+ import { DepositWithTime, FillWithTime } from "../../interfaces";
9
+ import { unwrapEventData } from "./";
10
+ import assert from "assert";
11
+ import {
12
+ FundsDepositedEventObject,
13
+ FilledRelayEventObject,
14
+ } from "@across-protocol/contracts/dist/typechain/contracts/SpokePool";
8
15
 
9
16
  // Utility type to extract the return type for the JSON encoding overload. We only care about the overload where the
10
17
  // configuration parameter (C) has the optional property 'encoding' set to 'json'.
@@ -19,6 +26,9 @@ type GetSignaturesForAddressConfig = Parameters<GetSignaturesForAddressApi["getS
19
26
  type GetSignaturesForAddressTransaction = ReturnType<GetSignaturesForAddressApi["getSignaturesForAddress"]>[number];
20
27
  type GetSignaturesForAddressApiResponse = readonly GetSignaturesForAddressTransaction[];
21
28
 
29
+ export type DepositEventFromSignature = Omit<DepositWithTime, "fromLiteChain" | "toLiteChain">;
30
+ export type FillEventFromSignature = FillWithTime;
31
+
22
32
  export class SvmCpiEventsClient {
23
33
  private rpc: SVMProvider;
24
34
  private programAddress: Address;
@@ -211,6 +221,109 @@ export class SvmCpiEventsClient {
211
221
  return events;
212
222
  }
213
223
 
224
+ /**
225
+ * Finds all FundsDeposited events for a given transaction signature.
226
+ *
227
+ * @param originChainId - The chain ID where the deposit originated.
228
+ * @param txSignature - The transaction signature to search for events.
229
+ * @param commitment - Optional commitment level for the transaction query.
230
+ * @returns A promise that resolves to an array of deposit events for the transaction, or undefined if none found.
231
+ */
232
+ public async getDepositEventsFromSignature(
233
+ originChainId: number,
234
+ txSignature: Signature,
235
+ commitment: Commitment = "confirmed"
236
+ ): Promise<DepositEventFromSignature[] | undefined> {
237
+ assert(chainIsSvm(originChainId), `Origin chain ${originChainId} is not an SVM chain`);
238
+
239
+ const [events, txDetails] = await Promise.all([
240
+ this.readEventsFromSignature(txSignature, commitment),
241
+ this.rpc
242
+ .getTransaction(txSignature, {
243
+ commitment,
244
+ maxSupportedTransactionVersion: 0,
245
+ })
246
+ .send(),
247
+ ]);
248
+
249
+ // Filter for FundsDeposited events only
250
+ const depositEvents = events?.filter((event) => event?.name === "FundsDeposited");
251
+
252
+ if (!txDetails || !depositEvents?.length) {
253
+ return;
254
+ }
255
+
256
+ return events.map((event) => {
257
+ const unwrappedEventArgs = unwrapEventData(event as Record<string, unknown>, ["depositId"]) as Record<
258
+ "data",
259
+ FundsDepositedEventObject
260
+ >;
261
+
262
+ return {
263
+ ...unwrappedEventArgs.data,
264
+ depositTimestamp: Number(txDetails.blockTime),
265
+ originChainId,
266
+ messageHash: getMessageHash(unwrappedEventArgs.data.message),
267
+ blockNumber: Number(txDetails.slot),
268
+ txnIndex: 0,
269
+ txnRef: txSignature,
270
+ logIndex: 0,
271
+ destinationChainId: unwrappedEventArgs.data.destinationChainId.toNumber(),
272
+ } satisfies DepositEventFromSignature;
273
+ });
274
+ }
275
+
276
+ /**
277
+ * Finds all FilledRelay events for a given transaction signature.
278
+ *
279
+ * @param destinationChainId - The destination chain ID (must be an SVM chain).
280
+ * @param txSignature - The transaction signature to search for events.
281
+ * @returns A promise that resolves to an array of fill events for the transaction, or undefined if none found.
282
+ */
283
+ public async getFillEventsFromSignature(
284
+ destinationChainId: number,
285
+ txSignature: Signature,
286
+ commitment: Commitment = "confirmed"
287
+ ): Promise<FillEventFromSignature[] | undefined> {
288
+ assert(chainIsSvm(destinationChainId), `Destination chain ${destinationChainId} is not an SVM chain`);
289
+
290
+ // Find all events from the transaction signature and get transaction details
291
+ const [events, txDetails] = await Promise.all([
292
+ this.readEventsFromSignature(txSignature, commitment),
293
+ this.rpc
294
+ .getTransaction(txSignature, {
295
+ commitment,
296
+ maxSupportedTransactionVersion: 0,
297
+ })
298
+ .send(),
299
+ ]);
300
+
301
+ // Filter for FilledRelay events only
302
+ const fillEvents = events?.filter((event) => event?.name === "FilledRelay");
303
+
304
+ if (!txDetails || !fillEvents?.length) {
305
+ return;
306
+ }
307
+
308
+ return fillEvents.map((event) => {
309
+ const unwrappedEventData = unwrapEventData(event as Record<string, unknown>) as Record<
310
+ "data",
311
+ FilledRelayEventObject
312
+ >;
313
+ return {
314
+ ...unwrappedEventData.data,
315
+ fillTimestamp: Number(txDetails.blockTime),
316
+ blockNumber: Number(txDetails.slot),
317
+ txnRef: txSignature,
318
+ txnIndex: 0,
319
+ logIndex: 0,
320
+ destinationChainId,
321
+ repaymentChainId: unwrappedEventData.data.repaymentChainId.toNumber(),
322
+ originChainId: unwrappedEventData.data.originChainId.toNumber(),
323
+ } satisfies FillEventFromSignature;
324
+ });
325
+ }
326
+
214
327
  public getProgramAddress(): Address {
215
328
  return this.programAddress;
216
329
  }
@@ -1,16 +1,16 @@
1
+ import { SvmSpokeClient } from "@across-protocol/contracts";
1
2
  import { BN, BorshEventCoder, Idl } from "@coral-xyz/anchor";
2
3
  import {
4
+ Address,
3
5
  address,
6
+ getAddressEncoder,
4
7
  getProgramDerivedAddress,
5
8
  getU64Encoder,
6
- getAddressEncoder,
7
- Address,
8
9
  isAddress,
9
10
  type TransactionSigner,
10
11
  } from "@solana/kit";
11
- import { BigNumber, getRelayDataHash, isUint8Array, SvmAddress } from "../../utils";
12
- import { SvmSpokeClient } from "@across-protocol/contracts";
13
12
  import { FillType, RelayData } from "../../interfaces";
13
+ import { BigNumber, SvmAddress, getRelayDataHash, isUint8Array } from "../../utils";
14
14
  import { EventName, SVMEventNames, SVMProvider } from "./types";
15
15
 
16
16
  /**
package/src/constants.ts CHANGED
@@ -54,6 +54,7 @@ export const DEFAULT_CACHING_TTL = 60 * 60 * 24 * 7 * 2; // 2 Weeks
54
54
  export const DEFAULT_CACHING_SAFE_LAG = 60 * 60; // 1 hour
55
55
 
56
56
  export const DEFAULT_SIMULATED_RELAYER_ADDRESS = "0x07aE8551Be970cB1cCa11Dd7a11F47Ae82e70E67";
57
+ export const DEFAULT_SIMULATED_RELAYER_ADDRESS_SVM = "FmMK62wrtWVb5SVoTZftSCGw3nEDA79hDbZNTRnC1R6t";
57
58
  export const DEFAULT_SIMULATED_RELAYER_ADDRESS_TEST = "0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D"; // Görli, ...
58
59
 
59
60
  export const DEFAULT_ARWEAVE_STORAGE_ADDRESS = "Z6hjBM8FHu90lYWB8o5jR1dfX92FlV2WBaND9xgp8Lg";
@@ -37,6 +37,10 @@ export interface DepositWithBlock extends Deposit, SortableEvent {
37
37
  quoteBlockNumber: number;
38
38
  }
39
39
 
40
+ export interface DepositWithTime extends Deposit, SortableEvent {
41
+ depositTimestamp: number;
42
+ }
43
+
40
44
  export enum FillStatus {
41
45
  Unfilled = 0,
42
46
  RequestedSlowFill,
@@ -66,6 +70,9 @@ export interface Fill extends Omit<RelayData, "message"> {
66
70
  }
67
71
 
68
72
  export interface FillWithBlock extends Fill, SortableEvent {}
73
+ export interface FillWithTime extends Fill, SortableEvent {
74
+ fillTimestamp: number;
75
+ }
69
76
 
70
77
  export interface EnabledDepositRoute {
71
78
  originToken: string;
@@ -3,7 +3,7 @@ import { isL2Provider as isOptimismL2Provider } from "@eth-optimism/sdk/dist/l2-
3
3
 
4
4
  import { PopulatedTransaction, providers, VoidSigner } from "ethers";
5
5
  import { Coingecko } from "../../coingecko";
6
- import { CHAIN_IDs, DEFAULT_SIMULATED_RELAYER_ADDRESS } from "../../constants";
6
+ import { CHAIN_IDs } from "../../constants";
7
7
  import { Deposit } from "../../interfaces";
8
8
  import { SpokePool, SpokePool__factory } from "../../typechain";
9
9
  import { populateV3Relay } from "../../arch/evm";
@@ -17,7 +17,7 @@ import {
17
17
  fixedPointAdjustment,
18
18
  } from "../../utils";
19
19
  import assert from "assert";
20
- import { Logger, QueryInterface } from "../relayFeeCalculator";
20
+ import { Logger, QueryInterface, getDefaultSimulatedRelayerAddress } from "../relayFeeCalculator";
21
21
  import { Transport } from "viem";
22
22
  import { getGasPriceEstimate, EvmGasPriceEstimate } from "../../gasPriceOracle";
23
23
  type Provider = providers.Provider;
@@ -72,7 +72,7 @@ export class QueryBase implements QueryInterface {
72
72
  */
73
73
  async getGasCosts(
74
74
  deposit: Omit<Deposit, "messageHash">,
75
- relayer = DEFAULT_SIMULATED_RELAYER_ADDRESS,
75
+ relayer = getDefaultSimulatedRelayerAddress(deposit.destinationChainId),
76
76
  options: Partial<{
77
77
  gasPrice: BigNumberish;
78
78
  gasUnits: BigNumberish;
@@ -122,7 +122,7 @@ export class QueryBase implements QueryInterface {
122
122
  */
123
123
  getUnsignedTxFromDeposit(
124
124
  deposit: Omit<Deposit, "messageHash">,
125
- relayer = DEFAULT_SIMULATED_RELAYER_ADDRESS
125
+ relayer = getDefaultSimulatedRelayerAddress(deposit.destinationChainId)
126
126
  ): Promise<PopulatedTransaction> {
127
127
  return populateV3Relay(this.spokePool, deposit, relayer);
128
128
  }
@@ -135,7 +135,7 @@ export class QueryBase implements QueryInterface {
135
135
  */
136
136
  async getNativeGasCost(
137
137
  deposit: Omit<Deposit, "messageHash">,
138
- relayer = DEFAULT_SIMULATED_RELAYER_ADDRESS
138
+ relayer = getDefaultSimulatedRelayerAddress(deposit.destinationChainId)
139
139
  ): Promise<BigNumber> {
140
140
  const unsignedTx = await this.getUnsignedTxFromDeposit(deposit, relayer);
141
141
  const voidSigner = new VoidSigner(relayer, this.provider);
@@ -152,7 +152,7 @@ export class QueryBase implements QueryInterface {
152
152
  */
153
153
  async getOpStackL1DataFee(
154
154
  unsignedTx: PopulatedTransaction,
155
- relayer = DEFAULT_SIMULATED_RELAYER_ADDRESS,
155
+ relayer = getDefaultSimulatedRelayerAddress(unsignedTx.chainId),
156
156
  options: Partial<{
157
157
  opStackL2GasUnits: BigNumberish;
158
158
  opStackL1DataFeeMultiplier: BigNumber;
@@ -3,11 +3,11 @@ import { CHAIN_IDs, TOKEN_SYMBOLS_MAP } from "@across-protocol/constants";
3
3
  import { getDeployedAddress } from "@across-protocol/contracts";
4
4
  import { asL2Provider } from "@eth-optimism/sdk";
5
5
  import { providers } from "ethers";
6
- import { DEFAULT_SIMULATED_RELAYER_ADDRESS, CUSTOM_GAS_TOKENS } from "../../constants";
6
+ import { CUSTOM_GAS_TOKENS } from "../../constants";
7
7
  import { chainIsOPStack, isDefined, chainIsSvm, SvmAddress } from "../../utils";
8
8
  import { QueryBase } from "./baseQuery";
9
9
  import { SVMProvider as svmProvider } from "../../arch/svm";
10
- import { DEFAULT_LOGGER, Logger } from "../relayFeeCalculator";
10
+ import { DEFAULT_LOGGER, getDefaultSimulatedRelayerAddress, Logger } from "../relayFeeCalculator";
11
11
  import { CustomGasTokenQueries } from "./customGasToken";
12
12
  import { SvmQuery } from "./svmQuery";
13
13
 
@@ -25,7 +25,7 @@ export class QueryBase__factory {
25
25
  provider: providers.Provider | svmProvider,
26
26
  symbolMapping = TOKEN_SYMBOLS_MAP,
27
27
  spokePoolAddress = getDeployedAddress("SpokePool", chainId),
28
- simulatedRelayerAddress = DEFAULT_SIMULATED_RELAYER_ADDRESS,
28
+ simulatedRelayerAddress = getDefaultSimulatedRelayerAddress(chainId),
29
29
  coingeckoProApiKey?: string,
30
30
  logger: Logger = DEFAULT_LOGGER,
31
31
  coingeckoBaseCurrency = "eth"
@@ -1,7 +1,7 @@
1
1
  import { pipe } from "@solana/functional";
2
2
  import { Coingecko } from "../../coingecko";
3
3
  import { SymbolMappingType } from "./";
4
- import { CHAIN_IDs, DEFAULT_SIMULATED_RELAYER_ADDRESS } from "../../constants";
4
+ import { CHAIN_IDs } from "../../constants";
5
5
  import { Deposit } from "../../interfaces";
6
6
  import { getGasPriceEstimate, SvmGasPriceEstimate } from "../../gasPriceOracle";
7
7
  import {
@@ -13,7 +13,7 @@ import {
13
13
  isDefined,
14
14
  toAddressType,
15
15
  } from "../../utils";
16
- import { Logger, QueryInterface } from "../relayFeeCalculator";
16
+ import { getDefaultSimulatedRelayerAddress, Logger, QueryInterface } from "../relayFeeCalculator";
17
17
  import {
18
18
  fillRelayInstruction,
19
19
  createApproveInstruction,
@@ -79,7 +79,7 @@ export class SvmQuery implements QueryInterface {
79
79
  */
80
80
  async getGasCosts(
81
81
  deposit: Omit<Deposit, "messageHash">,
82
- _relayer = DEFAULT_SIMULATED_RELAYER_ADDRESS,
82
+ _relayer = getDefaultSimulatedRelayerAddress(deposit.destinationChainId),
83
83
  options: Partial<{
84
84
  gasPrice: BigNumberish;
85
85
  gasUnits: BigNumberish;
@@ -87,11 +87,64 @@ export class SvmQuery implements QueryInterface {
87
87
  priorityFeeMultiplier: BigNumber;
88
88
  }> = {}
89
89
  ): Promise<TransactionCostEstimate> {
90
+ const relayer = _relayer ? toAddressType(_relayer).forceSvmAddress() : this.simulatedRelayerAddress;
91
+
92
+ const fillRelayTx = await this.getFillRelayTx(deposit, relayer.toBase58());
93
+
94
+ const [computeUnitsConsumed, _gasPriceEstimate] = await Promise.all([
95
+ toBN(await this.computeUnitEstimator(fillRelayTx)),
96
+ getGasPriceEstimate(this.provider, {
97
+ unsignedTx: fillRelayTx,
98
+ baseFeeMultiplier: options.baseFeeMultiplier,
99
+ priorityFeeMultiplier: options.priorityFeeMultiplier,
100
+ }),
101
+ ]);
102
+
103
+ // We can cast the gas price estimate to an SvmGasPriceEstimate here since the oracle should always
104
+ // query the Solana adapter.
105
+ const gasPriceEstimate = _gasPriceEstimate as SvmGasPriceEstimate;
106
+ const gasPrice = gasPriceEstimate.baseFee.add(
107
+ gasPriceEstimate.microLamportsPerComputeUnit.mul(computeUnitsConsumed).div(toBN(1_000_000)) // 1_000_000 microLamports/lamport.
108
+ );
109
+
110
+ return {
111
+ nativeGasCost: computeUnitsConsumed,
112
+ tokenGasCost: gasPrice,
113
+ gasPrice,
114
+ };
115
+ }
116
+
117
+ /**
118
+ * @notice Return the gas cost of a simulated transaction
119
+ * @param fillRelayTx FillRelay transaction
120
+ * @param relayer SVM address of the relayer
121
+ * @returns Estimated gas cost in compute units
122
+ */
123
+ async getNativeGasCost(
124
+ deposit: Omit<Deposit, "messageHash">,
125
+ _relayer = getDefaultSimulatedRelayerAddress(deposit.destinationChainId)
126
+ ): Promise<BigNumber> {
127
+ const fillRelayTx = await this.getFillRelayTx(deposit, _relayer);
128
+ const computeUnitsConsumed = toBN(await this.computeUnitEstimator(fillRelayTx));
129
+ return computeUnitsConsumed;
130
+ }
131
+
132
+ /**
133
+ * @notice Return the fillRelay transaction for a given deposit
134
+ * @param deposit
135
+ * @param relayer SVM address of the relayer
136
+ * @returns FillRelay transaction
137
+ */
138
+ async getFillRelayTx(
139
+ deposit: Omit<Deposit, "messageHash">,
140
+ _relayer = getDefaultSimulatedRelayerAddress(deposit.destinationChainId)
141
+ ) {
142
+ const relayer = _relayer ? toAddressType(_relayer).forceSvmAddress() : this.simulatedRelayerAddress;
90
143
  // If the user did not have a token account created on destination, then we need to include this as a gas cost.
91
144
  const mint = toAddressType(deposit.outputToken).forceSvmAddress();
92
145
  const owner = toAddressType(deposit.recipient).forceSvmAddress();
93
146
  const associatedToken = await getAssociatedTokenAddress(owner, mint);
94
- const simulatedSigner = SolanaVoidSigner(this.simulatedRelayerAddress.toBase58());
147
+ const simulatedSigner = SolanaVoidSigner(relayer.toBase58());
95
148
 
96
149
  // If the recipient has an associated token account on destination, then skip generating the instruction for creating a new token account.
97
150
  let recipientCreateTokenAccountInstructions: IInstruction[] | undefined = undefined;
@@ -134,7 +187,7 @@ export class SvmQuery implements QueryInterface {
134
187
  const recentBlockhash = await this.provider.getLatestBlockhash().send();
135
188
  const fillRelayTx = pipe(
136
189
  createTransactionMessage({ version: 0 }),
137
- (tx) => setTransactionMessageFeePayer(this.simulatedRelayerAddress.toV2Address(), tx),
190
+ (tx) => setTransactionMessageFeePayer(relayer.toV2Address(), tx),
138
191
  (tx) => setTransactionMessageLifetimeUsingBlockhash(recentBlockhash.value, tx),
139
192
  (tx) =>
140
193
  isDefined(recipientCreateTokenAccountInstructions)
@@ -142,28 +195,7 @@ export class SvmQuery implements QueryInterface {
142
195
  : tx,
143
196
  (tx) => appendTransactionMessageInstructions([createTokenAccountsIx, approveIx, fillIx], tx)
144
197
  );
145
-
146
- const [computeUnitsConsumed, _gasPriceEstimate] = await Promise.all([
147
- toBN(await this.computeUnitEstimator(fillRelayTx)),
148
- getGasPriceEstimate(this.provider, {
149
- unsignedTx: fillRelayTx,
150
- baseFeeMultiplier: options.baseFeeMultiplier,
151
- priorityFeeMultiplier: options.priorityFeeMultiplier,
152
- }),
153
- ]);
154
-
155
- // We can cast the gas price estimate to an SvmGasPriceEstimate here since the oracle should always
156
- // query the Solana adapter.
157
- const gasPriceEstimate = _gasPriceEstimate as SvmGasPriceEstimate;
158
- const gasPrice = gasPriceEstimate.baseFee.add(
159
- gasPriceEstimate.microLamportsPerComputeUnit.mul(computeUnitsConsumed).div(toBN(1_000_000)) // 1_000_000 microLamports/lamport.
160
- );
161
-
162
- return {
163
- nativeGasCost: computeUnitsConsumed,
164
- tokenGasCost: gasPrice,
165
- gasPrice,
166
- };
198
+ return fillRelayTx;
167
199
  }
168
200
 
169
201
  /**
@@ -1,5 +1,9 @@
1
1
  import assert from "assert";
2
- import { DEFAULT_SIMULATED_RELAYER_ADDRESS, TOKEN_SYMBOLS_MAP } from "../constants";
2
+ import {
3
+ DEFAULT_SIMULATED_RELAYER_ADDRESS,
4
+ DEFAULT_SIMULATED_RELAYER_ADDRESS_SVM,
5
+ TOKEN_SYMBOLS_MAP,
6
+ } from "../constants";
3
7
  import { Deposit } from "../interfaces";
4
8
  import {
5
9
  BigNumber,
@@ -18,6 +22,7 @@ import {
18
22
  toBNWei,
19
23
  isZeroAddress,
20
24
  compareAddressesSimple,
25
+ chainIsSvm,
21
26
  } from "../utils";
22
27
  import { Transport } from "viem";
23
28
 
@@ -36,6 +41,7 @@ export interface QueryInterface {
36
41
  }>
37
42
  ) => Promise<TransactionCostEstimate>;
38
43
  getTokenPrice: (tokenSymbol: string) => Promise<number>;
44
+ getNativeGasCost: (deposit: Omit<Deposit, "messageHash">, relayer: string) => Promise<BigNumber>;
39
45
  }
40
46
 
41
47
  export const expectedCapitalCostsKeys = ["lowerBound", "upperBound", "cutoff", "decimals"];
@@ -105,6 +111,12 @@ export const DEFAULT_LOGGER: Logger = {
105
111
  error: (...args) => console.error(args),
106
112
  };
107
113
 
114
+ export function getDefaultSimulatedRelayerAddress(chainId?: number) {
115
+ return isDefined(chainId) && chainIsSvm(chainId)
116
+ ? DEFAULT_SIMULATED_RELAYER_ADDRESS_SVM
117
+ : DEFAULT_SIMULATED_RELAYER_ADDRESS;
118
+ }
119
+
108
120
  // Small amount to simulate filling with. Should be low enough to guarantee a successful fill.
109
121
  const safeOutputAmount = toBN(100);
110
122
 
@@ -241,7 +253,7 @@ export class RelayFeeCalculator {
241
253
  deposit: Deposit,
242
254
  amountToRelay: BigNumberish,
243
255
  simulateZeroFill = false,
244
- relayerAddress = DEFAULT_SIMULATED_RELAYER_ADDRESS,
256
+ relayerAddress = getDefaultSimulatedRelayerAddress(deposit.destinationChainId),
245
257
  _tokenPrice?: number,
246
258
  tokenMapping = TOKEN_SYMBOLS_MAP,
247
259
  gasPrice?: BigNumberish,
@@ -480,7 +492,7 @@ export class RelayFeeCalculator {
480
492
  deposit: Deposit,
481
493
  amountToRelay?: BigNumberish,
482
494
  simulateZeroFill = false,
483
- relayerAddress = DEFAULT_SIMULATED_RELAYER_ADDRESS,
495
+ relayerAddress = getDefaultSimulatedRelayerAddress(deposit.destinationChainId),
484
496
  _tokenPrice?: number,
485
497
  gasPrice?: BigNumberish,
486
498
  gasUnits?: BigNumberish,
@@ -1,4 +1,4 @@
1
- import { encodeAbiParameters, keccak256 } from "viem";
1
+ import { encodeAbiParameters, Hex, keccak256 } from "viem";
2
2
  import { MAX_SAFE_DEPOSIT_ID, ZERO_ADDRESS, ZERO_BYTES } from "../constants";
3
3
  import { Deposit, RelayData } from "../interfaces";
4
4
  import { toBytes32 } from "./AddressUtils";
@@ -91,5 +91,5 @@ export function isZeroAddress(address: string): boolean {
91
91
  }
92
92
 
93
93
  export function getMessageHash(message: string): string {
94
- return isMessageEmpty(message) ? ZERO_BYTES : keccak256(message as "0x{string}");
94
+ return isMessageEmpty(message) ? ZERO_BYTES : keccak256(message as Hex);
95
95
  }