@chainlink/ccip-sdk 0.96.0 → 0.97.0

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 (199) hide show
  1. package/dist/api/index.d.ts +17 -8
  2. package/dist/api/index.d.ts.map +1 -1
  3. package/dist/api/index.js +27 -10
  4. package/dist/api/index.js.map +1 -1
  5. package/dist/api/types.d.ts +0 -2
  6. package/dist/api/types.d.ts.map +1 -1
  7. package/dist/aptos/exec.d.ts +2 -2
  8. package/dist/aptos/exec.d.ts.map +1 -1
  9. package/dist/aptos/exec.js.map +1 -1
  10. package/dist/aptos/hasher.d.ts.map +1 -1
  11. package/dist/aptos/hasher.js +1 -1
  12. package/dist/aptos/hasher.js.map +1 -1
  13. package/dist/aptos/index.d.ts +13 -10
  14. package/dist/aptos/index.d.ts.map +1 -1
  15. package/dist/aptos/index.js +42 -68
  16. package/dist/aptos/index.js.map +1 -1
  17. package/dist/aptos/types.d.ts +2 -19
  18. package/dist/aptos/types.d.ts.map +1 -1
  19. package/dist/aptos/types.js +0 -11
  20. package/dist/aptos/types.js.map +1 -1
  21. package/dist/chain.d.ts +532 -151
  22. package/dist/chain.d.ts.map +1 -1
  23. package/dist/chain.js +113 -18
  24. package/dist/chain.js.map +1 -1
  25. package/dist/commits.d.ts +4 -6
  26. package/dist/commits.d.ts.map +1 -1
  27. package/dist/commits.js +4 -4
  28. package/dist/commits.js.map +1 -1
  29. package/dist/errors/CCIPError.d.ts +33 -4
  30. package/dist/errors/CCIPError.d.ts.map +1 -1
  31. package/dist/errors/CCIPError.js +33 -4
  32. package/dist/errors/CCIPError.js.map +1 -1
  33. package/dist/errors/codes.d.ts +3 -0
  34. package/dist/errors/codes.d.ts.map +1 -1
  35. package/dist/errors/codes.js +3 -1
  36. package/dist/errors/codes.js.map +1 -1
  37. package/dist/errors/index.d.ts +1 -1
  38. package/dist/errors/index.d.ts.map +1 -1
  39. package/dist/errors/index.js +1 -1
  40. package/dist/errors/index.js.map +1 -1
  41. package/dist/errors/recovery.d.ts.map +1 -1
  42. package/dist/errors/recovery.js +4 -1
  43. package/dist/errors/recovery.js.map +1 -1
  44. package/dist/errors/specialized.d.ts +1695 -120
  45. package/dist/errors/specialized.d.ts.map +1 -1
  46. package/dist/errors/specialized.js +1715 -123
  47. package/dist/errors/specialized.js.map +1 -1
  48. package/dist/errors/utils.d.ts.map +1 -1
  49. package/dist/errors/utils.js +0 -1
  50. package/dist/errors/utils.js.map +1 -1
  51. package/dist/evm/abi/OffRamp_2_0.d.ts +764 -0
  52. package/dist/evm/abi/OffRamp_2_0.d.ts.map +1 -0
  53. package/dist/evm/abi/OffRamp_2_0.js +744 -0
  54. package/dist/evm/abi/OffRamp_2_0.js.map +1 -0
  55. package/dist/evm/abi/OnRamp_2_0.d.ts +925 -0
  56. package/dist/evm/abi/OnRamp_2_0.d.ts.map +1 -0
  57. package/dist/evm/abi/OnRamp_2_0.js +992 -0
  58. package/dist/evm/abi/OnRamp_2_0.js.map +1 -0
  59. package/dist/evm/const.d.ts +12 -2
  60. package/dist/evm/const.d.ts.map +1 -1
  61. package/dist/evm/const.js +8 -2
  62. package/dist/evm/const.js.map +1 -1
  63. package/dist/evm/errors.d.ts.map +1 -1
  64. package/dist/evm/errors.js +7 -2
  65. package/dist/evm/errors.js.map +1 -1
  66. package/dist/evm/extra-args.d.ts.map +1 -1
  67. package/dist/evm/extra-args.js +5 -24
  68. package/dist/evm/extra-args.js.map +1 -1
  69. package/dist/evm/hasher.d.ts.map +1 -1
  70. package/dist/evm/hasher.js +23 -13
  71. package/dist/evm/hasher.js.map +1 -1
  72. package/dist/evm/index.d.ts +73 -14
  73. package/dist/evm/index.d.ts.map +1 -1
  74. package/dist/evm/index.js +240 -141
  75. package/dist/evm/index.js.map +1 -1
  76. package/dist/evm/messages.d.ts +59 -5
  77. package/dist/evm/messages.d.ts.map +1 -1
  78. package/dist/evm/messages.js +210 -0
  79. package/dist/evm/messages.js.map +1 -1
  80. package/dist/evm/offchain.js.map +1 -1
  81. package/dist/evm/types.d.ts +7 -2
  82. package/dist/evm/types.d.ts.map +1 -1
  83. package/dist/evm/types.js +22 -1
  84. package/dist/evm/types.js.map +1 -1
  85. package/dist/execution.d.ts +62 -22
  86. package/dist/execution.d.ts.map +1 -1
  87. package/dist/execution.js +98 -61
  88. package/dist/execution.js.map +1 -1
  89. package/dist/extra-args.d.ts +13 -3
  90. package/dist/extra-args.d.ts.map +1 -1
  91. package/dist/extra-args.js +13 -3
  92. package/dist/extra-args.js.map +1 -1
  93. package/dist/gas.d.ts +25 -2
  94. package/dist/gas.d.ts.map +1 -1
  95. package/dist/gas.js +30 -4
  96. package/dist/gas.js.map +1 -1
  97. package/dist/index.d.ts +1 -1
  98. package/dist/index.d.ts.map +1 -1
  99. package/dist/requests.d.ts +85 -14
  100. package/dist/requests.d.ts.map +1 -1
  101. package/dist/requests.js +99 -16
  102. package/dist/requests.js.map +1 -1
  103. package/dist/selectors.d.ts.map +1 -1
  104. package/dist/selectors.js +12 -0
  105. package/dist/selectors.js.map +1 -1
  106. package/dist/shared/bcs-codecs.d.ts +61 -0
  107. package/dist/shared/bcs-codecs.d.ts.map +1 -0
  108. package/dist/shared/bcs-codecs.js +102 -0
  109. package/dist/shared/bcs-codecs.js.map +1 -0
  110. package/dist/shared/constants.d.ts +3 -0
  111. package/dist/shared/constants.d.ts.map +1 -0
  112. package/dist/shared/constants.js +3 -0
  113. package/dist/shared/constants.js.map +1 -0
  114. package/dist/solana/exec.d.ts +2 -2
  115. package/dist/solana/exec.d.ts.map +1 -1
  116. package/dist/solana/exec.js.map +1 -1
  117. package/dist/solana/index.d.ts +80 -17
  118. package/dist/solana/index.d.ts.map +1 -1
  119. package/dist/solana/index.js +67 -30
  120. package/dist/solana/index.js.map +1 -1
  121. package/dist/sui/hasher.d.ts.map +1 -1
  122. package/dist/sui/hasher.js +1 -1
  123. package/dist/sui/hasher.js.map +1 -1
  124. package/dist/sui/index.d.ts +14 -12
  125. package/dist/sui/index.d.ts.map +1 -1
  126. package/dist/sui/index.js +38 -34
  127. package/dist/sui/index.js.map +1 -1
  128. package/dist/sui/manuallyExec/encoder.d.ts +2 -2
  129. package/dist/sui/manuallyExec/encoder.d.ts.map +1 -1
  130. package/dist/sui/manuallyExec/encoder.js.map +1 -1
  131. package/dist/sui/manuallyExec/index.d.ts +2 -2
  132. package/dist/sui/manuallyExec/index.d.ts.map +1 -1
  133. package/dist/ton/exec.d.ts +2 -2
  134. package/dist/ton/exec.d.ts.map +1 -1
  135. package/dist/ton/exec.js.map +1 -1
  136. package/dist/ton/index.d.ts +9 -16
  137. package/dist/ton/index.d.ts.map +1 -1
  138. package/dist/ton/index.js +26 -31
  139. package/dist/ton/index.js.map +1 -1
  140. package/dist/ton/types.d.ts +2 -2
  141. package/dist/ton/types.d.ts.map +1 -1
  142. package/dist/ton/types.js.map +1 -1
  143. package/dist/types.d.ts +46 -11
  144. package/dist/types.d.ts.map +1 -1
  145. package/dist/types.js +6 -1
  146. package/dist/types.js.map +1 -1
  147. package/dist/utils.d.ts +65 -2
  148. package/dist/utils.d.ts.map +1 -1
  149. package/dist/utils.js +74 -2
  150. package/dist/utils.js.map +1 -1
  151. package/package.json +9 -9
  152. package/src/api/index.ts +33 -10
  153. package/src/api/types.ts +0 -2
  154. package/src/aptos/exec.ts +2 -2
  155. package/src/aptos/hasher.ts +1 -1
  156. package/src/aptos/index.ts +51 -89
  157. package/src/aptos/types.ts +2 -15
  158. package/src/chain.ts +581 -163
  159. package/src/commits.ts +9 -9
  160. package/src/errors/CCIPError.ts +33 -4
  161. package/src/errors/codes.ts +3 -1
  162. package/src/errors/index.ts +1 -0
  163. package/src/errors/recovery.ts +7 -1
  164. package/src/errors/specialized.ts +1726 -130
  165. package/src/errors/utils.ts +0 -1
  166. package/src/evm/abi/OffRamp_2_0.ts +743 -0
  167. package/src/evm/abi/OnRamp_2_0.ts +991 -0
  168. package/src/evm/const.ts +10 -3
  169. package/src/evm/errors.ts +6 -2
  170. package/src/evm/extra-args.ts +4 -21
  171. package/src/evm/hasher.ts +30 -18
  172. package/src/evm/index.ts +310 -166
  173. package/src/evm/messages.ts +323 -11
  174. package/src/evm/offchain.ts +2 -2
  175. package/src/evm/types.ts +20 -2
  176. package/src/execution.ts +125 -86
  177. package/src/extra-args.ts +13 -3
  178. package/src/gas.ts +29 -3
  179. package/src/index.ts +2 -2
  180. package/src/requests.ts +112 -16
  181. package/src/selectors.ts +12 -0
  182. package/src/shared/bcs-codecs.ts +132 -0
  183. package/src/shared/constants.ts +2 -0
  184. package/src/solana/exec.ts +4 -4
  185. package/src/solana/index.ts +100 -68
  186. package/src/sui/hasher.ts +1 -1
  187. package/src/sui/index.ts +50 -47
  188. package/src/sui/manuallyExec/encoder.ts +2 -2
  189. package/src/sui/manuallyExec/index.ts +2 -2
  190. package/src/ton/exec.ts +2 -2
  191. package/src/ton/index.ts +37 -40
  192. package/src/ton/types.ts +2 -2
  193. package/src/types.ts +70 -29
  194. package/src/utils.ts +73 -2
  195. package/dist/aptos/utils.d.ts +0 -12
  196. package/dist/aptos/utils.d.ts.map +0 -1
  197. package/dist/aptos/utils.js +0 -15
  198. package/dist/aptos/utils.js.map +0 -1
  199. package/src/aptos/utils.ts +0 -24
package/src/evm/index.ts CHANGED
@@ -1,24 +1,23 @@
1
- import { parseAbi } from 'abitype'
2
1
  import {
3
2
  type BytesLike,
4
3
  type Interface,
5
4
  type JsonRpcApiProvider,
6
5
  type Log,
6
+ type Result,
7
7
  type Signer,
8
8
  type TransactionReceipt,
9
9
  type TransactionRequest,
10
10
  type TransactionResponse,
11
11
  Contract,
12
12
  JsonRpcProvider,
13
- Result,
14
13
  WebSocketProvider,
15
14
  ZeroAddress,
16
15
  getAddress,
17
16
  hexlify,
18
17
  isBytesLike,
19
18
  isHexString,
19
+ keccak256,
20
20
  toBeHex,
21
- toBigInt,
22
21
  zeroPadValue,
23
22
  } from 'ethers'
24
23
  import type { TypedContract } from 'ethers-abitype'
@@ -34,16 +33,16 @@ import {
34
33
  } from '../chain.ts'
35
34
  import {
36
35
  CCIPAddressInvalidEvmError,
36
+ CCIPApiClientNotAvailableError,
37
37
  CCIPBlockNotFoundError,
38
38
  CCIPContractNotRouterError,
39
39
  CCIPContractTypeInvalidError,
40
40
  CCIPDataFormatUnsupportedError,
41
41
  CCIPExecTxNotConfirmedError,
42
42
  CCIPExecTxRevertedError,
43
- CCIPExtraArgsParseError,
44
43
  CCIPHasherVersionUnsupportedError,
45
44
  CCIPLogDataInvalidError,
46
- CCIPMessageDecodeError,
45
+ CCIPNotImplementedError,
47
46
  CCIPSourceChainUnsupportedError,
48
47
  CCIPTokenNotConfiguredError,
49
48
  CCIPTokenPoolChainConfigNotFoundError,
@@ -60,8 +59,10 @@ import {
60
59
  type CCIPExecution,
61
60
  type CCIPMessage,
62
61
  type CCIPRequest,
62
+ type CCIPVerifications,
63
63
  type ChainTransaction,
64
64
  type CommitReport,
65
+ type ExecutionInput,
65
66
  type ExecutionReceipt,
66
67
  type ExecutionState,
67
68
  type Lane,
@@ -88,12 +89,21 @@ import type TokenPool_ABI from './abi/LockReleaseTokenPool_1_6_1.ts'
88
89
  import EVM2EVMOffRamp_1_2_ABI from './abi/OffRamp_1_2.ts'
89
90
  import EVM2EVMOffRamp_1_5_ABI from './abi/OffRamp_1_5.ts'
90
91
  import OffRamp_1_6_ABI from './abi/OffRamp_1_6.ts'
92
+ import OffRamp_2_0_ABI from './abi/OffRamp_2_0.ts'
91
93
  import EVM2EVMOnRamp_1_2_ABI from './abi/OnRamp_1_2.ts'
92
94
  import EVM2EVMOnRamp_1_5_ABI from './abi/OnRamp_1_5.ts'
93
- import OnRamp_1_6_ABI from './abi/OnRamp_1_6.ts'
95
+ import type OnRamp_1_6_ABI from './abi/OnRamp_1_6.ts'
96
+ import type OnRamp_2_0_ABI from './abi/OnRamp_2_0.ts'
94
97
  import type Router_ABI from './abi/Router.ts'
95
98
  import type TokenAdminRegistry_1_5_ABI from './abi/TokenAdminRegistry_1_5.ts'
96
- import { commitsFragments, interfaces, receiptsFragments, requestsFragments } from './const.ts'
99
+ import {
100
+ CCV_INDEXER_URL,
101
+ VersionedContractABI,
102
+ commitsFragments,
103
+ interfaces,
104
+ receiptsFragments,
105
+ requestsFragments,
106
+ } from './const.ts'
97
107
  import { parseData } from './errors.ts'
98
108
  import {
99
109
  decodeExtraArgs as decodeExtraArgs_,
@@ -104,30 +114,19 @@ import { getV12LeafHasher, getV16LeafHasher } from './hasher.ts'
104
114
  import { getEvmLogs } from './logs.ts'
105
115
  import {
106
116
  type CCIPMessage_V1_6_EVM,
117
+ type CCIPMessage_V2_0,
107
118
  type CleanAddressable,
108
- parseSourceTokenData,
119
+ type MessageV1,
120
+ type TokenTransferV1,
121
+ decodeMessageV1,
109
122
  } from './messages.ts'
123
+ export { decodeMessageV1 }
124
+ export type { MessageV1, TokenTransferV1 }
110
125
  import { encodeEVMOffchainTokenData, fetchEVMOffchainTokenData } from './offchain.ts'
111
- import { buildMessageForDest, getMessagesInBatch } from '../requests.ts'
112
- import type { UnsignedEVMTx } from './types.ts'
126
+ import { buildMessageForDest, decodeMessage, getMessagesInBatch } from '../requests.ts'
127
+ import { type UnsignedEVMTx, resultToObject } from './types.ts'
113
128
  export type { UnsignedEVMTx }
114
129
 
115
- const VersionedContractABI = parseAbi(['function typeAndVersion() view returns (string)'])
116
-
117
- function resultToObject<T>(o: T): T {
118
- if (o instanceof Promise) return o.then(resultToObject) as T
119
- if (!(o instanceof Result)) return o
120
- if (o.length === 0) return o.toArray() as T
121
- try {
122
- const obj = o.toObject()
123
- if (!Object.keys(obj).every((k) => /^_+\d*$/.test(k)))
124
- return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, resultToObject(v)])) as T
125
- } catch (_) {
126
- // fallthrough
127
- }
128
- return o.toArray().map(resultToObject) as T
129
- }
130
-
131
130
  /** typeguard for ethers Signer interface (used for `wallet`s) */
132
131
  function isSigner(wallet: unknown): wallet is Signer {
133
132
  return (
@@ -158,6 +157,26 @@ async function submitTransaction(
158
157
 
159
158
  /**
160
159
  * EVM chain implementation supporting Ethereum-compatible networks.
160
+ *
161
+ * Provides methods for sending CCIP cross-chain messages, querying message
162
+ * status, fetching fee quotes, and manually executing pending messages on
163
+ * Ethereum Virtual Machine compatible chains.
164
+ *
165
+ * @example Create from RPC URL
166
+ * ```typescript
167
+ * import { EVMChain } from '@chainlink/ccip-sdk'
168
+ *
169
+ * const chain = await EVMChain.fromUrl('https://rpc.sepolia.org')
170
+ * console.log(`Connected to: ${chain.network.name}`)
171
+ * ```
172
+ *
173
+ * @example Query messages in a transaction
174
+ * ```typescript
175
+ * const requests = await chain.getMessagesInTx('0xabc123...')
176
+ * for (const req of requests) {
177
+ * console.log(`Message ID: ${req.message.messageId}`)
178
+ * }
179
+ * ```
161
180
  */
162
181
  export class EVMChain extends Chain<typeof ChainFamily.EVM> {
163
182
  static {
@@ -168,6 +187,13 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
168
187
 
169
188
  provider: JsonRpcApiProvider
170
189
  readonly destroy$: Promise<void>
190
+ private noncesPromises: Record<string, Promise<unknown>>
191
+ /**
192
+ * Cache of current nonces per wallet address.
193
+ * Used internally by {@link sendMessage} and {@link execute} to manage transaction ordering.
194
+ * Can be inspected for debugging or manually adjusted if needed.
195
+ */
196
+ nonces: Record<string, number>
171
197
 
172
198
  /**
173
199
  * Creates a new EVMChain instance.
@@ -177,6 +203,9 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
177
203
  constructor(provider: JsonRpcApiProvider, network: NetworkInfo, ctx?: ChainContext) {
178
204
  super(network, ctx)
179
205
 
206
+ this.noncesPromises = {}
207
+ this.nonces = {}
208
+
180
209
  this.provider = provider
181
210
  this.destroy$ = new Promise<void>((resolve) => (this.destroy = resolve))
182
211
  void this.destroy$.finally(() => provider.destroy())
@@ -216,6 +245,22 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
216
245
  return (await this.provider.listAccounts()).map(({ address }) => address)
217
246
  }
218
247
 
248
+ /**
249
+ * Get the next nonce for a wallet address and increment the internal counter.
250
+ * Fetches from the network on first call, then uses cached value.
251
+ * @param address - Wallet address to get nonce for
252
+ * @returns The next available nonce
253
+ */
254
+ async nextNonce(address: string): Promise<number> {
255
+ await (this.noncesPromises[address] ??= this.provider
256
+ .getTransactionCount(address)
257
+ .then((nonce) => {
258
+ this.nonces[address] = nonce
259
+ return nonce
260
+ }))
261
+ return this.nonces[address]!++
262
+ }
263
+
219
264
  /**
220
265
  * Creates a JSON-RPC provider from a URL.
221
266
  * @param url - WebSocket (wss://) or HTTP (https://) endpoint URL.
@@ -260,9 +305,20 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
260
305
 
261
306
  /**
262
307
  * Creates an EVMChain instance from an RPC URL.
308
+ *
263
309
  * @param url - WebSocket (wss://) or HTTP (https://) endpoint URL.
264
- * @param ctx - context containing logger.
265
- * @returns A new EVMChain instance.
310
+ * @param ctx - Optional context containing logger and API client configuration.
311
+ * @returns A new EVMChain instance connected to the specified network.
312
+ * @throws {@link CCIPChainNotFoundError} if chain cannot be identified from chainId
313
+ *
314
+ * @example
315
+ * ```typescript
316
+ * // HTTP connection
317
+ * const chain = await EVMChain.fromUrl('https://rpc.sepolia.org')
318
+ *
319
+ * // With custom logger
320
+ * const chain = await EVMChain.fromUrl(url, { logger: customLogger })
321
+ * ```
266
322
  */
267
323
  static async fromUrl(url: string, ctx?: ChainContext): Promise<EVMChain> {
268
324
  return this.fromProvider(await this._getProvider(url), ctx)
@@ -298,15 +354,15 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
298
354
  }
299
355
 
300
356
  /** {@inheritDoc Chain.getMessagesInBatch} */
301
- getMessagesInBatch<
357
+ override getMessagesInBatch<
302
358
  R extends PickDeep<
303
359
  CCIPRequest,
304
360
  'lane' | `log.${'topics' | 'address' | 'blockNumber'}` | 'message.sequenceNumber'
305
361
  >,
306
362
  >(
307
363
  request: R,
308
- commit: Pick<CommitReport, 'minSeqNr' | 'maxSeqNr'>,
309
- opts?: { page?: number },
364
+ range: Pick<CommitReport, 'minSeqNr' | 'maxSeqNr'>,
365
+ opts?: Pick<LogFilter, 'page'>,
310
366
  ): Promise<R['message'][]> {
311
367
  let opts_: Parameters<EVMChain['getLogs']>[0] | undefined
312
368
  if (request.lane.version >= CCIPVersion.V1_6) {
@@ -316,7 +372,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
316
372
  topics: [[request.log.topics[0]!], [toBeHex(request.lane.destChainSelector, 32)]],
317
373
  }
318
374
  }
319
- return getMessagesInBatch(this, request, commit, opts_)
375
+ return getMessagesInBatch(this, request, range, opts_)
320
376
  }
321
377
 
322
378
  /** {@inheritDoc Chain.typeAndVersion} */
@@ -326,7 +382,9 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
326
382
  VersionedContractABI,
327
383
  this.provider,
328
384
  ) as unknown as TypedContract<typeof VersionedContractABI>
329
- return parseTypeAndVersion(await contract.typeAndVersion())
385
+ const res = parseTypeAndVersion(await contract.typeAndVersion())
386
+ if (res[1].startsWith('1.7.')) res[1] = CCIPVersion.V2_0
387
+ return res
330
388
  }
331
389
 
332
390
  /**
@@ -356,79 +414,16 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
356
414
  const result = interfaces.OnRamp_v1_6.decodeEventLog(fragment, log.data, log.topics)
357
415
  message = resultToObject(result) as Record<string, unknown>
358
416
  if (message.message) message = message.message as Record<string, unknown> | undefined
417
+ else if (message.encodedMessage) {
418
+ Object.assign(message, decodeMessageV1(message.encodedMessage as BytesLike))
419
+ }
359
420
  if (message) break
360
421
  } catch (_) {
361
422
  // try next fragment
362
423
  }
363
424
  }
364
425
  if (!message) return
365
- if (!isHexString(message.sender, 20)) throw new CCIPMessageDecodeError('invalid sender')
366
-
367
- if (message.header) {
368
- // CCIPMessage_V1_6
369
- Object.assign(message, message.header)
370
- delete message.header
371
- }
372
-
373
- const sourceFamily = networkInfo(
374
- (message as { sourceChainSelector: bigint }).sourceChainSelector,
375
- ).family
376
- let destFamily: ChainFamily = ChainFamily.EVM
377
- if ((message as { destChainSelector?: bigint }).destChainSelector) {
378
- destFamily = networkInfo((message as { destChainSelector: bigint }).destChainSelector).family
379
- }
380
- // conversions to make any message version be compatible with latest v1.6
381
- message.tokenAmounts = (message.tokenAmounts as Record<string, string | bigint | number>[]).map(
382
- (tokenAmount, i) => {
383
- if ('sourceTokenData' in message) {
384
- // CCIPMessage_V1_2_EVM
385
- try {
386
- tokenAmount = {
387
- ...parseSourceTokenData(
388
- (message as { sourceTokenData: string[] }).sourceTokenData[i]!,
389
- ),
390
- ...tokenAmount,
391
- }
392
- } catch (_) {
393
- // legacy sourceTokenData
394
- }
395
- }
396
- if (typeof tokenAmount.destExecData === 'string' && tokenAmount.destGasAmount == null) {
397
- // CCIPMessage_V1_6_EVM
398
- tokenAmount.destGasAmount = toBigInt(getDataBytes(tokenAmount.destExecData))
399
- }
400
- // Can be undefined if the message is from before v1.5 and failed to parse sourceTokenData
401
- if (tokenAmount.sourcePoolAddress) {
402
- tokenAmount.sourcePoolAddress = decodeAddress(
403
- tokenAmount.sourcePoolAddress as string,
404
- sourceFamily,
405
- )
406
- }
407
- if (tokenAmount.destTokenAddress) {
408
- tokenAmount.destTokenAddress = decodeAddress(
409
- tokenAmount.destTokenAddress as string,
410
- destFamily,
411
- )
412
- }
413
- return tokenAmount
414
- },
415
- )
416
- message.sender = decodeAddress(message.sender, sourceFamily)
417
- message.feeToken = decodeAddress(message.feeToken as string, sourceFamily)
418
- message.receiver = decodeAddress(message.receiver as string, destFamily)
419
- if (message.extraArgs) {
420
- // v1.6+
421
- const parsed = this.decodeExtraArgs(message.extraArgs as string)
422
- if (!parsed) throw new CCIPExtraArgsParseError(message.extraArgs as string)
423
- const { _tag, ...rest } = parsed
424
- // merge parsed extraArgs to any family in message root object
425
- Object.assign(message, rest)
426
- } else if (message.nonce === 0n) {
427
- // v1.2..v1.5 targets EVM only; extraArgs is not explicit, gasLimit is already in
428
- // message body, allowOutOfOrderExecution (in v1.5) was present only as nonce=0
429
- message.allowOutOfOrderExecution = true
430
- }
431
- return message as CCIPMessage
426
+ return decodeMessage(message)
432
427
  }
433
428
 
434
429
  /**
@@ -609,13 +604,23 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
609
604
  return router as string
610
605
  }
611
606
  case CCIPVersion.V1_6: {
612
- onRampABI = OnRamp_1_6_ABI
613
- const contract = new Contract(onRamp, onRampABI, this.provider) as unknown as TypedContract<
614
- typeof onRampABI
615
- >
607
+ const contract = new Contract(
608
+ onRamp,
609
+ interfaces.OnRamp_v1_6,
610
+ this.provider,
611
+ ) as unknown as TypedContract<typeof OnRamp_1_6_ABI>
616
612
  const [, , router] = await contract.getDestChainConfig(destChainSelector)
617
613
  return router as string
618
614
  }
615
+ case CCIPVersion.V2_0: {
616
+ const contract = new Contract(
617
+ onRamp,
618
+ interfaces.OnRamp_v2_0,
619
+ this.provider,
620
+ ) as unknown as TypedContract<typeof OnRamp_2_0_ABI>
621
+ const { router } = await contract.getDestChainConfig(destChainSelector)
622
+ return router as string
623
+ }
619
624
  default:
620
625
  throw new CCIPVersionUnsupportedError(version)
621
626
  }
@@ -642,8 +647,11 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
642
647
  ;({ router } = await contract.getDynamicConfig())
643
648
  break
644
649
  }
645
- case CCIPVersion.V1_6: {
650
+ case CCIPVersion.V1_6:
646
651
  offRampABI = OffRamp_1_6_ABI
652
+ // falls through
653
+ case CCIPVersion.V2_0: {
654
+ offRampABI = OffRamp_2_0_ABI
647
655
  const contract = new Contract(
648
656
  offRamp,
649
657
  offRampABI,
@@ -692,10 +700,10 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
692
700
  }
693
701
 
694
702
  /**
695
- * {@inheritDoc Chain.getOnRampForOffRamp}
703
+ * {@inheritDoc Chain.getOnRampsForOffRamp}
696
704
  * @throws {@link CCIPVersionUnsupportedError} if OffRamp version is not supported
697
705
  */
698
- async getOnRampForOffRamp(offRamp: string, sourceChainSelector: bigint): Promise<string> {
706
+ async getOnRampsForOffRamp(offRamp: string, sourceChainSelector: bigint): Promise<string[]> {
699
707
  const [, version] = await this.typeAndVersion(offRamp)
700
708
  let offRampABI
701
709
  switch (version) {
@@ -710,7 +718,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
710
718
  this.provider,
711
719
  ) as unknown as TypedContract<typeof offRampABI>
712
720
  const { onRamp } = await contract.getStaticConfig()
713
- return onRamp as string
721
+ return [onRamp as string]
714
722
  }
715
723
  case CCIPVersion.V1_6: {
716
724
  offRampABI = OffRamp_1_6_ABI
@@ -720,7 +728,19 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
720
728
  this.provider,
721
729
  ) as unknown as TypedContract<typeof offRampABI>
722
730
  const { onRamp } = await contract.getSourceChainConfig(sourceChainSelector)
723
- return decodeOnRampAddress(onRamp, networkInfo(sourceChainSelector).family)
731
+ if (!onRamp || onRamp.match(/^(0x)?0*$/i)) return []
732
+ return [decodeOnRampAddress(onRamp, networkInfo(sourceChainSelector).family)]
733
+ }
734
+ case CCIPVersion.V2_0: {
735
+ offRampABI = OffRamp_2_0_ABI
736
+ const contract = new Contract(
737
+ offRamp,
738
+ offRampABI,
739
+ this.provider,
740
+ ) as unknown as TypedContract<typeof offRampABI>
741
+ const { onRamps } = await contract.getSourceChainConfig(sourceChainSelector)
742
+ const sourceFamily = networkInfo(sourceChainSelector).family
743
+ return onRamps.map((onRamp) => decodeOnRampAddress(onRamp, sourceFamily))
724
744
  }
725
745
  default:
726
746
  throw new CCIPVersionUnsupportedError(version)
@@ -728,8 +748,19 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
728
748
  }
729
749
 
730
750
  /**
731
- * {@inheritDoc Chain.getCommitStoreForOffRamp}
751
+ * Fetch the CommitStore set in OffRamp config (CCIP v1.5 and earlier).
752
+ * For CCIP v1.6 and later, it should return the offRamp address.
753
+ *
754
+ * @param offRamp - OffRamp contract address
755
+ * @returns Promise resolving to CommitStore address
756
+ *
757
+ * @example Get commit store
758
+ * ```typescript
759
+ * const commitStore = await dest.getCommitStoreForOffRamp(offRampAddress)
760
+ * // For v1.6+, commitStore === offRampAddress
761
+ * ```
732
762
  * @throws {@link CCIPVersionUnsupportedError} if OffRamp version is not supported
763
+ * @internal
733
764
  */
734
765
  async getCommitStoreForOffRamp(offRamp: string): Promise<string> {
735
766
  const [, version] = await this.typeAndVersion(offRamp)
@@ -748,11 +779,8 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
748
779
  const { commitStore } = await contract.getStaticConfig()
749
780
  return commitStore as string
750
781
  }
751
- case CCIPVersion.V1_6: {
752
- return offRamp
753
- }
754
782
  default:
755
- throw new CCIPVersionUnsupportedError(version)
783
+ return offRamp
756
784
  }
757
785
  }
758
786
 
@@ -1012,27 +1040,37 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1012
1040
  let sendTx: TransactionRequest = txs.transactions[txs.transactions.length - 1]!
1013
1041
 
1014
1042
  // approve all tokens (including feeToken, if needed) in parallel
1015
- let nonce = await this.provider.getTransactionCount(sender)
1016
1043
  const responses = await Promise.all(
1017
1044
  approveTxs.map(async (tx: TransactionRequest) => {
1018
- tx.nonce = nonce++
1019
- tx = await wallet.populateTransaction(tx)
1020
- tx.from = undefined
1021
- const response = await submitTransaction(wallet, tx, this.provider)
1022
- this.logger.debug('approve =>', response.hash)
1023
- return response
1045
+ tx.nonce = await this.nextNonce(sender)
1046
+ try {
1047
+ tx = await wallet.populateTransaction(tx)
1048
+ tx.from = undefined
1049
+ const response = await submitTransaction(wallet, tx, this.provider)
1050
+ this.logger.debug('approve =>', response.hash)
1051
+ return response
1052
+ } catch (err) {
1053
+ this.nonces[sender]!--
1054
+ throw err
1055
+ }
1024
1056
  }),
1025
1057
  )
1026
1058
  if (responses.length) await responses[responses.length - 1]!.wait(1, 60_000) // wait last tx nonce to be mined
1027
1059
 
1028
- sendTx.nonce = nonce++
1029
- // sendTx.gasLimit = await this.provider.estimateGas(sendTx)
1030
- sendTx = await wallet.populateTransaction(sendTx)
1031
- sendTx.from = undefined // some signers don't like receiving pre-populated `from`
1032
- const response = await submitTransaction(wallet, sendTx, this.provider)
1060
+ sendTx.nonce = await this.nextNonce(sender)
1061
+ let response
1062
+ try {
1063
+ // sendTx.gasLimit = await this.provider.estimateGas(sendTx)
1064
+ sendTx = await wallet.populateTransaction(sendTx)
1065
+ sendTx.from = undefined // some signers don't like receiving pre-populated `from`
1066
+ response = await submitTransaction(wallet, sendTx, this.provider)
1067
+ } catch (err) {
1068
+ this.nonces[sender]!--
1069
+ throw err
1070
+ }
1033
1071
  this.logger.debug('ccipSend =>', response.hash)
1034
- await response.wait(1, 60_000)
1035
- return (await this.getMessagesInTx(await this.getTransaction(response.hash)))[0]!
1072
+ const tx = (await response.wait(1, 60_000))!
1073
+ return (await this.getMessagesInTx(await this.getTransaction(tx)))[0]!
1036
1074
  }
1037
1075
 
1038
1076
  /** {@inheritDoc Chain.getOffchainTokenData} */
@@ -1041,33 +1079,70 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1041
1079
  }
1042
1080
 
1043
1081
  /**
1044
- * {@inheritDoc Chain.generateUnsignedExecuteReport}
1082
+ * {@inheritDoc Chain.generateUnsignedExecute}
1045
1083
  * @returns array containing one unsigned `manuallyExecute` TransactionRequest object
1046
1084
  * @throws {@link CCIPVersionUnsupportedError} if OffRamp version is not supported
1047
1085
  */
1048
- async generateUnsignedExecuteReport({
1049
- offRamp,
1050
- execReport,
1051
- ...opts
1052
- }: Parameters<Chain['generateUnsignedExecuteReport']>[0]): Promise<UnsignedEVMTx> {
1053
- const [_, version] = await this.typeAndVersion(offRamp)
1086
+ async generateUnsignedExecute(
1087
+ opts: Parameters<Chain['generateUnsignedExecute']>[0],
1088
+ ): Promise<UnsignedEVMTx> {
1089
+ let input: ExecutionInput, offRamp: string
1090
+ if (!('input' in opts)) {
1091
+ if (!this.apiClient) throw new CCIPApiClientNotAvailableError()
1092
+ ;({ offRamp, ...input } = await this.apiClient.getExecutionInput(opts.messageId))
1093
+ } else {
1094
+ ;({ offRamp, input } = opts)
1095
+ }
1096
+ if ('verifications' in input) {
1097
+ const contract = new Contract(
1098
+ offRamp,
1099
+ interfaces.OffRamp_v2_0,
1100
+ this.provider,
1101
+ ) as unknown as TypedContract<typeof OffRamp_2_0_ABI>
1102
+
1103
+ const message = decodeMessageV1(input.encodedMessage)
1104
+ const messageId = keccak256(input.encodedMessage)
1105
+ // `execute` doesn't revert on failure, so we need to estimate using `executeSingleMessage`
1106
+ const txGasLimit = await contract.executeSingleMessage.estimateGas(
1107
+ {
1108
+ ...message,
1109
+ executionGasLimit: BigInt(message.executionGasLimit),
1110
+ ccipReceiveGasLimit: BigInt(message.ccipReceiveGasLimit),
1111
+ finality: BigInt(message.finality),
1112
+ },
1113
+ messageId,
1114
+ input.verifications.map(({ destAddress }) => destAddress),
1115
+ input.verifications.map(({ ccvData }) => hexlify(ccvData)),
1116
+ BigInt(opts.gasLimit ?? 0),
1117
+ { from: offRamp }, // internal method
1118
+ )
1119
+ const execTx = await contract.execute.populateTransaction(
1120
+ input.encodedMessage,
1121
+ input.verifications.map(({ destAddress }) => destAddress),
1122
+ input.verifications.map(({ ccvData }) => hexlify(ccvData)),
1123
+ BigInt(opts.gasLimit ?? 0),
1124
+ )
1125
+ execTx.gasLimit = txGasLimit + 40000n // plus `execute`'s overhead
1126
+ return { family: ChainFamily.EVM, transactions: [execTx] }
1127
+ }
1054
1128
 
1055
1129
  let manualExecTx
1056
- const offchainTokenData = execReport.offchainTokenData.map(encodeEVMOffchainTokenData)
1130
+ const [_, version] = await this.typeAndVersion(offRamp)
1131
+ const offchainTokenData = input.offchainTokenData.map(encodeEVMOffchainTokenData)
1057
1132
 
1058
1133
  switch (version) {
1059
1134
  case CCIPVersion.V1_2: {
1060
1135
  const contract = new Contract(
1061
1136
  offRamp,
1062
- EVM2EVMOffRamp_1_2_ABI,
1137
+ interfaces.EVM2EVMOffRamp_v1_2,
1063
1138
  this.provider,
1064
1139
  ) as unknown as TypedContract<typeof EVM2EVMOffRamp_1_2_ABI>
1065
1140
  const gasOverride = BigInt(opts.gasLimit ?? 0)
1066
1141
  manualExecTx = await contract.manuallyExecute.populateTransaction(
1067
1142
  {
1068
- ...execReport,
1069
- proofs: execReport.proofs.map((d) => hexlify(d)),
1070
- messages: [execReport.message as CCIPMessage<typeof CCIPVersion.V1_2>],
1143
+ ...input,
1144
+ proofs: input.proofs.map((d) => hexlify(d)),
1145
+ messages: [input.message as CCIPMessage<typeof CCIPVersion.V1_2>],
1071
1146
  offchainTokenData: [offchainTokenData],
1072
1147
  },
1073
1148
  [gasOverride],
@@ -1077,20 +1152,20 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1077
1152
  case CCIPVersion.V1_5: {
1078
1153
  const contract = new Contract(
1079
1154
  offRamp,
1080
- EVM2EVMOffRamp_1_5_ABI,
1155
+ interfaces.EVM2EVMOffRamp_v1_5,
1081
1156
  this.provider,
1082
1157
  ) as unknown as TypedContract<typeof EVM2EVMOffRamp_1_5_ABI>
1083
1158
  manualExecTx = await contract.manuallyExecute.populateTransaction(
1084
1159
  {
1085
- ...execReport,
1086
- proofs: execReport.proofs.map((d) => hexlify(d)),
1087
- messages: [execReport.message as CCIPMessage<typeof CCIPVersion.V1_5>],
1160
+ ...input,
1161
+ proofs: input.proofs.map((d) => hexlify(d)),
1162
+ messages: [input.message as CCIPMessage<typeof CCIPVersion.V1_5>],
1088
1163
  offchainTokenData: [offchainTokenData],
1089
1164
  },
1090
1165
  [
1091
1166
  {
1092
1167
  receiverExecutionGasLimit: BigInt(opts.gasLimit ?? 0),
1093
- tokenGasOverrides: execReport.message.tokenAmounts.map(() =>
1168
+ tokenGasOverrides: input.message.tokenAmounts.map(() =>
1094
1169
  BigInt(opts.tokensGasLimit ?? opts.gasLimit ?? 0),
1095
1170
  ),
1096
1171
  },
@@ -1100,30 +1175,32 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1100
1175
  }
1101
1176
  case CCIPVersion.V1_6: {
1102
1177
  // normalize message
1103
- const sender = zeroPadValue(getAddressBytes(execReport.message.sender), 32)
1104
- const tokenAmounts = (execReport.message as CCIPMessage_V1_6_EVM).tokenAmounts.map(
1105
- (ta) => ({
1106
- ...ta,
1107
- sourcePoolAddress: zeroPadValue(getAddressBytes(ta.sourcePoolAddress), 32),
1108
- extraData: hexlify(getDataBytes(ta.extraData)),
1109
- }),
1110
- )
1178
+ const senderBytes = getAddressBytes(input.message.sender)
1179
+ // Addresses ≤32 bytes (EVM 20B, Aptos/Solana/Sui 32B) are zero-padded to 32 bytes;
1180
+ // Addresses >32 bytes (e.g., TON 36B) are used as raw bytes without padding
1181
+ const sender =
1182
+ senderBytes.length <= 32 ? zeroPadValue(senderBytes, 32) : hexlify(senderBytes)
1183
+ const tokenAmounts = (input.message as CCIPMessage_V1_6_EVM).tokenAmounts.map((ta) => ({
1184
+ ...ta,
1185
+ sourcePoolAddress: zeroPadValue(getAddressBytes(ta.sourcePoolAddress), 32),
1186
+ extraData: hexlify(getDataBytes(ta.extraData)),
1187
+ }))
1111
1188
  const message = {
1112
- ...(execReport.message as CCIPMessage_V1_6_EVM),
1189
+ ...(input.message as CCIPMessage_V1_6_EVM),
1113
1190
  sender,
1114
1191
  tokenAmounts,
1115
1192
  }
1116
1193
  const contract = new Contract(
1117
1194
  offRamp,
1118
- OffRamp_1_6_ABI,
1195
+ interfaces.OffRamp_v1_6,
1119
1196
  this.provider,
1120
1197
  ) as unknown as TypedContract<typeof OffRamp_1_6_ABI>
1121
1198
  manualExecTx = await contract.manuallyExecute.populateTransaction(
1122
1199
  [
1123
1200
  {
1124
- ...execReport,
1125
- proofs: execReport.proofs.map((p) => hexlify(p)),
1126
- sourceChainSelector: execReport.message.sourceChainSelector,
1201
+ ...input,
1202
+ proofs: input.proofs.map((p) => hexlify(p)),
1203
+ sourceChainSelector: input.message.sourceChainSelector,
1127
1204
  messages: [
1128
1205
  {
1129
1206
  ...message,
@@ -1143,7 +1220,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1143
1220
  [
1144
1221
  {
1145
1222
  receiverExecutionGasLimit: BigInt(opts.gasLimit ?? 0),
1146
- tokenGasOverrides: execReport.message.tokenAmounts.map(() =>
1223
+ tokenGasOverrides: input.message.tokenAmounts.map(() =>
1147
1224
  BigInt(opts.tokensGasLimit ?? opts.gasLimit ?? 0),
1148
1225
  ),
1149
1226
  },
@@ -1159,23 +1236,28 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1159
1236
  }
1160
1237
 
1161
1238
  /**
1162
- * {@inheritDoc Chain.executeReport}
1239
+ * {@inheritDoc Chain.execute}
1163
1240
  * @throws {@link CCIPWalletInvalidError} if wallet is not a valid Signer
1164
1241
  * @throws {@link CCIPExecTxNotConfirmedError} if execution transaction fails to confirm
1165
1242
  * @throws {@link CCIPExecTxRevertedError} if execution transaction reverts
1166
1243
  */
1167
- async executeReport(opts: Parameters<Chain['executeReport']>[0]) {
1244
+ async execute(opts: Parameters<Chain['execute']>[0]) {
1168
1245
  const wallet = opts.wallet
1169
1246
  if (!isSigner(wallet)) throw new CCIPWalletInvalidError(wallet)
1170
1247
 
1171
- const unsignedTxs = await this.generateUnsignedExecuteReport({
1248
+ const unsignedTxs = await this.generateUnsignedExecute({
1172
1249
  ...opts,
1173
1250
  payer: await wallet.getAddress(),
1174
1251
  })
1175
- const unsignedTx = await wallet.populateTransaction(unsignedTxs.transactions[0]!)
1176
- unsignedTx.from = undefined // some signers don't like receiving pre-populated `from`
1177
- const response = await submitTransaction(wallet, unsignedTx, this.provider)
1252
+
1253
+ const unsignedTx: TransactionRequest = unsignedTxs.transactions[0]!
1254
+ unsignedTx.nonce = await this.nextNonce(await wallet.getAddress())
1255
+ const populatedTx = await wallet.populateTransaction(unsignedTx)
1256
+ populatedTx.from = undefined // some signers don't like receiving pre-populated `from`
1257
+
1258
+ const response = await submitTransaction(wallet, populatedTx, this.provider)
1178
1259
  this.logger.debug('manuallyExecute =>', response.hash)
1260
+
1179
1261
  const receipt = await response.wait(1, 60_000)
1180
1262
  if (!receipt?.hash) throw new CCIPExecTxNotConfirmedError(response.hash)
1181
1263
  if (!receipt.status) throw new CCIPExecTxRevertedError(response.hash)
@@ -1407,6 +1489,64 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1407
1489
  )
1408
1490
  }
1409
1491
 
1492
+ /** {@inheritDoc Chain.getVerifications} */
1493
+ override async getVerifications(
1494
+ opts: Parameters<Chain['getVerifications']>[0],
1495
+ ): Promise<CCIPVerifications> {
1496
+ const { offRamp, request } = opts
1497
+ if (request.lane.version >= CCIPVersion.V2_0) {
1498
+ const message = request.message as CCIPMessage_V2_0
1499
+ if (!message.encodedMessage)
1500
+ throw new CCIPNotImplementedError(`CCIPAPIClient getMessageById v2 encodedMessage`)
1501
+ const contract = new Contract(
1502
+ offRamp,
1503
+ interfaces.OffRamp_v2_0,
1504
+ this.provider,
1505
+ ) as unknown as TypedContract<typeof OffRamp_2_0_ABI>
1506
+ const ccvs = await contract.getCCVsForMessage(message.encodedMessage)
1507
+ const [requiredCCVs, optionalCCVs, optionalThreshold] = ccvs.map(
1508
+ resultToObject,
1509
+ ) as unknown as CleanAddressable<typeof ccvs>
1510
+ const verificationPolicy = {
1511
+ requiredCCVs,
1512
+ optionalCCVs,
1513
+ optionalThreshold: Number(optionalThreshold),
1514
+ }
1515
+
1516
+ if (this.apiClient) {
1517
+ const apiRes = await this.apiClient.getMessageById(request.message.messageId)
1518
+ if ('verifiers' in apiRes.message) {
1519
+ const verifiers = apiRes.message.verifiers as {
1520
+ items: {
1521
+ destAddress: string
1522
+ sourceAddress: string
1523
+ verification: { data: string; timestamp: string }
1524
+ }[]
1525
+ }
1526
+ return {
1527
+ verificationPolicy,
1528
+ verifications: verifiers.items.map((item) => ({
1529
+ destAddress: item.destAddress,
1530
+ sourceAddress: item.sourceAddress,
1531
+ ccvData: item.verification.data,
1532
+ timestamp: new Date(item.verification.timestamp).getTime() / 1e3,
1533
+ })),
1534
+ }
1535
+ }
1536
+ }
1537
+
1538
+ const url = `${CCV_INDEXER_URL}/v1/verifierresults/${request.message.messageId}`
1539
+ const res = await fetch(url)
1540
+ const json = await res.json()
1541
+ return json as CCIPVerifications
1542
+ } else if (request.lane.version < CCIPVersion.V1_6) {
1543
+ // v1.2..v1.5 EVM (only) have separate CommitStore
1544
+ opts.offRamp = await this.getCommitStoreForOffRamp(opts.offRamp)
1545
+ }
1546
+ // fallback <=v1.6
1547
+ return super.getVerifications(opts)
1548
+ }
1549
+
1410
1550
  /** {@inheritDoc Chain.getExecutionReceipts} */
1411
1551
  override async *getExecutionReceipts(
1412
1552
  opts: Parameters<Chain['getExecutionReceipts']>[0],
@@ -1426,10 +1566,14 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1426
1566
  // onlyFallback: false,
1427
1567
  }
1428
1568
  } else /* >= V1.6 */ {
1569
+ const topicHash =
1570
+ version === CCIPVersion.V1_6
1571
+ ? interfaces.OffRamp_v1_6.getEvent('ExecutionStateChanged')!.topicHash
1572
+ : interfaces.OffRamp_v2_0.getEvent('ExecutionStateChanged')!.topicHash
1429
1573
  opts_ = {
1430
1574
  ...opts,
1431
1575
  topics: [
1432
- interfaces.OffRamp_v1_6.getEvent('ExecutionStateChanged')!.topicHash,
1576
+ topicHash,
1433
1577
  sourceChainSelector ? toBeHex(sourceChainSelector, 32) : null,
1434
1578
  null,
1435
1579
  messageId ?? null,