@across-protocol/sdk 4.3.37 → 4.3.40

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 (147) hide show
  1. package/dist/cjs/arch/svm/BlockUtils.d.ts +3 -1
  2. package/dist/cjs/arch/svm/BlockUtils.js +3 -2
  3. package/dist/cjs/arch/svm/BlockUtils.js.map +1 -1
  4. package/dist/cjs/arch/svm/SpokeUtils.d.ts +9 -8
  5. package/dist/cjs/arch/svm/SpokeUtils.js +106 -130
  6. package/dist/cjs/arch/svm/SpokeUtils.js.map +1 -1
  7. package/dist/cjs/arch/svm/provider.d.ts +1 -1
  8. package/dist/cjs/arch/svm/provider.js +2 -1
  9. package/dist/cjs/arch/svm/provider.js.map +1 -1
  10. package/dist/cjs/arch/svm/utils.d.ts +5 -2
  11. package/dist/cjs/arch/svm/utils.js +23 -6
  12. package/dist/cjs/arch/svm/utils.js.map +1 -1
  13. package/dist/cjs/clients/BaseAbstractClient.d.ts +3 -1
  14. package/dist/cjs/clients/BaseAbstractClient.js +31 -13
  15. package/dist/cjs/clients/BaseAbstractClient.js.map +1 -1
  16. package/dist/cjs/clients/BundleDataClient/BundleDataClient.js +1 -1
  17. package/dist/cjs/clients/BundleDataClient/BundleDataClient.js.map +1 -1
  18. package/dist/cjs/clients/BundleDataClient/utils/PoolRebalanceUtils.js +1 -1
  19. package/dist/cjs/clients/BundleDataClient/utils/PoolRebalanceUtils.js.map +1 -1
  20. package/dist/cjs/clients/SpokePoolClient/SVMSpokePoolClient.js +5 -5
  21. package/dist/cjs/clients/SpokePoolClient/SVMSpokePoolClient.js.map +1 -1
  22. package/dist/cjs/providers/mocks/MockCachedSolanaRpcFactory.d.ts +1 -1
  23. package/dist/cjs/providers/mocks/MockCachedSolanaRpcFactory.js +2 -2
  24. package/dist/cjs/providers/mocks/MockCachedSolanaRpcFactory.js.map +1 -1
  25. package/dist/cjs/providers/mocks/MockRateLimitedSolanaRpcFactory.d.ts +1 -1
  26. package/dist/cjs/providers/mocks/MockRateLimitedSolanaRpcFactory.js +2 -2
  27. package/dist/cjs/providers/mocks/MockRateLimitedSolanaRpcFactory.js.map +1 -1
  28. package/dist/cjs/providers/mocks/MockRetrySolanaRpcFactory.d.ts +1 -1
  29. package/dist/cjs/providers/mocks/MockRetrySolanaRpcFactory.js +2 -2
  30. package/dist/cjs/providers/mocks/MockRetrySolanaRpcFactory.js.map +1 -1
  31. package/dist/cjs/providers/solana/baseRpcFactories.d.ts +3 -3
  32. package/dist/cjs/providers/solana/baseRpcFactories.js +4 -8
  33. package/dist/cjs/providers/solana/baseRpcFactories.js.map +1 -1
  34. package/dist/cjs/providers/solana/cachedRpcFactory.js.map +1 -1
  35. package/dist/cjs/providers/solana/index.d.ts +1 -0
  36. package/dist/cjs/providers/solana/index.js +1 -0
  37. package/dist/cjs/providers/solana/index.js.map +1 -1
  38. package/dist/cjs/providers/solana/quorumFallbackRpcFactory.d.ts +16 -0
  39. package/dist/cjs/providers/solana/quorumFallbackRpcFactory.js +208 -0
  40. package/dist/cjs/providers/solana/quorumFallbackRpcFactory.js.map +1 -0
  41. package/dist/cjs/providers/utils.d.ts +1 -0
  42. package/dist/cjs/providers/utils.js +5 -1
  43. package/dist/cjs/providers/utils.js.map +1 -1
  44. package/dist/cjs/utils/BlockExplorerUtils.js +1 -1
  45. package/dist/cjs/utils/BlockExplorerUtils.js.map +1 -1
  46. package/dist/esm/arch/svm/BlockUtils.d.ts +3 -1
  47. package/dist/esm/arch/svm/BlockUtils.js +3 -2
  48. package/dist/esm/arch/svm/BlockUtils.js.map +1 -1
  49. package/dist/esm/arch/svm/SpokeUtils.d.ts +9 -8
  50. package/dist/esm/arch/svm/SpokeUtils.js +109 -131
  51. package/dist/esm/arch/svm/SpokeUtils.js.map +1 -1
  52. package/dist/esm/arch/svm/provider.d.ts +1 -1
  53. package/dist/esm/arch/svm/provider.js +1 -1
  54. package/dist/esm/arch/svm/provider.js.map +1 -1
  55. package/dist/esm/arch/svm/utils.d.ts +11 -2
  56. package/dist/esm/arch/svm/utils.js +28 -6
  57. package/dist/esm/arch/svm/utils.js.map +1 -1
  58. package/dist/esm/clients/BaseAbstractClient.d.ts +3 -1
  59. package/dist/esm/clients/BaseAbstractClient.js +31 -13
  60. package/dist/esm/clients/BaseAbstractClient.js.map +1 -1
  61. package/dist/esm/clients/BundleDataClient/BundleDataClient.js +1 -1
  62. package/dist/esm/clients/BundleDataClient/BundleDataClient.js.map +1 -1
  63. package/dist/esm/clients/BundleDataClient/utils/PoolRebalanceUtils.js +1 -1
  64. package/dist/esm/clients/BundleDataClient/utils/PoolRebalanceUtils.js.map +1 -1
  65. package/dist/esm/clients/SpokePoolClient/SVMSpokePoolClient.js +5 -5
  66. package/dist/esm/clients/SpokePoolClient/SVMSpokePoolClient.js.map +1 -1
  67. package/dist/esm/pool/poolClient.js +1 -1
  68. package/dist/esm/providers/mocks/MockCachedSolanaRpcFactory.d.ts +1 -1
  69. package/dist/esm/providers/mocks/MockCachedSolanaRpcFactory.js +1 -1
  70. package/dist/esm/providers/mocks/MockCachedSolanaRpcFactory.js.map +1 -1
  71. package/dist/esm/providers/mocks/MockRateLimitedSolanaRpcFactory.d.ts +1 -1
  72. package/dist/esm/providers/mocks/MockRateLimitedSolanaRpcFactory.js +1 -1
  73. package/dist/esm/providers/mocks/MockRateLimitedSolanaRpcFactory.js.map +1 -1
  74. package/dist/esm/providers/mocks/MockRetrySolanaRpcFactory.d.ts +1 -1
  75. package/dist/esm/providers/mocks/MockRetrySolanaRpcFactory.js +1 -1
  76. package/dist/esm/providers/mocks/MockRetrySolanaRpcFactory.js.map +1 -1
  77. package/dist/esm/providers/solana/baseRpcFactories.d.ts +3 -3
  78. package/dist/esm/providers/solana/baseRpcFactories.js +4 -8
  79. package/dist/esm/providers/solana/baseRpcFactories.js.map +1 -1
  80. package/dist/esm/providers/solana/cachedRpcFactory.js +2 -0
  81. package/dist/esm/providers/solana/cachedRpcFactory.js.map +1 -1
  82. package/dist/esm/providers/solana/index.d.ts +1 -0
  83. package/dist/esm/providers/solana/index.js +1 -0
  84. package/dist/esm/providers/solana/index.js.map +1 -1
  85. package/dist/esm/providers/solana/quorumFallbackRpcFactory.d.ts +16 -0
  86. package/dist/esm/providers/solana/quorumFallbackRpcFactory.js +225 -0
  87. package/dist/esm/providers/solana/quorumFallbackRpcFactory.js.map +1 -0
  88. package/dist/esm/providers/utils.d.ts +1 -0
  89. package/dist/esm/providers/utils.js +3 -0
  90. package/dist/esm/providers/utils.js.map +1 -1
  91. package/dist/esm/utils/AddressUtils.js +1 -1
  92. package/dist/esm/utils/AddressUtils.js.map +1 -1
  93. package/dist/esm/utils/BlockExplorerUtils.js +1 -1
  94. package/dist/esm/utils/BlockExplorerUtils.js.map +1 -1
  95. package/dist/esm/utils/abi/index.d.ts +1 -1
  96. package/dist/esm/utils/abi/index.js +1 -1
  97. package/dist/types/arch/svm/BlockUtils.d.ts +3 -1
  98. package/dist/types/arch/svm/BlockUtils.d.ts.map +1 -1
  99. package/dist/types/arch/svm/SpokeUtils.d.ts +9 -8
  100. package/dist/types/arch/svm/SpokeUtils.d.ts.map +1 -1
  101. package/dist/types/arch/svm/provider.d.ts +1 -1
  102. package/dist/types/arch/svm/provider.d.ts.map +1 -1
  103. package/dist/types/arch/svm/utils.d.ts +11 -2
  104. package/dist/types/arch/svm/utils.d.ts.map +1 -1
  105. package/dist/types/clients/BaseAbstractClient.d.ts +3 -1
  106. package/dist/types/clients/BaseAbstractClient.d.ts.map +1 -1
  107. package/dist/types/clients/BundleDataClient/BundleDataClient.d.ts.map +1 -1
  108. package/dist/types/clients/BundleDataClient/utils/PoolRebalanceUtils.d.ts.map +1 -1
  109. package/dist/types/providers/mocks/MockCachedSolanaRpcFactory.d.ts +1 -1
  110. package/dist/types/providers/mocks/MockCachedSolanaRpcFactory.d.ts.map +1 -1
  111. package/dist/types/providers/mocks/MockRateLimitedSolanaRpcFactory.d.ts +1 -1
  112. package/dist/types/providers/mocks/MockRateLimitedSolanaRpcFactory.d.ts.map +1 -1
  113. package/dist/types/providers/mocks/MockRetrySolanaRpcFactory.d.ts +1 -1
  114. package/dist/types/providers/mocks/MockRetrySolanaRpcFactory.d.ts.map +1 -1
  115. package/dist/types/providers/solana/baseRpcFactories.d.ts +3 -3
  116. package/dist/types/providers/solana/baseRpcFactories.d.ts.map +1 -1
  117. package/dist/types/providers/solana/cachedRpcFactory.d.ts.map +1 -1
  118. package/dist/types/providers/solana/index.d.ts +1 -0
  119. package/dist/types/providers/solana/index.d.ts.map +1 -1
  120. package/dist/types/providers/solana/quorumFallbackRpcFactory.d.ts +17 -0
  121. package/dist/types/providers/solana/quorumFallbackRpcFactory.d.ts.map +1 -0
  122. package/dist/types/providers/utils.d.ts +1 -0
  123. package/dist/types/providers/utils.d.ts.map +1 -1
  124. package/dist/types/utils/abi/index.d.ts +1 -1
  125. package/package.json +1 -1
  126. package/src/arch/svm/BlockUtils.ts +3 -1
  127. package/src/arch/svm/SpokeUtils.ts +100 -137
  128. package/src/arch/svm/provider.ts +1 -0
  129. package/src/arch/svm/utils.ts +30 -4
  130. package/src/caching/Arweave/ArweaveClient.ts +1 -1
  131. package/src/clients/BaseAbstractClient.ts +24 -8
  132. package/src/clients/BundleDataClient/BundleDataClient.ts +1 -0
  133. package/src/clients/BundleDataClient/utils/PoolRebalanceUtils.ts +5 -1
  134. package/src/clients/SpokePoolClient/SVMSpokePoolClient.ts +5 -5
  135. package/src/pool/poolClient.ts +1 -1
  136. package/src/providers/mocks/MockCachedSolanaRpcFactory.ts +1 -1
  137. package/src/providers/mocks/MockRateLimitedSolanaRpcFactory.ts +1 -1
  138. package/src/providers/mocks/MockRetrySolanaRpcFactory.ts +1 -1
  139. package/src/providers/rateLimitedProvider.ts +1 -1
  140. package/src/providers/solana/baseRpcFactories.ts +3 -3
  141. package/src/providers/solana/cachedRpcFactory.ts +2 -0
  142. package/src/providers/solana/index.ts +1 -0
  143. package/src/providers/solana/quorumFallbackRpcFactory.ts +248 -0
  144. package/src/providers/utils.ts +4 -0
  145. package/src/utils/AddressUtils.ts +1 -1
  146. package/src/utils/BlockExplorerUtils.ts +1 -1
  147. package/src/utils/abi/index.ts +1 -1
@@ -1,7 +1,6 @@
1
1
  import { MessageTransmitterClient, SvmSpokeClient, TokenMessengerMinterClient } from "@across-protocol/contracts";
2
2
  import { decodeFillStatusAccount, fetchState } from "@across-protocol/contracts/dist/src/svm/clients/SvmSpoke";
3
3
  import { decodeMessageHeader, hashNonEmptyMessage } from "@across-protocol/contracts/dist/src/svm/web3-v1";
4
- import { intToU8Array32 } from "@across-protocol/contracts/dist/src/svm/web3-v1/conversionUtils";
5
4
  import { SYSTEM_PROGRAM_ADDRESS } from "@solana-program/system";
6
5
  import {
7
6
  ASSOCIATED_TOKEN_PROGRAM_ADDRESS,
@@ -32,10 +31,11 @@ import {
32
31
  signTransactionMessageWithSigners,
33
32
  some,
34
33
  type TransactionSigner,
34
+ type Commitment,
35
35
  } from "@solana/kit";
36
36
  import assert from "assert";
37
- import { arrayify, hexZeroPad, hexlify } from "ethers/lib/utils";
38
- import { Logger } from "winston";
37
+ import winston from "winston";
38
+ import { arrayify } from "ethers/lib/utils";
39
39
  import { CHAIN_IDs, TOKEN_SYMBOLS_MAP } from "../../constants";
40
40
  import { DepositWithBlock, FillStatus, FillWithBlock, RelayData, RelayExecutionEventInfo } from "../../interfaces";
41
41
  import {
@@ -54,7 +54,6 @@ import {
54
54
  toAddressType,
55
55
  } from "../../utils";
56
56
  import {
57
- bigToU8a32,
58
57
  createDefaultTransaction,
59
58
  getCCTPNoncePda,
60
59
  getEventAuthority,
@@ -66,6 +65,7 @@ import {
66
65
  toAddress,
67
66
  unwrapEventData,
68
67
  getRootBundlePda,
68
+ toSvmRelayData,
69
69
  } from "./";
70
70
  import { SvmCpiEventsClient } from "./eventsClient";
71
71
  import { SVM_BLOCK_NOT_AVAILABLE, SVM_SLOT_SKIPPED, isSolanaError } from "./provider";
@@ -89,22 +89,51 @@ type ProtoFill = Omit<RelayData, "recipient" | "outputToken"> & {
89
89
  outputToken: SvmAddress;
90
90
  };
91
91
 
92
+ export function getSlot(provider: SVMProvider, commitment: Commitment, logger: winston.Logger): Promise<bigint> {
93
+ return _callGetSlotWithRetry(provider, commitment, logger);
94
+ }
95
+
96
+ async function _callGetSlotWithRetry(
97
+ provider: SVMProvider,
98
+ commitment: Commitment,
99
+ logger: winston.Logger
100
+ ): Promise<bigint> {
101
+ try {
102
+ return await provider.getSlot({ commitment }).send();
103
+ } catch (err) {
104
+ if (isSolanaError(err)) {
105
+ const { __code: code } = err.context;
106
+ logger.debug({
107
+ at: "_getSlotWithRetry",
108
+ message: "Caught error from getSlot()",
109
+ code,
110
+ commitment,
111
+ });
112
+ }
113
+
114
+ // TODO: Implement retry logic once we better understand how these errors look:
115
+ throw err;
116
+ }
117
+ }
118
+
92
119
  /**
93
120
  * Retrieves the chain time at a particular slot.
94
121
  */
95
122
  export function getTimestampForSlot(
96
123
  provider: SVMProvider,
97
124
  slotNumber: bigint,
125
+ logger: winston.Logger,
98
126
  maxRetries = 2
99
127
  ): Promise<number | undefined> {
100
- return _callGetTimestampForSlotWithRetry(provider, slotNumber, 0, maxRetries);
128
+ return _callGetTimestampForSlotWithRetry(provider, slotNumber, 0, maxRetries, logger);
101
129
  }
102
130
 
103
131
  async function _callGetTimestampForSlotWithRetry(
104
132
  provider: SVMProvider,
105
133
  slotNumber: bigint,
106
134
  retryAttempt: number,
107
- maxRetries: number
135
+ maxRetries: number,
136
+ logger: winston.Logger
108
137
  ): Promise<number | undefined> {
109
138
  // @note: getBlockTime receives a slot number, not a block number.
110
139
  let _timestamp: bigint;
@@ -118,6 +147,7 @@ async function _callGetTimestampForSlotWithRetry(
118
147
 
119
148
  const { __code: code } = err.context;
120
149
  const slot = slotNumber.toString();
150
+
121
151
  switch (err.context.__code) {
122
152
  case SVM_SLOT_SKIPPED:
123
153
  return undefined;
@@ -129,11 +159,27 @@ async function _callGetTimestampForSlotWithRetry(
129
159
  if (retryAttempt >= maxRetries) {
130
160
  throw new Error(`Timeout on SVM getBlockTime() for slot ${slot} after ${retryAttempt} retry attempts`);
131
161
  }
162
+ logger.debug({
163
+ at: "getTimestampForSlot",
164
+ message: `Retrying getBlockTime() after ${delaySeconds} seconds for retry attempt #${retryAttempt}`,
165
+ slot,
166
+ retryAttempt,
167
+ maxRetries,
168
+ delaySeconds,
169
+ });
132
170
  await delay(delaySeconds);
133
- return _callGetTimestampForSlotWithRetry(provider, slotNumber, ++retryAttempt, maxRetries);
171
+ return _callGetTimestampForSlotWithRetry(provider, slotNumber, ++retryAttempt, maxRetries, logger);
134
172
  }
135
173
 
136
174
  default:
175
+ logger.debug({
176
+ at: "getTimestampForSlot",
177
+ message: "Caught error from getBlockTime()",
178
+ errorCode: code,
179
+ slot,
180
+ retryAttempt,
181
+ maxRetries,
182
+ });
137
183
  throw new Error(`Unhandled SVM getBlockTime() error for slot ${slot}: ${code}`, { cause: err });
138
184
  }
139
185
  }
@@ -197,6 +243,7 @@ export function getDepositIdAtBlock(_contract: unknown, _blockTag: number): Prom
197
243
  export async function findDeposit(
198
244
  eventClient: SvmCpiEventsClient,
199
245
  depositId: BigNumber,
246
+ logger: winston.Logger,
200
247
  slot?: bigint,
201
248
  secondsLookback = 2 * 24 * 60 * 60 // 2 days
202
249
  ): Promise<DepositWithBlock | undefined> {
@@ -206,7 +253,7 @@ export async function findDeposit(
206
253
  }
207
254
 
208
255
  const provider = eventClient.getRpc();
209
- const { slot: currentSlot } = await getNearestSlotTime(provider);
256
+ const { slot: currentSlot } = await getNearestSlotTime(provider, logger);
210
257
 
211
258
  // If no slot is provided, use the current slot
212
259
  // If a slot is provided, ensure it's not in the future
@@ -264,6 +311,7 @@ export async function relayFillStatus(
264
311
  relayData: RelayData,
265
312
  destinationChainId: number,
266
313
  svmEventsClient: SvmCpiEventsClient,
314
+ logger: winston.Logger,
267
315
  atHeight?: number
268
316
  ): Promise<FillStatus> {
269
317
  assert(chainIsSvm(destinationChainId), "Destination chain must be an SVM chain");
@@ -277,7 +325,7 @@ export async function relayFillStatus(
277
325
  const commitment = "confirmed";
278
326
  const [fillStatusAccount, { slot: currentSlot, timestamp }] = await Promise.all([
279
327
  fetchEncodedAccount(provider, fillStatusPda, { commitment }),
280
- getNearestSlotTime(provider, { commitment }),
328
+ getNearestSlotTime(provider, logger, { commitment }),
281
329
  ]);
282
330
  toSlot = currentSlot;
283
331
 
@@ -314,8 +362,8 @@ export async function fillStatusArray(
314
362
  relayData: RelayData[],
315
363
  destinationChainId: number,
316
364
  svmEventsClient: SvmCpiEventsClient,
317
- atHeight?: number,
318
- logger?: Logger
365
+ logger: winston.Logger,
366
+ atHeight?: number
319
367
  ): Promise<(FillStatus | undefined)[]> {
320
368
  assert(chainIsSvm(destinationChainId), "Destination chain must be an SVM chain");
321
369
  const provider = svmEventsClient.getRpc();
@@ -343,7 +391,7 @@ export async function fillStatusArray(
343
391
  // Otherwise, initialize all statuses as undefined
344
392
  const fillStatuses: (FillStatus | undefined)[] =
345
393
  atHeight === undefined
346
- ? await fetchBatchFillStatusFromPdaAccounts(provider, fillStatusPdas, relayData)
394
+ ? await fetchBatchFillStatusFromPdaAccounts(provider, fillStatusPdas, relayData, logger)
347
395
  : new Array(relayData.length).fill(undefined);
348
396
 
349
397
  // Collect indices of deposits that still need their status resolved
@@ -359,7 +407,7 @@ export async function fillStatusArray(
359
407
  const missingResults: { index: number; fillStatus: FillStatus }[] = [];
360
408
 
361
409
  // Determine the toSlot to use for event reconstruction
362
- const toSlot = atHeight ? BigInt(atHeight) : (await getNearestSlotTime(provider)).slot;
410
+ const toSlot = atHeight ? BigInt(atHeight) : (await getNearestSlotTime(provider, logger)).slot;
363
411
 
364
412
  // @note: This path is mostly used for deposits past their fill deadline.
365
413
  // If it becomes a bottleneck, consider returning an "Unknown" status that can be handled downstream.
@@ -397,11 +445,12 @@ export async function findFillEvent(
397
445
  relayData: RelayData,
398
446
  destinationChainId: number,
399
447
  svmEventsClient: SvmCpiEventsClient,
448
+ logger: winston.Logger,
400
449
  fromSlot: number,
401
450
  toSlot?: number
402
451
  ): Promise<FillWithBlock | undefined> {
403
452
  assert(chainIsSvm(destinationChainId), "Destination chain must be an SVM chain");
404
- toSlot ??= Number((await getNearestSlotTime(svmEventsClient.getRpc())).slot);
453
+ toSlot ??= Number((await getNearestSlotTime(svmEventsClient.getRpc(), logger)).slot);
405
454
 
406
455
  // Get fillStatus PDA using relayData
407
456
  const programId = svmEventsClient.getProgramAddress();
@@ -465,11 +514,10 @@ export async function fillRelayInstruction(
465
514
  relayData: ProtoFill,
466
515
  signer: TransactionSigner<string>,
467
516
  recipientTokenAccount: Address<string>,
468
- repaymentAddress: EvmAddress | SvmAddress = SvmAddress.from(signer.address),
469
- repaymentChainId = relayData.destinationChainId
517
+ repaymentAddress: EvmAddress | SvmAddress,
518
+ repaymentChainId: number
470
519
  ) {
471
520
  const program = toAddress(spokePool);
472
-
473
521
  assert(
474
522
  repaymentAddress.isValidOn(repaymentChainId),
475
523
  `Invalid repayment address for chain ${repaymentChainId}: ${repaymentAddress.toNative()}.`
@@ -480,61 +528,29 @@ export async function fillRelayInstruction(
480
528
 
481
529
  const relayer = SvmAddress.from(signer.address);
482
530
 
483
- // Create ATA for the relayer and recipient token accounts
484
- const relayerTokenAccount = await getAssociatedTokenAddress(relayer, relayData.outputToken);
485
-
486
- const [statePda, fillStatusPda, eventAuthority] = await Promise.all([
531
+ const [statePda, fillStatusPda, eventAuthority, delegatePda, relayerTokenAccount] = await Promise.all([
487
532
  getStatePda(program),
488
533
  getFillStatusPda(program, relayData, relayData.destinationChainId),
489
534
  getEventAuthority(program),
535
+ getFillRelayDelegatePda(relayDataHash, BigInt(repaymentChainId), signer.address, program),
536
+ getAssociatedTokenAddress(relayer, relayData.outputToken),
490
537
  ]);
491
538
 
492
- const depositIdBuffer = new Uint8Array(32);
493
- const shortenedBuffer = new Uint8Array(Buffer.from(relayData.depositId.toHexString().slice(2), "hex"));
494
- depositIdBuffer.set(shortenedBuffer, 32 - shortenedBuffer.length);
495
-
496
- const delegatePda = await getFillRelayDelegatePda(
497
- relayDataHash,
498
- BigInt(repaymentChainId),
499
- toAddress(relayer),
500
- program
501
- );
502
-
503
- const [recipient, outputToken, exclusiveRelayer, depositor, inputToken] = [
504
- relayData.recipient,
505
- relayData.outputToken,
506
- relayData.exclusiveRelayer,
507
- relayData.depositor,
508
- relayData.inputToken,
509
- ].map(toAddress);
510
-
539
+ const svmRelayData = toSvmRelayData(relayData);
511
540
  return SvmSpokeClient.getFillRelayInstruction({
512
541
  signer,
513
542
  state: statePda,
514
543
  delegate: toAddress(SvmAddress.from(delegatePda.toString())),
515
- mint: outputToken,
544
+ mint: svmRelayData.outputToken,
516
545
  relayerTokenAccount: relayerTokenAccount,
517
546
  recipientTokenAccount: recipientTokenAccount,
518
547
  fillStatus: fillStatusPda,
519
548
  eventAuthority,
520
549
  program,
521
550
  relayHash: relayDataHash,
522
- relayData: some({
523
- depositor,
524
- recipient,
525
- exclusiveRelayer,
526
- inputToken,
527
- outputToken,
528
- inputAmount: bigToU8a32(relayData.inputAmount.toBigInt()),
529
- outputAmount: relayData.outputAmount.toBigInt(),
530
- originChainId: BigInt(relayData.originChainId),
531
- fillDeadline: relayData.fillDeadline,
532
- exclusivityDeadline: relayData.exclusivityDeadline,
533
- depositId: depositIdBuffer,
534
- message: new Uint8Array(Buffer.from(relayData.message.slice(2), "hex")),
535
- }),
551
+ relayData: some(svmRelayData),
536
552
  repaymentChainId: some(BigInt(repaymentChainId)),
537
- repaymentAddress: toAddress(repaymentAddress),
553
+ repaymentAddress: some(toAddress(repaymentAddress)),
538
554
  });
539
555
  }
540
556
 
@@ -575,7 +591,7 @@ export async function getFillRelayTx(
575
591
  repaymentChainId: number,
576
592
  repaymentAddress: SdkAddress
577
593
  ) {
578
- const { depositor, recipient, inputToken, outputToken, exclusiveRelayer, destinationChainId } = relayData;
594
+ const svmRelayData = toSvmRelayData(relayData);
579
595
 
580
596
  assert(
581
597
  repaymentAddress.isValidOn(repaymentChainId),
@@ -583,44 +599,27 @@ export async function getFillRelayTx(
583
599
  );
584
600
 
585
601
  const program = toAddress(spokePoolAddr);
586
- const _relayDataHash = getRelayDataHash(relayData, destinationChainId);
602
+ const _relayDataHash = getRelayDataHash(relayData, relayData.destinationChainId);
587
603
  const relayDataHash = new Uint8Array(Buffer.from(_relayDataHash.slice(2), "hex"));
588
604
 
589
- const [state, delegate] = await Promise.all([
605
+ const [state, delegate, mintInfo, fillStatus, eventAuthority] = await Promise.all([
590
606
  getStatePda(program),
591
607
  getFillRelayDelegatePda(relayDataHash, BigInt(repaymentChainId), toAddress(repaymentAddress), program),
592
- ]);
593
-
594
- const mint = toAddress(outputToken);
595
- const mintInfo = await getMintInfo(solanaClient, mint);
596
-
597
- const [recipientAta, relayerAta, fillStatus, eventAuthority] = await Promise.all([
598
- getAssociatedTokenAddress(recipient, outputToken, mintInfo.programAddress),
599
- getAssociatedTokenAddress(SvmAddress.from(signer.address), outputToken, mintInfo.programAddress),
600
- getFillStatusPda(program, relayData, destinationChainId),
608
+ getMintInfo(solanaClient, svmRelayData.outputToken),
609
+ getFillStatusPda(program, relayData, relayData.destinationChainId),
601
610
  getEventAuthority(program),
602
611
  ]);
603
612
 
604
- const svmRelayData: SvmSpokeClient.FillRelayInput["relayData"] = {
605
- depositor: toAddress(depositor),
606
- recipient: toAddress(recipient),
607
- exclusiveRelayer: toAddress(exclusiveRelayer),
608
- inputToken: toAddress(inputToken),
609
- outputToken: mint,
610
- inputAmount: bigToU8a32(relayData.inputAmount.toBigInt()),
611
- outputAmount: relayData.outputAmount.toBigInt(),
612
- originChainId: relayData.originChainId,
613
- depositId: new Uint8Array(intToU8Array32(relayData.depositId.toNumber())),
614
- fillDeadline: relayData.fillDeadline,
615
- exclusivityDeadline: relayData.exclusivityDeadline,
616
- message: new Uint8Array(Buffer.from(relayData.message, "hex")),
617
- };
613
+ const [recipientAta, relayerAta] = await Promise.all([
614
+ getAssociatedTokenAddress(relayData.recipient, relayData.outputToken, mintInfo.programAddress),
615
+ getAssociatedTokenAddress(SvmAddress.from(signer.address), relayData.outputToken, mintInfo.programAddress),
616
+ ]);
618
617
 
619
618
  const fillInput: SvmSpokeClient.FillRelayInput = {
620
619
  signer: signer,
621
620
  state,
622
621
  delegate,
623
- mint,
622
+ mint: svmRelayData.outputToken,
624
623
  relayerTokenAccount: relayerAta,
625
624
  recipientTokenAccount: recipientAta,
626
625
  fillStatus,
@@ -777,48 +776,16 @@ export async function getSlowFillRequestTx(
777
776
  },
778
777
  signer: TransactionSigner
779
778
  ) {
780
- const {
781
- depositor,
782
- recipient,
783
- inputToken,
784
- outputToken,
785
- exclusiveRelayer,
786
- destinationChainId,
787
- originChainId,
788
- depositId,
789
- fillDeadline,
790
- exclusivityDeadline,
791
- message,
792
- } = relayData;
793
-
794
779
  const program = toAddress(spokePoolAddr);
795
- const relayDataHash = getRelayDataHash(relayData, destinationChainId);
780
+ const relayDataHash = getRelayDataHash(relayData, relayData.destinationChainId);
796
781
 
797
782
  const [state, fillStatus, eventAuthority] = await Promise.all([
798
783
  getStatePda(program),
799
- getFillStatusPda(program, relayData, destinationChainId),
784
+ getFillStatusPda(program, relayData, relayData.destinationChainId),
800
785
  getEventAuthority(program),
801
786
  ]);
802
787
 
803
- const depositIdBuffer = new Uint8Array(32);
804
- const shortenedBuffer = arrayify(depositId.toHexString());
805
- depositIdBuffer.set(shortenedBuffer, 32 - shortenedBuffer.length);
806
-
807
- const relayDataInput: SvmSpokeClient.RequestSlowFillInput["relayData"] = {
808
- depositor: toAddress(depositor),
809
- recipient: toAddress(recipient),
810
- exclusiveRelayer: toAddress(exclusiveRelayer),
811
- inputToken: toAddress(inputToken),
812
- outputToken: toAddress(outputToken),
813
- inputAmount: bigToU8a32(relayData.inputAmount.toBigInt()),
814
- outputAmount: relayData.outputAmount.toBigInt(),
815
- originChainId: BigInt(originChainId),
816
- depositId: depositIdBuffer,
817
- fillDeadline: fillDeadline,
818
- exclusivityDeadline: exclusivityDeadline,
819
- message: arrayify(message),
820
- };
821
-
788
+ const svmRelayData = toSvmRelayData(relayData);
822
789
  const requestSlowFillInput: SvmSpokeClient.RequestSlowFillInput = {
823
790
  signer,
824
791
  state,
@@ -826,7 +793,7 @@ export async function getSlowFillRequestTx(
826
793
  eventAuthority,
827
794
  program,
828
795
  relayHash: arrayify(relayDataHash),
829
- relayData: relayDataInput,
796
+ relayData: svmRelayData,
830
797
  systemProgram: SYSTEM_PROGRAM_ADDRESS,
831
798
  };
832
799
 
@@ -882,28 +849,23 @@ export async function getAssociatedTokenAddress(
882
849
  }
883
850
 
884
851
  export function getRelayDataHash(relayData: RelayData, destinationChainId: number): string {
885
- const addressEncoder = getAddressEncoder();
852
+ assert(relayData.message.startsWith("0x"), "Message must be a hex string");
886
853
  const uint64Encoder = getU64Encoder();
887
- const uint32Encoder = getU32Encoder();
888
854
 
889
- assert(relayData.message.startsWith("0x"), "Message must be a hex string");
890
- const encodeAddress = (data: SdkAddress) => Uint8Array.from(addressEncoder.encode(toAddress(data)));
855
+ const svmRelayData = toSvmRelayData(relayData);
856
+ const relayDataEncoder = SvmSpokeClient.getRelayDataEncoder();
857
+ const encodedRelayData = relayDataEncoder.encode(svmRelayData);
858
+ const encodedMessage = Buffer.from(relayData.message.slice(2), "hex");
891
859
 
860
+ // Reformat the encoded relay data the same way it is done in the SvmSpoke:
861
+ // https://github.com/across-protocol/contracts/blob/3310f8dc716407a5f97ef5fd2eae63df83251f2f/programs/svm-spoke/src/utils/merkle_proof_utils.rs#L5
862
+ const messageOffset = encodedRelayData.length - 4 - encodedMessage.length;
892
863
  const contentToHash = Buffer.concat([
893
- encodeAddress(relayData.depositor),
894
- encodeAddress(relayData.recipient),
895
- encodeAddress(relayData.exclusiveRelayer),
896
- encodeAddress(relayData.inputToken),
897
- encodeAddress(relayData.outputToken),
898
- arrayify(hexZeroPad(hexlify(relayData.inputAmount), 32)),
899
- Uint8Array.from(uint64Encoder.encode(BigInt(relayData.outputAmount.toString()))),
900
- Uint8Array.from(uint64Encoder.encode(BigInt(relayData.originChainId.toString()))),
901
- arrayify(hexZeroPad(hexlify(relayData.depositId), 32)),
902
- Uint8Array.from(uint32Encoder.encode(relayData.fillDeadline)),
903
- Uint8Array.from(uint32Encoder.encode(relayData.exclusivityDeadline)),
904
- hashNonEmptyMessage(Buffer.from(arrayify(relayData.message))),
864
+ encodedRelayData.slice(0, messageOffset),
865
+ hashNonEmptyMessage(encodedMessage),
905
866
  Uint8Array.from(uint64Encoder.encode(BigInt(destinationChainId))),
906
867
  ]);
868
+
907
869
  return keccak256(contentToHash);
908
870
  }
909
871
 
@@ -961,14 +923,15 @@ async function resolveFillStatusFromPdaEvents(
961
923
  async function fetchBatchFillStatusFromPdaAccounts(
962
924
  provider: SVMProvider,
963
925
  fillStatusPdas: Address[],
964
- relayDataArray: RelayData[]
926
+ relayDataArray: RelayData[],
927
+ logger: winston.Logger
965
928
  ): Promise<(FillStatus | undefined)[]> {
966
929
  const chunkSize = 100; // SVM method getMultipleAccounts allows a max of 100 addresses per request
967
930
  const commitment = "confirmed";
968
931
 
969
932
  const [pdaAccounts, { timestamp }] = await Promise.all([
970
933
  Promise.all(chunk(fillStatusPdas, chunkSize).map((chunk) => fetchEncodedAccounts(provider, chunk, { commitment }))),
971
- getNearestSlotTime(provider, { commitment }),
934
+ getNearestSlotTime(provider, logger, { commitment }),
972
935
  ]);
973
936
 
974
937
  const fillStatuses = pdaAccounts.flat().map((account, index) => {
@@ -6,4 +6,5 @@ export {
6
6
  isSolanaError,
7
7
  SOLANA_ERROR__JSON_RPC__SERVER_ERROR_BLOCK_NOT_AVAILABLE as SVM_BLOCK_NOT_AVAILABLE,
8
8
  SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SLOT_SKIPPED as SVM_SLOT_SKIPPED,
9
+ SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE as SVM_TRANSACTION_PREFLIGHT_FAILURE,
9
10
  } from "@solana/kit";
@@ -25,8 +25,9 @@ import bs58 from "bs58";
25
25
  import { ethers } from "ethers";
26
26
  import { FillType, RelayData } from "../../interfaces";
27
27
  import { BigNumber, Address as SdkAddress, biMin, getRelayDataHash, isDefined, isUint8Array } from "../../utils";
28
- import { getTimestampForSlot } from "./SpokeUtils";
28
+ import { getTimestampForSlot, getSlot } from "./SpokeUtils";
29
29
  import { AttestedCCTPMessage, EventName, SVMEventNames, SVMProvider } from "./types";
30
+ import winston from "winston";
30
31
 
31
32
  export { isSolanaError } from "@solana/kit";
32
33
 
@@ -67,13 +68,14 @@ export function toAddress(address: SdkAddress): Address<string> {
67
68
  */
68
69
  export async function getNearestSlotTime(
69
70
  provider: SVMProvider,
71
+ logger: winston.Logger,
70
72
  opts: { slot: bigint } | { commitment: Commitment } = { commitment: "confirmed" }
71
73
  ): Promise<{ slot: bigint; timestamp: number }> {
72
74
  let timestamp: number | undefined;
73
- let slot = "slot" in opts ? opts.slot : await provider.getSlot(opts).send();
75
+ let slot = "slot" in opts ? opts.slot : await getSlot(provider, opts.commitment, logger);
74
76
 
75
77
  do {
76
- timestamp = await getTimestampForSlot(provider, slot);
78
+ timestamp = await getTimestampForSlot(provider, slot, logger);
77
79
  } while (!isDefined(timestamp) && --slot);
78
80
  assert(isDefined(timestamp), `Unable to resolve block time for SVM slot ${slot}`);
79
81
 
@@ -87,11 +89,12 @@ export async function getNearestSlotTime(
87
89
  */
88
90
  export async function getLatestFinalizedSlotWithBlock(
89
91
  provider: SVMProvider,
92
+ logger: winston.Logger,
90
93
  maxSlot: bigint,
91
94
  maxLookback = 1000
92
95
  ): Promise<number> {
93
96
  const opts = { maxSupportedTransactionVersion: 0, transactionDetails: "none", rewards: false } as const;
94
- const { slot: finalizedSlot } = await getNearestSlotTime(provider, { commitment: "finalized" });
97
+ const { slot: finalizedSlot } = await getNearestSlotTime(provider, logger, { commitment: "finalized" });
95
98
  const endSlot = biMin(maxSlot, finalizedSlot);
96
99
 
97
100
  let slot = endSlot;
@@ -430,6 +433,29 @@ export const simulateAndDecode = async <P extends (buf: Buffer) => unknown>(
430
433
  return parser(Buffer.from(simulationResult.value.returnData.data[0], "base64")) as ReturnType<P>;
431
434
  };
432
435
 
436
+ /**
437
+ * Converts a common `RelayData` type to an SvmSpokeClient.RelayData` type. This is useful for when we need
438
+ * to interface directly with the SvmSpoke.
439
+ * @param relayData The common RelayData TS type.
440
+ * @returns RelayData which conforms to the typing of the SvmSpoke.
441
+ */
442
+ export function toSvmRelayData(relayData: RelayData): SvmSpokeClient.RelayData {
443
+ return {
444
+ originChainId: BigInt(relayData.originChainId),
445
+ depositor: address(relayData.depositor.toBase58()),
446
+ recipient: address(relayData.recipient.toBase58()),
447
+ depositId: ethers.utils.arrayify(ethers.utils.hexZeroPad(relayData.depositId.toHexString(), 32)),
448
+ inputToken: address(relayData.inputToken.toBase58()),
449
+ outputToken: address(relayData.outputToken.toBase58()),
450
+ inputAmount: ethers.utils.arrayify(ethers.utils.hexZeroPad(relayData.inputAmount.toHexString(), 32)),
451
+ outputAmount: relayData.outputAmount.toBigInt(),
452
+ message: Uint8Array.from(Buffer.from(relayData.message.slice(2), "hex")),
453
+ fillDeadline: relayData.fillDeadline,
454
+ exclusiveRelayer: address(relayData.exclusiveRelayer.toBase58()),
455
+ exclusivityDeadline: relayData.exclusivityDeadline,
456
+ };
457
+ }
458
+
433
459
  /**
434
460
  * Returns the PDA for the CCTP nonce.
435
461
  * @param solanaClient The Solana client.
@@ -101,7 +101,7 @@ export class ArweaveClient {
101
101
  const transactionUrl = `${this.gatewayUrl}/${transactionID}`;
102
102
  // We should query in via Axios directly to the gateway URL. The reasoning behind this is
103
103
  // that the Arweave SDK's `getData` method is too slow and does not provide a way to set a timeout.
104
- // Therefore, something that could take milliesconds to complete could take tens of minutes.
104
+ // Therefore, something that could take milliseconds to complete could take tens of minutes.
105
105
  const { data, status: responseStatus } = await axios.get<Record<string, unknown>>(transactionUrl);
106
106
  // Ensure that the result is successful. If it is not, the retrieved value is not our expected type
107
107
  // but rather a {status: string, statusText: string} object. We can detect that and return null.
@@ -2,6 +2,7 @@ import { providers } from "ethers";
2
2
  import { CachingMechanismInterface } from "../interfaces";
3
3
  import { EventSearchConfig, isDefined, MakeOptional } from "../utils";
4
4
  import { getNearestSlotTime, SVMProvider } from "../arch/svm";
5
+ import winston from "winston";
5
6
 
6
7
  export enum UpdateFailureReason {
7
8
  NotReady,
@@ -59,9 +60,7 @@ export abstract class BaseAbstractClient {
59
60
  * @provider Ethers RPC provider instance.
60
61
  * @returns An EventSearchConfig instance if valid, otherwise an UpdateFailureReason.
61
62
  */
62
- public async updateSearchConfig(
63
- provider: providers.Provider | SVMProvider
64
- ): Promise<EventSearchConfig | UpdateFailureReason> {
63
+ public async updateSearchConfig(provider: providers.Provider): Promise<EventSearchConfig | UpdateFailureReason> {
65
64
  const from = this.firstHeightToSearch;
66
65
  let { to } = this.eventSearchConfig;
67
66
  if (isDefined(to)) {
@@ -69,12 +68,29 @@ export abstract class BaseAbstractClient {
69
68
  throw new Error(`Invalid event search config from (${from}) > to (${to})`);
70
69
  }
71
70
  } else {
72
- if (provider instanceof providers.Provider) {
73
- to = await provider.getBlockNumber();
74
- } else {
75
- const { slot } = await getNearestSlotTime(provider);
76
- to = Number(slot);
71
+ to = await provider.getBlockNumber();
72
+ if (to < from) {
73
+ return UpdateFailureReason.AlreadyUpdated;
77
74
  }
75
+ }
76
+
77
+ const { maxLookBack } = this.eventSearchConfig;
78
+ return { from, to, maxLookBack };
79
+ }
80
+
81
+ public async updateSvmSearchConfig(
82
+ provider: SVMProvider,
83
+ logger: winston.Logger
84
+ ): Promise<EventSearchConfig | UpdateFailureReason> {
85
+ const from = this.firstHeightToSearch;
86
+ let { to } = this.eventSearchConfig;
87
+ if (isDefined(to)) {
88
+ if (from > to) {
89
+ throw new Error(`Invalid event search config from (${from}) > to (${to})`);
90
+ }
91
+ } else {
92
+ const { slot } = await getNearestSlotTime(provider, logger);
93
+ to = Number(slot);
78
94
  if (to < from) {
79
95
  return UpdateFailureReason.AlreadyUpdated;
80
96
  }
@@ -1651,6 +1651,7 @@ export class BundleDataClient {
1651
1651
  deposit,
1652
1652
  spokePoolClient.chainId,
1653
1653
  spokePoolClient.svmEventsClient,
1654
+ spokePoolClient.logger,
1654
1655
  spokePoolClient.deploymentBlock,
1655
1656
  spokePoolClient.latestHeightSearched
1656
1657
  );
@@ -41,7 +41,11 @@ export async function getWidestPossibleExpectedBlockRange(
41
41
  assert(isSVMSpokePoolClient(spokePoolClient));
42
42
 
43
43
  const maxSlot = resolveEndBlock(chainId, idx); // Respect any configured buffer for Solana.
44
- return getLatestFinalizedSlotWithBlock(spokePoolClient.svmEventsClient.getRpc(), BigInt(maxSlot));
44
+ return getLatestFinalizedSlotWithBlock(
45
+ spokePoolClient.svmEventsClient.getRpc(),
46
+ spokePoolClient.logger,
47
+ BigInt(maxSlot)
48
+ );
45
49
  };
46
50
 
47
51
  const latestPossibleBundleEndBlockNumbers = await Promise.all(
@@ -115,7 +115,7 @@ export class SVMSpokePoolClient extends SpokePoolClient {
115
115
  * Performs an update to refresh the state of this client by querying SVM events.
116
116
  */
117
117
  protected async _update(eventsToQuery: string[]): Promise<SpokePoolUpdate> {
118
- const searchConfig = await this.updateSearchConfig(this.svmEventsClient.getRpc());
118
+ const searchConfig = await this.updateSvmSearchConfig(this.svmEventsClient.getRpc(), this.logger);
119
119
  if (isUpdateFailureReason(searchConfig)) {
120
120
  const reason = searchConfig;
121
121
  return { success: false, reason };
@@ -195,7 +195,7 @@ export class SVMSpokePoolClient extends SpokePoolClient {
195
195
  public override async getTimestampForBlock(slot: number): Promise<number> {
196
196
  let _slot = BigInt(slot);
197
197
  do {
198
- const timestamp = await getTimestampForSlot(this.svmEventsClient.getRpc(), _slot);
198
+ const timestamp = await getTimestampForSlot(this.svmEventsClient.getRpc(), _slot, this.logger);
199
199
  if (isDefined(timestamp)) {
200
200
  return timestamp;
201
201
  }
@@ -217,7 +217,7 @@ export class SVMSpokePoolClient extends SpokePoolClient {
217
217
  * Finds a deposit based on its deposit ID on the SVM chain.
218
218
  */
219
219
  public async findDeposit(depositId: BigNumber): Promise<DepositSearchResult> {
220
- const deposit = await findDeposit(this.svmEventsClient, depositId);
220
+ const deposit = await findDeposit(this.svmEventsClient, depositId, this.logger);
221
221
  if (!deposit) {
222
222
  return {
223
223
  found: false,
@@ -244,7 +244,7 @@ export class SVMSpokePoolClient extends SpokePoolClient {
244
244
  * Retrieves the fill status for a given relay data from the SVM chain.
245
245
  */
246
246
  public override relayFillStatus(relayData: RelayData, atHeight?: number): Promise<FillStatus> {
247
- return relayFillStatus(this.programId, relayData, this.chainId, this.svmEventsClient, atHeight);
247
+ return relayFillStatus(this.programId, relayData, this.chainId, this.svmEventsClient, this.logger, atHeight);
248
248
  }
249
249
 
250
250
  /**
@@ -260,6 +260,6 @@ export class SVMSpokePoolClient extends SpokePoolClient {
260
260
  ): Promise<(FillStatus | undefined)[]> {
261
261
  // @note: deploymentBlock actually refers to the deployment slot. Also, blockTag should be a slot number.
262
262
  destinationChainId ??= this.chainId;
263
- return fillStatusArray(this.programId, relayData, destinationChainId, this.svmEventsClient, atHeight, this.logger);
263
+ return fillStatusArray(this.programId, relayData, destinationChainId, this.svmEventsClient, this.logger, atHeight);
264
264
  }
265
265
  }