@chainlink/ccip-sdk 0.95.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 (217) hide show
  1. package/README.md +2 -2
  2. package/dist/all-chains.d.ts +23 -0
  3. package/dist/all-chains.d.ts.map +1 -0
  4. package/dist/all-chains.js +24 -0
  5. package/dist/all-chains.js.map +1 -0
  6. package/dist/api/index.d.ts +31 -19
  7. package/dist/api/index.d.ts.map +1 -1
  8. package/dist/api/index.js +46 -25
  9. package/dist/api/index.js.map +1 -1
  10. package/dist/api/types.d.ts +24 -30
  11. package/dist/api/types.d.ts.map +1 -1
  12. package/dist/aptos/exec.d.ts +2 -2
  13. package/dist/aptos/exec.d.ts.map +1 -1
  14. package/dist/aptos/exec.js.map +1 -1
  15. package/dist/aptos/hasher.d.ts.map +1 -1
  16. package/dist/aptos/hasher.js +1 -1
  17. package/dist/aptos/hasher.js.map +1 -1
  18. package/dist/aptos/index.d.ts +43 -15
  19. package/dist/aptos/index.d.ts.map +1 -1
  20. package/dist/aptos/index.js +112 -105
  21. package/dist/aptos/index.js.map +1 -1
  22. package/dist/aptos/types.d.ts +2 -19
  23. package/dist/aptos/types.d.ts.map +1 -1
  24. package/dist/aptos/types.js +0 -11
  25. package/dist/aptos/types.js.map +1 -1
  26. package/dist/chain.d.ts +734 -174
  27. package/dist/chain.d.ts.map +1 -1
  28. package/dist/chain.js +216 -31
  29. package/dist/chain.js.map +1 -1
  30. package/dist/commits.d.ts +4 -6
  31. package/dist/commits.d.ts.map +1 -1
  32. package/dist/commits.js +4 -4
  33. package/dist/commits.js.map +1 -1
  34. package/dist/errors/CCIPError.d.ts +33 -4
  35. package/dist/errors/CCIPError.d.ts.map +1 -1
  36. package/dist/errors/CCIPError.js +33 -4
  37. package/dist/errors/CCIPError.js.map +1 -1
  38. package/dist/errors/codes.d.ts +5 -0
  39. package/dist/errors/codes.d.ts.map +1 -1
  40. package/dist/errors/codes.js +5 -1
  41. package/dist/errors/codes.js.map +1 -1
  42. package/dist/errors/index.d.ts +2 -2
  43. package/dist/errors/index.d.ts.map +1 -1
  44. package/dist/errors/index.js +2 -2
  45. package/dist/errors/index.js.map +1 -1
  46. package/dist/errors/recovery.d.ts.map +1 -1
  47. package/dist/errors/recovery.js +6 -1
  48. package/dist/errors/recovery.js.map +1 -1
  49. package/dist/errors/specialized.d.ts +1702 -121
  50. package/dist/errors/specialized.d.ts.map +1 -1
  51. package/dist/errors/specialized.js +1729 -125
  52. package/dist/errors/specialized.js.map +1 -1
  53. package/dist/errors/utils.d.ts.map +1 -1
  54. package/dist/errors/utils.js +0 -1
  55. package/dist/errors/utils.js.map +1 -1
  56. package/dist/evm/abi/OffRamp_2_0.d.ts +764 -0
  57. package/dist/evm/abi/OffRamp_2_0.d.ts.map +1 -0
  58. package/dist/evm/abi/OffRamp_2_0.js +744 -0
  59. package/dist/evm/abi/OffRamp_2_0.js.map +1 -0
  60. package/dist/evm/abi/OnRamp_2_0.d.ts +925 -0
  61. package/dist/evm/abi/OnRamp_2_0.d.ts.map +1 -0
  62. package/dist/evm/abi/OnRamp_2_0.js +992 -0
  63. package/dist/evm/abi/OnRamp_2_0.js.map +1 -0
  64. package/dist/evm/const.d.ts +12 -2
  65. package/dist/evm/const.d.ts.map +1 -1
  66. package/dist/evm/const.js +8 -2
  67. package/dist/evm/const.js.map +1 -1
  68. package/dist/evm/errors.d.ts.map +1 -1
  69. package/dist/evm/errors.js +7 -2
  70. package/dist/evm/errors.js.map +1 -1
  71. package/dist/evm/extra-args.d.ts +25 -0
  72. package/dist/evm/extra-args.d.ts.map +1 -0
  73. package/dist/evm/extra-args.js +309 -0
  74. package/dist/evm/extra-args.js.map +1 -0
  75. package/dist/evm/gas.d.ts.map +1 -1
  76. package/dist/evm/gas.js +7 -12
  77. package/dist/evm/gas.js.map +1 -1
  78. package/dist/evm/hasher.d.ts.map +1 -1
  79. package/dist/evm/hasher.js +23 -13
  80. package/dist/evm/hasher.js.map +1 -1
  81. package/dist/evm/index.d.ts +140 -35
  82. package/dist/evm/index.d.ts.map +1 -1
  83. package/dist/evm/index.js +306 -226
  84. package/dist/evm/index.js.map +1 -1
  85. package/dist/evm/messages.d.ts +59 -5
  86. package/dist/evm/messages.d.ts.map +1 -1
  87. package/dist/evm/messages.js +210 -0
  88. package/dist/evm/messages.js.map +1 -1
  89. package/dist/evm/offchain.js.map +1 -1
  90. package/dist/evm/types.d.ts +7 -2
  91. package/dist/evm/types.d.ts.map +1 -1
  92. package/dist/evm/types.js +22 -1
  93. package/dist/evm/types.js.map +1 -1
  94. package/dist/execution.d.ts +62 -22
  95. package/dist/execution.d.ts.map +1 -1
  96. package/dist/execution.js +102 -51
  97. package/dist/execution.js.map +1 -1
  98. package/dist/extra-args.d.ts +113 -4
  99. package/dist/extra-args.d.ts.map +1 -1
  100. package/dist/extra-args.js +38 -3
  101. package/dist/extra-args.js.map +1 -1
  102. package/dist/gas.d.ts +31 -5
  103. package/dist/gas.d.ts.map +1 -1
  104. package/dist/gas.js +43 -9
  105. package/dist/gas.js.map +1 -1
  106. package/dist/index.d.ts +11 -10
  107. package/dist/index.d.ts.map +1 -1
  108. package/dist/index.js +8 -8
  109. package/dist/index.js.map +1 -1
  110. package/dist/requests.d.ts +101 -22
  111. package/dist/requests.d.ts.map +1 -1
  112. package/dist/requests.js +115 -24
  113. package/dist/requests.js.map +1 -1
  114. package/dist/selectors.d.ts.map +1 -1
  115. package/dist/selectors.js +24 -0
  116. package/dist/selectors.js.map +1 -1
  117. package/dist/shared/bcs-codecs.d.ts +61 -0
  118. package/dist/shared/bcs-codecs.d.ts.map +1 -0
  119. package/dist/shared/bcs-codecs.js +102 -0
  120. package/dist/shared/bcs-codecs.js.map +1 -0
  121. package/dist/shared/constants.d.ts +3 -0
  122. package/dist/shared/constants.d.ts.map +1 -0
  123. package/dist/shared/constants.js +3 -0
  124. package/dist/shared/constants.js.map +1 -0
  125. package/dist/solana/exec.d.ts +2 -2
  126. package/dist/solana/exec.d.ts.map +1 -1
  127. package/dist/solana/exec.js.map +1 -1
  128. package/dist/solana/index.d.ts +148 -30
  129. package/dist/solana/index.d.ts.map +1 -1
  130. package/dist/solana/index.js +137 -44
  131. package/dist/solana/index.js.map +1 -1
  132. package/dist/sui/hasher.d.ts.map +1 -1
  133. package/dist/sui/hasher.js +1 -1
  134. package/dist/sui/hasher.js.map +1 -1
  135. package/dist/sui/index.d.ts +49 -19
  136. package/dist/sui/index.d.ts.map +1 -1
  137. package/dist/sui/index.js +76 -43
  138. package/dist/sui/index.js.map +1 -1
  139. package/dist/sui/manuallyExec/encoder.d.ts +2 -2
  140. package/dist/sui/manuallyExec/encoder.d.ts.map +1 -1
  141. package/dist/sui/manuallyExec/encoder.js.map +1 -1
  142. package/dist/sui/manuallyExec/index.d.ts +2 -2
  143. package/dist/sui/manuallyExec/index.d.ts.map +1 -1
  144. package/dist/ton/exec.d.ts +2 -2
  145. package/dist/ton/exec.d.ts.map +1 -1
  146. package/dist/ton/exec.js.map +1 -1
  147. package/dist/ton/index.d.ts +66 -27
  148. package/dist/ton/index.d.ts.map +1 -1
  149. package/dist/ton/index.js +172 -47
  150. package/dist/ton/index.js.map +1 -1
  151. package/dist/ton/send.d.ts +52 -0
  152. package/dist/ton/send.d.ts.map +1 -0
  153. package/dist/ton/send.js +166 -0
  154. package/dist/ton/send.js.map +1 -0
  155. package/dist/ton/types.d.ts +2 -2
  156. package/dist/ton/types.d.ts.map +1 -1
  157. package/dist/ton/types.js.map +1 -1
  158. package/dist/types.d.ts +148 -12
  159. package/dist/types.d.ts.map +1 -1
  160. package/dist/types.js +6 -1
  161. package/dist/types.js.map +1 -1
  162. package/dist/utils.d.ts +79 -4
  163. package/dist/utils.d.ts.map +1 -1
  164. package/dist/utils.js +92 -7
  165. package/dist/utils.js.map +1 -1
  166. package/package.json +16 -11
  167. package/src/all-chains.ts +26 -0
  168. package/src/api/index.ts +58 -34
  169. package/src/api/types.ts +24 -31
  170. package/src/aptos/exec.ts +2 -2
  171. package/src/aptos/hasher.ts +1 -1
  172. package/src/aptos/index.ts +127 -129
  173. package/src/aptos/types.ts +2 -15
  174. package/src/chain.ts +837 -191
  175. package/src/commits.ts +9 -9
  176. package/src/errors/CCIPError.ts +33 -4
  177. package/src/errors/codes.ts +5 -1
  178. package/src/errors/index.ts +2 -1
  179. package/src/errors/recovery.ts +9 -1
  180. package/src/errors/specialized.ts +1745 -132
  181. package/src/errors/utils.ts +0 -1
  182. package/src/evm/abi/OffRamp_2_0.ts +743 -0
  183. package/src/evm/abi/OnRamp_2_0.ts +991 -0
  184. package/src/evm/const.ts +10 -3
  185. package/src/evm/errors.ts +6 -2
  186. package/src/evm/extra-args.ts +360 -0
  187. package/src/evm/gas.ts +14 -13
  188. package/src/evm/hasher.ts +30 -18
  189. package/src/evm/index.ts +376 -281
  190. package/src/evm/messages.ts +323 -11
  191. package/src/evm/offchain.ts +2 -2
  192. package/src/evm/types.ts +20 -2
  193. package/src/execution.ts +126 -71
  194. package/src/extra-args.ts +118 -4
  195. package/src/gas.ts +44 -11
  196. package/src/index.ts +14 -11
  197. package/src/requests.ts +128 -24
  198. package/src/selectors.ts +24 -0
  199. package/src/shared/bcs-codecs.ts +132 -0
  200. package/src/shared/constants.ts +2 -0
  201. package/src/solana/exec.ts +4 -4
  202. package/src/solana/index.ts +170 -82
  203. package/src/sui/hasher.ts +1 -1
  204. package/src/sui/index.ts +88 -56
  205. package/src/sui/manuallyExec/encoder.ts +2 -2
  206. package/src/sui/manuallyExec/index.ts +2 -2
  207. package/src/ton/exec.ts +2 -2
  208. package/src/ton/index.ts +220 -58
  209. package/src/ton/send.ts +222 -0
  210. package/src/ton/types.ts +2 -2
  211. package/src/types.ts +173 -30
  212. package/src/utils.ts +91 -7
  213. package/dist/aptos/utils.d.ts +0 -12
  214. package/dist/aptos/utils.d.ts.map +0 -1
  215. package/dist/aptos/utils.js +0 -15
  216. package/dist/aptos/utils.js.map +0 -1
  217. package/src/aptos/utils.ts +0 -24
package/src/chain.ts CHANGED
@@ -3,37 +3,41 @@ import type { PickDeep, SetOptional } from 'type-fest'
3
3
 
4
4
  import { type LaneLatencyResponse, CCIPAPIClient } from './api/index.ts'
5
5
  import type { UnsignedAptosTx } from './aptos/types.ts'
6
- import { getCommitReport } from './commits.ts'
6
+ import { getOnchainCommitReport } from './commits.ts'
7
7
  import {
8
8
  CCIPApiClientNotAvailableError,
9
9
  CCIPChainFamilyMismatchError,
10
10
  CCIPExecTxRevertedError,
11
+ CCIPTokenPoolChainConfigNotFoundError,
11
12
  CCIPTransactionNotFinalizedError,
12
13
  } from './errors/index.ts'
13
- import { DEFAULT_GAS_LIMIT } from './evm/const.ts'
14
14
  import type { UnsignedEVMTx } from './evm/types.ts'
15
+ import { calculateManualExecProof } from './execution.ts'
15
16
  import type {
16
17
  EVMExtraArgsV1,
17
18
  EVMExtraArgsV2,
18
19
  ExtraArgs,
20
+ GenericExtraArgsV3,
19
21
  SVMExtraArgsV1,
20
22
  SuiExtraArgsV1,
21
23
  } from './extra-args.ts'
22
24
  import type { LeafHasher } from './hasher/common.ts'
23
25
  import { getMessagesInTx } from './requests.ts'
26
+ import { DEFAULT_GAS_LIMIT } from './shared/constants.ts'
24
27
  import type { UnsignedSolanaTx } from './solana/types.ts'
25
28
  import type { UnsignedTONTx } from './ton/types.ts'
26
29
  import {
27
30
  type AnyMessage,
28
- type CCIPCommit,
29
31
  type CCIPExecution,
30
32
  type CCIPMessage,
31
33
  type CCIPRequest,
34
+ type CCIPVerifications,
35
+ type CCIPVersion,
32
36
  type ChainFamily,
33
37
  type ChainTransaction,
34
38
  type CommitReport,
39
+ type ExecutionInput,
35
40
  type ExecutionReceipt,
36
- type ExecutionReport,
37
41
  type Lane,
38
42
  type Log_,
39
43
  type Logger,
@@ -43,7 +47,24 @@ import {
43
47
  type WithLogger,
44
48
  ExecutionState,
45
49
  } from './types.ts'
46
- import { util, withRetry } from './utils.ts'
50
+ import { networkInfo, util, withRetry } from './utils.ts'
51
+
52
+ /** Field names unique to GenericExtraArgsV3 (not present in V2). */
53
+ const V3_ONLY_FIELDS = [
54
+ 'blockConfirmations',
55
+ 'ccvs',
56
+ 'ccvArgs',
57
+ 'executor',
58
+ 'executorArgs',
59
+ 'tokenReceiver',
60
+ 'tokenArgs',
61
+ ] as const
62
+
63
+ /** Check if extraArgs contains any V3-only fields. */
64
+ function hasV3ExtraArgs(extraArgs: Partial<ExtraArgs> | undefined): boolean {
65
+ if (!extraArgs) return false
66
+ return V3_ONLY_FIELDS.some((field) => field in extraArgs)
67
+ }
47
68
 
48
69
  /**
49
70
  * Context for Chain class initialization.
@@ -57,7 +78,7 @@ import { util, withRetry } from './utils.ts'
57
78
  *
58
79
  * @example Custom API endpoint
59
80
  * ```typescript
60
- * const api = new CCIPAPIClient('https://staging-api.example.com', { logger })
81
+ * const api = CCIPAPIClient.fromUrl('https://staging-api.example.com', { logger })
61
82
  * const chain = await EVMChain.fromUrl(rpcUrl, { apiClient: api, logger })
62
83
  * ```
63
84
  *
@@ -71,8 +92,7 @@ export type ChainContext = WithLogger & {
71
92
  /**
72
93
  * CCIP API client instance for lane information queries.
73
94
  *
74
- * - `undefined` (default): Creates CCIPAPIClient with production endpoint
75
- * (https://api.ccip.chain.link)
95
+ * - `undefined` (default): Creates CCIPAPIClient with {@link DEFAULT_API_BASE_URL}
76
96
  * - `CCIPAPIClient`: Uses provided instance (allows custom URL, fetch, etc.)
77
97
  * - `null`: Disables API client entirely (getLaneLatency() will throw)
78
98
  *
@@ -164,24 +184,51 @@ export type GetBalanceOpts = {
164
184
 
165
185
  /**
166
186
  * Rate limiter state for token pool configurations.
167
- * Null if rate limiting is disabled.
187
+ *
188
+ * @remarks
189
+ * - Returns the rate limiter bucket state when rate limiting is **enabled**
190
+ * - Returns `null` when rate limiting is **disabled** (unlimited throughput)
191
+ *
192
+ * @example Handling nullable state
193
+ * ```typescript
194
+ * const remote = await chain.getTokenPoolRemotes(poolAddress)
195
+ * const state = remote['ethereum-mainnet'].inboundRateLimiterState
196
+ *
197
+ * if (state === null) {
198
+ * console.log('Rate limiting disabled - unlimited throughput')
199
+ * } else {
200
+ * console.log(`Capacity: ${state.capacity}, Available: ${state.tokens}`)
201
+ * }
202
+ * ```
168
203
  */
169
204
  export type RateLimiterState = {
170
205
  /** Current token balance in the rate limiter bucket. */
171
206
  tokens: bigint
172
207
  /** Maximum capacity of the rate limiter bucket. */
173
208
  capacity: bigint
174
- /** Rate at which tokens are replenished. */
209
+ /** Rate at which tokens are replenished (tokens per second). */
175
210
  rate: bigint
176
211
  } | null
177
212
 
178
213
  /**
179
- * Remote token pool configuration for a specific chain.
214
+ * Remote token pool configuration for a specific destination chain.
215
+ *
216
+ * @remarks
217
+ * Each entry represents the configuration needed to transfer tokens
218
+ * from the current chain to a specific destination chain.
180
219
  */
181
220
  export type TokenPoolRemote = {
182
221
  /** Address of the remote token on the destination chain. */
183
222
  remoteToken: string
184
- /** Addresses of remote token pools. */
223
+ /**
224
+ * Addresses of remote token pools on the destination chain.
225
+ *
226
+ * @remarks
227
+ * Multiple pools may exist for:
228
+ * - Redundancy (failover if one pool is unavailable)
229
+ * - Capacity aggregation across pools
230
+ * - Version management (different pool implementations)
231
+ */
185
232
  remotePools: string[]
186
233
  /** Inbound rate limiter state for tokens coming into this chain. */
187
234
  inboundRateLimiterState: RateLimiterState
@@ -189,6 +236,43 @@ export type TokenPoolRemote = {
189
236
  outboundRateLimiterState: RateLimiterState
190
237
  }
191
238
 
239
+ /**
240
+ * Token pool configuration returned by {@link Chain.getTokenPoolConfig}.
241
+ *
242
+ * @remarks
243
+ * Contains the core configuration of a token pool including the token it manages,
244
+ * the router it's registered with, and optionally its version identifier.
245
+ */
246
+ export type TokenPoolConfig = {
247
+ /** Address of the token managed by this pool. */
248
+ token: string
249
+ /** Address of the CCIP router this pool is registered with. */
250
+ router: string
251
+ /**
252
+ * Version identifier string (e.g., "BurnMintTokenPool 1.5.1").
253
+ *
254
+ * @remarks
255
+ * May be undefined for older pool implementations that don't expose this method.
256
+ */
257
+ typeAndVersion?: string
258
+ }
259
+
260
+ /**
261
+ * Token configuration from a TokenAdminRegistry, returned by {@link Chain.getRegistryTokenConfig}.
262
+ *
263
+ * @remarks
264
+ * The TokenAdminRegistry tracks which administrator controls each token
265
+ * and which pool is authorized to handle transfers.
266
+ */
267
+ export type RegistryTokenConfig = {
268
+ /** Address of the current administrator for this token. */
269
+ administrator: string
270
+ /** Address of pending administrator (if ownership transfer is in progress). */
271
+ pendingAdministrator?: string
272
+ /** Address of the token pool authorized to handle this token's transfers. */
273
+ tokenPool?: string
274
+ }
275
+
192
276
  /**
193
277
  * Maps chain family to respective unsigned transaction type.
194
278
  */
@@ -202,7 +286,7 @@ export type UnsignedTx = {
202
286
  }
203
287
 
204
288
  /**
205
- * Common options for [[getFee]], [[generateUnsignedSendMessage]] and [[sendMessage]] Chain methods
289
+ * Common options for {@link Chain.getFee}, {@link Chain.generateUnsignedSendMessage} and {@link Chain.sendMessage} methods.
206
290
  */
207
291
  export type SendMessageOpts = {
208
292
  /** Router address on this chain */
@@ -216,13 +300,23 @@ export type SendMessageOpts = {
216
300
  }
217
301
 
218
302
  /**
219
- * Common options for [[generateUnsignedExecuteReport]] and [[executeReport]] Chain methods
303
+ * Common options for {@link Chain.generateUnsignedExecute} and {@link Chain.execute} methods.
220
304
  */
221
- export type ExecuteReportOpts = {
222
- /** address of the OffRamp contract */
223
- offRamp: string
224
- /** execution report */
225
- execReport: ExecutionReport
305
+ export type ExecuteOpts = (
306
+ | {
307
+ /** address of the OffRamp contract */
308
+ offRamp: string
309
+ /** input payload to execute message; contains proofs for v1 and verifications for v2 */
310
+ input: ExecutionInput
311
+ }
312
+ | {
313
+ /**
314
+ * messageId of message to execute; requires `apiClient`.
315
+ * @remarks Currently throws CCIPNotImplementedError - API endpoint pending.
316
+ */
317
+ messageId: string
318
+ }
319
+ ) & {
226
320
  /** gasLimit or computeUnits limit override for the ccipReceive call */
227
321
  gasLimit?: number
228
322
  /** For EVM, overrides gasLimit on tokenPool call */
@@ -249,6 +343,7 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
249
343
  * Base constructor for Chain class.
250
344
  * @param network - NetworkInfo object for the Chain instance
251
345
  * @param ctx - Optional context with logger and API client configuration
346
+ * @throws {@link CCIPChainFamilyMismatchError} if network family doesn't match the Chain subclass
252
347
  */
253
348
  constructor(network: NetworkInfo, ctx?: ChainContext) {
254
349
  const { logger = console, apiClient, apiRetryConfig } = ctx ?? {}
@@ -270,7 +365,7 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
270
365
  this.apiClient = apiClient // Use provided instance
271
366
  this.apiRetryConfig = { ...DEFAULT_API_RETRY_CONFIG, ...apiRetryConfig }
272
367
  } else {
273
- this.apiClient = new CCIPAPIClient(undefined, { logger }) // Default
368
+ this.apiClient = CCIPAPIClient.fromUrl(undefined, { logger }) // Default
274
369
  this.apiRetryConfig = { ...DEFAULT_API_RETRY_CONFIG, ...apiRetryConfig }
275
370
  }
276
371
  }
@@ -284,20 +379,63 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
284
379
  }
285
380
 
286
381
  /**
287
- * Fetch the timestamp of a given block
288
- * @param block - positive block number, negative finality depth or 'finalized' tag
289
- * @returns timestamp of the block, in seconds
382
+ * Fetch the timestamp of a given block.
383
+ *
384
+ * @param block - Positive block number, negative finality depth, or 'finalized' tag
385
+ * @returns Promise resolving to timestamp of the block, in seconds
386
+ *
387
+ * @throws {@link CCIPBlockNotFoundError} if block does not exist
388
+ *
389
+ * @example Get finalized block timestamp
390
+ * ```typescript
391
+ * const chain = await EVMChain.fromUrl('https://eth-mainnet.example.com')
392
+ * const timestamp = await chain.getBlockTimestamp('finalized')
393
+ * console.log(`Finalized at: ${new Date(timestamp * 1000).toISOString()}`)
394
+ * ```
290
395
  */
291
396
  abstract getBlockTimestamp(block: number | 'finalized'): Promise<number>
292
397
  /**
293
- * Fetch a transaction by its hash
294
- * @param hash - transaction hash
295
- * @returns generic transaction details
398
+ * Fetch a transaction by its hash.
399
+ *
400
+ * @param hash - Transaction hash
401
+ * @returns Promise resolving to generic transaction details
402
+ *
403
+ * @throws {@link CCIPTransactionNotFoundError} if transaction does not exist (transient)
404
+ *
405
+ * @example Fetch transaction details
406
+ * ```typescript
407
+ * const chain = await EVMChain.fromUrl('https://eth-mainnet.example.com')
408
+ * try {
409
+ * const tx = await chain.getTransaction('0xabc123...')
410
+ * console.log(`Block: ${tx.blockNumber}, Timestamp: ${tx.timestamp}`)
411
+ * } catch (err) {
412
+ * if (err instanceof CCIPTransactionNotFoundError && err.isTransient) {
413
+ * // Transaction may be pending
414
+ * }
415
+ * }
416
+ * ```
296
417
  */
297
418
  abstract getTransaction(hash: string): Promise<ChainTransaction>
298
419
  /**
299
- * Confirm a log tx is finalized or wait for it to be finalized
300
- * Throws if it isn't included (e.g. a reorg)
420
+ * Confirm a log tx is finalized or wait for it to be finalized.
421
+ *
422
+ * @param opts - Options containing the request, finality level, and optional cancel promise
423
+ * @returns true when the transaction is finalized
424
+ *
425
+ * @throws {@link CCIPTransactionNotFinalizedError} if the transaction is not included (e.g., due to a reorg)
426
+ *
427
+ * @example Wait for message finality
428
+ * ```typescript
429
+ * const request = await source.getMessagesInTx(txHash)
430
+ * try {
431
+ * await source.waitFinalized({ request: request[0] })
432
+ * console.log('Transaction finalized')
433
+ * } catch (err) {
434
+ * if (err instanceof CCIPTransactionNotFinalizedError) {
435
+ * console.log('Transaction not yet finalized')
436
+ * }
437
+ * }
438
+ * ```
301
439
  */
302
440
  async waitFinalized({
303
441
  request: { log, tx },
@@ -359,14 +497,30 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
359
497
  * - `page`: if provided, try to use this page/range for batches
360
498
  * - `watch`: true or cancellation promise, getLogs continuously after initial fetch
361
499
  * @returns An async iterable iterator of logs.
500
+ * @throws {@link CCIPLogsWatchRequiresFinalityError} if watch mode is used without a finality endBlock tag
501
+ * @throws {@link CCIPLogsWatchRequiresStartError} if watch mode is used without startBlock or startTime
502
+ * @throws {@link CCIPLogsAddressRequiredError} if address is required but not provided (chain-specific)
362
503
  */
363
504
  abstract getLogs(opts: LogFilter): AsyncIterableIterator<Log_>
364
505
 
365
506
  /**
366
- * Fetch all CCIP requests in a transaction
507
+ * Fetch all CCIP requests in a transaction.
508
+ *
367
509
  * @param tx - ChainTransaction or txHash to fetch requests from
368
- * @returns CCIP messages in the transaction (at least one)
369
- **/
510
+ * @returns Promise resolving to CCIP messages in the transaction (at least one)
511
+ *
512
+ * @throws {@link CCIPTransactionNotFoundError} if transaction does not exist
513
+ * @throws {@link CCIPMessageNotFoundInTxError} if no CCIPSendRequested events in tx
514
+ *
515
+ * @example Get messages from transaction
516
+ * ```typescript
517
+ * const chain = await EVMChain.fromUrl('https://eth-mainnet.example.com')
518
+ * const requests = await chain.getMessagesInTx('0xabc123...')
519
+ * for (const req of requests) {
520
+ * console.log(`Message ID: ${req.message.messageId}`)
521
+ * }
522
+ * ```
523
+ */
370
524
  async getMessagesInTx(tx: string | ChainTransaction): Promise<CCIPRequest[]> {
371
525
  const txHash = typeof tx === 'string' ? tx : tx.hash
372
526
  try {
@@ -396,13 +550,33 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
396
550
  }
397
551
 
398
552
  /**
399
- * Fetch a message by ID.
400
- * Default implementation just tries API.
401
- * Children may override to fetch from chain as fallback
402
- * @param messageId - message ID to fetch request for
403
- * @param _opts - onRamp may be required in some implementations, and throw if missing
404
- * @returns CCIPRequest
405
- **/
553
+ * Fetch a CCIP message by its unique message ID.
554
+ *
555
+ * @remarks
556
+ * Uses the CCIP API to retrieve message details. The returned request includes
557
+ * a `metadata` field with API-specific information.
558
+ *
559
+ * @example
560
+ * ```typescript
561
+ * const request = await chain.getMessageById(messageId)
562
+ * console.log(`Sender: ${request.message.sender}`)
563
+ *
564
+ * if (request.metadata) {
565
+ * console.log(`Status: ${request.metadata.status}`)
566
+ * if (request.metadata.deliveryTime) {
567
+ * console.log(`Delivered in ${request.metadata.deliveryTime}ms`)
568
+ * }
569
+ * }
570
+ * ```
571
+ *
572
+ * @param messageId - The unique message ID (0x + 64 hex chars)
573
+ * @param _opts - Optional: `onRamp` hint for non-EVM chains
574
+ * @returns CCIPRequest with `metadata` populated from API
575
+ * @throws {@link CCIPApiClientNotAvailableError} if API disabled
576
+ * @throws {@link CCIPMessageIdNotFoundError} if message not found
577
+ * @throws {@link CCIPOnRampRequiredError} if onRamp is required but not provided
578
+ * @throws {@link CCIPHttpError} if API request fails
579
+ */
406
580
  async getMessageById(
407
581
  messageId: string,
408
582
  _opts?: { page?: number; onRamp?: string },
@@ -417,93 +591,213 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
417
591
 
418
592
  /**
419
593
  * Fetches all CCIP messages contained in a given commit batch.
420
- * @param request - CCIPRequest to fetch batch for.
421
- * @param commit - CommitReport range (min, max).
422
- * @param opts - Optional parameters (e.g., `page` for pagination width).
594
+ * To be implemented for chains supporting CCIPVersion &lt;= v1.6.0
595
+ *
596
+ * @param request - CCIPRequest to fetch batch for
597
+ * @param range - batch range \{ minSeqnr, maxSeqNr \}, e.g. from [[CommitReport]]
598
+ * @param opts - Optional parameters (e.g., `page` for pagination width)
599
+ * @returns Array of messages in the batch
600
+ *
601
+ * @throws {@link CCIPMessageBatchIncompleteError} if not all messages in range could be fetched
602
+ *
603
+ * @example Get all messages in a batch
604
+ * ```typescript
605
+ * const verifications = await dest.getVerifications({ offRamp, request })
606
+ * const messages = await source.getMessagesInBatch(request, verifications.report)
607
+ * console.log(`Found ${messages.length} messages in batch`)
608
+ * ```
423
609
  */
424
- abstract getMessagesInBatch<
610
+ getMessagesInBatch?<
425
611
  R extends PickDeep<
426
612
  CCIPRequest,
427
613
  'lane' | `log.${'topics' | 'address' | 'blockNumber'}` | 'message.sequenceNumber'
428
614
  >,
429
615
  >(
430
616
  request: R,
431
- commit: Pick<CommitReport, 'minSeqNr' | 'maxSeqNr'>,
617
+ range: Pick<CommitReport, 'minSeqNr' | 'maxSeqNr'>,
432
618
  opts?: { page?: number },
433
619
  ): Promise<R['message'][]>
620
+
621
+ /**
622
+ * Fetch input data needed for executing messages
623
+ * Should be called on the *source* instance
624
+ * @param opts - getExecutionInput options containing request and verifications
625
+ * @returns `input` payload to be passed to [[execute]]
626
+ * @see {@link execute} - method to execute a message
627
+ */
628
+ async getExecutionInput({
629
+ request,
630
+ verifications,
631
+ ...opts
632
+ }: {
633
+ request: CCIPRequest
634
+ verifications: CCIPVerifications
635
+ } & Pick<LogFilter, 'page'>): Promise<ExecutionInput> {
636
+ if ('verifications' in verifications) {
637
+ // >=v2 verifications is enough for execution
638
+ return {
639
+ encodedMessage: (request.message as CCIPMessage<typeof CCIPVersion.V2_0>).encodedMessage,
640
+ ...verifications,
641
+ }
642
+ }
643
+ // other messages in same batch are available from `source` side;
644
+ // not needed for chain families supporting only >=v2
645
+ const messagesInBatch = await this.getMessagesInBatch!(request, verifications.report, opts)
646
+ const execReportProof = calculateManualExecProof(
647
+ messagesInBatch,
648
+ request.lane,
649
+ request.message.messageId,
650
+ verifications.report.merkleRoot,
651
+ this,
652
+ )
653
+ const offchainTokenData = await this.getOffchainTokenData(request)
654
+ return {
655
+ ...execReportProof,
656
+ message: request.message,
657
+ offchainTokenData,
658
+ } as ExecutionInput
659
+ }
434
660
  /**
435
- * Fetch typeAndVersion for a given CCIP contract address
661
+ * Fetch typeAndVersion for a given CCIP contract address.
662
+ *
436
663
  * @param address - CCIP contract address
437
- * @returns type - parsed type of the contract, e.g. `OnRamp`
438
- * @returns version - parsed version of the contract, e.g. `1.6.0`
439
- * @returns typeAndVersion - original (unparsed) typeAndVersion() string
440
- * @returns suffix - suffix of the version, if any (e.g. `-dev`)
664
+ * @returns Promise resolving to tuple:
665
+ * - `type` - Parsed type of the contract, e.g. `OnRamp`
666
+ * - `version` - Parsed version of the contract, e.g. `1.6.0`
667
+ * - `typeAndVersion` - Original (unparsed) typeAndVersion() string
668
+ * - `suffix` - Suffix of the version, if any (e.g. `-dev`)
669
+ *
670
+ * @throws {@link CCIPTypeVersionInvalidError} if typeAndVersion string cannot be parsed
671
+ *
672
+ * @example Check contract version
673
+ * ```typescript
674
+ * const [type, version] = await chain.typeAndVersion(contractAddress)
675
+ * console.log(`Contract: ${type} v${version}`)
676
+ * if (version < '1.6.0') {
677
+ * console.log('Legacy contract detected')
678
+ * }
679
+ * ```
441
680
  */
442
681
  abstract typeAndVersion(
443
682
  address: string,
444
683
  ): Promise<[type: string, version: string, typeAndVersion: string, suffix?: string]>
445
684
 
446
685
  /**
447
- * Fetch the Router address set in OnRamp config
448
- * Used to discover OffRamp connected to OnRamp
686
+ * Fetch the Router address set in OnRamp config.
687
+ * Used to discover OffRamp connected to OnRamp.
688
+ *
449
689
  * @param onRamp - OnRamp contract address
450
- * @param destChainSelector - destination chain selector
451
- * @returns Router address
690
+ * @param destChainSelector - Destination chain selector
691
+ * @returns Promise resolving to Router address
692
+ *
693
+ * @throws {@link CCIPContractTypeInvalidError} if address is not an OnRamp
694
+ *
695
+ * @example Get router from onRamp
696
+ * ```typescript
697
+ * const router = await chain.getRouterForOnRamp(onRampAddress, destSelector)
698
+ * console.log(`Router: ${router}`)
699
+ * ```
452
700
  */
453
701
  abstract getRouterForOnRamp(onRamp: string, destChainSelector: bigint): Promise<string>
454
702
  /**
455
- * Fetch the Router address set in OffRamp config
703
+ * Fetch the Router address set in OffRamp config.
704
+ *
456
705
  * @param offRamp - OffRamp contract address
457
- * @param sourceChainSelector - source chain selector
458
- * @returns Router address
706
+ * @param sourceChainSelector - Source chain selector
707
+ * @returns Promise resolving to Router address
708
+ *
709
+ * @throws {@link CCIPContractTypeInvalidError} if address is not an OffRamp
710
+ *
711
+ * @example Get router from offRamp
712
+ * ```typescript
713
+ * const router = await chain.getRouterForOffRamp(offRampAddress, sourceSelector)
714
+ * console.log(`Router: ${router}`)
715
+ * ```
459
716
  */
460
717
  abstract getRouterForOffRamp(offRamp: string, sourceChainSelector: bigint): Promise<string>
461
718
  /**
462
- * Get the native token address for a Router
463
- * @param router - router contract address
464
- * @returns native token address (usually wrapped)
719
+ * Get the native token address for a Router.
720
+ *
721
+ * @param router - Router contract address
722
+ * @returns Promise resolving to native token address (usually wrapped)
723
+ *
724
+ * @example Get wrapped native token
725
+ * ```typescript
726
+ * const weth = await chain.getNativeTokenForRouter(routerAddress)
727
+ * console.log(`Wrapped native: ${weth}`)
728
+ * ```
465
729
  */
466
730
  abstract getNativeTokenForRouter(router: string): Promise<string>
467
731
  /**
468
- * Fetch the OffRamps allowlisted in a Router
469
- * Used to discover OffRamp connected to an OnRamp
732
+ * Fetch the OffRamps allowlisted in a Router.
733
+ * Used to discover OffRamp connected to an OnRamp.
734
+ *
470
735
  * @param router - Router contract address
471
- * @param sourceChainSelector - source chain selector
472
- * @returns array of OffRamp addresses
736
+ * @param sourceChainSelector - Source chain selector
737
+ * @returns Promise resolving to array of OffRamp addresses
738
+ *
739
+ * @example Get offRamps for a source chain
740
+ * ```typescript
741
+ * const offRamps = await dest.getOffRampsForRouter(routerAddress, sourceSelector)
742
+ * console.log(`Found ${offRamps.length} offRamp(s)`)
743
+ * ```
473
744
  */
474
745
  abstract getOffRampsForRouter(router: string, sourceChainSelector: bigint): Promise<string[]>
475
746
  /**
476
- * Fetch the OnRamp registered in a Router for a destination chain
747
+ * Fetch the OnRamp registered in a Router for a destination chain.
748
+ *
477
749
  * @param router - Router contract address
478
- * @param destChainSelector - destination chain selector
479
- * @returns OnRamp addresses
750
+ * @param destChainSelector - Destination chain selector
751
+ * @returns Promise resolving to OnRamp address
752
+ *
753
+ * @throws {@link CCIPLaneNotFoundError} if no lane exists to destination
754
+ *
755
+ * @example Get onRamp for destination
756
+ * ```typescript
757
+ * const onRamp = await source.getOnRampForRouter(routerAddress, destSelector)
758
+ * console.log(`OnRamp: ${onRamp}`)
759
+ * ```
480
760
  */
481
761
  abstract getOnRampForRouter(router: string, destChainSelector: bigint): Promise<string>
482
762
  /**
483
- * Fetch the OnRamp address set in OffRamp config
484
- * Used to discover OffRamp connected to an OnRamp
763
+ * Fetch the OnRamps addresses set in OffRamp config.
764
+ * Used to discover OffRamp connected to an OnRamp.
765
+ *
485
766
  * @param offRamp - OffRamp contract address
486
- * @param sourceChainSelector - source chain selector
487
- * @returns OnRamp address
488
- */
489
- abstract getOnRampForOffRamp(offRamp: string, sourceChainSelector: bigint): Promise<string>
490
- /**
491
- * Fetch the CommitStore set in OffRamp config (CCIP v1.5 and earlier).
492
- * For CCIP v1.6 and later, it should return the offRamp address.
493
- * @param offRamp - OffRamp contract address.
494
- * @returns CommitStore address.
767
+ * @param sourceChainSelector - Source chain selector
768
+ * @returns Promise resolving to OnRamps addresses
769
+ *
770
+ * @example Get onRamp from offRamp config
771
+ * ```typescript
772
+ * const [onRamp] = await dest.getOnRampsForOffRamp(offRampAddress, sourceSelector)
773
+ * console.log(`OnRamp: ${onRamp}`)
774
+ * ```
495
775
  */
496
- abstract getCommitStoreForOffRamp(offRamp: string): Promise<string>
776
+ abstract getOnRampsForOffRamp(offRamp: string, sourceChainSelector: bigint): Promise<string[]>
497
777
  /**
498
- * Fetch the TokenPool's token/mint
778
+ * Fetch the TokenPool's token/mint.
779
+ *
499
780
  * @param tokenPool - TokenPool address
500
- * @returns Token or mint address
781
+ * @returns Promise resolving to token or mint address
782
+ *
783
+ * @example Get token for pool
784
+ * ```typescript
785
+ * const token = await chain.getTokenForTokenPool(tokenPoolAddress)
786
+ * console.log(`Token: ${token}`)
787
+ * ```
501
788
  */
502
789
  abstract getTokenForTokenPool(tokenPool: string): Promise<string>
503
790
  /**
504
- * Fetch token metadata
791
+ * Fetch token metadata.
792
+ *
505
793
  * @param token - Token address
506
- * @returns Token symbol and decimals, and optionally name
794
+ * @returns Promise resolving to token symbol, decimals, and optionally name
795
+ *
796
+ * @example Get token info
797
+ * ```typescript
798
+ * const info = await chain.getTokenInfo(tokenAddress)
799
+ * console.log(`${info.symbol}: ${info.decimals} decimals`)
800
+ * ```
507
801
  */
508
802
  abstract getTokenInfo(token: string): Promise<TokenInfo>
509
803
  /**
@@ -515,14 +809,14 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
515
809
  *
516
810
  * @example Query native token balance
517
811
  * ```typescript
518
- * const balance = await chain.getBalance({ address: '0x123...' })
812
+ * const balance = await chain.getBalance({ holder: '0x123...' })
519
813
  * console.log(`Native balance: ${balance}`) // balance in wei
520
814
  * ```
521
815
  *
522
816
  * @example Query ERC20 token balance
523
817
  * ```typescript
524
818
  * const balance = await chain.getBalance({
525
- * address: '0x123...',
819
+ * holder: '0x123...',
526
820
  * token: '0xLINK...'
527
821
  * })
528
822
  * console.log(`LINK balance: ${balance}`) // balance in smallest units
@@ -530,20 +824,52 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
530
824
  */
531
825
  abstract getBalance(opts: GetBalanceOpts): Promise<bigint>
532
826
  /**
533
- * Fetch TokenAdminRegistry configured in a given OnRamp, Router, etc
534
- * Needed to map a source token to its dest counterparts
535
- * @param onRamp - Some contract for which we can fetch a TokenAdminRegistry
827
+ * Fetch TokenAdminRegistry configured in a given OnRamp, Router, etc.
828
+ * Needed to map a source token to its dest counterparts.
829
+ *
830
+ * @param address - Contract address (OnRamp, Router, etc.)
831
+ * @returns Promise resolving to TokenAdminRegistry address
832
+ *
833
+ * @example Get token registry
834
+ * ```typescript
835
+ * const registry = await chain.getTokenAdminRegistryFor(onRampAddress)
836
+ * console.log(`Registry: ${registry}`)
837
+ * ```
536
838
  */
537
839
  abstract getTokenAdminRegistryFor(address: string): Promise<string>
538
840
  /**
539
- * Fetch the current fee for a given intended message
841
+ * Fetch the current fee for a given intended message.
842
+ *
540
843
  * @param opts - {@link SendMessageOpts} without approveMax
844
+ * @returns Fee amount in the feeToken's smallest units
845
+ *
846
+ * @example Calculate message fee
847
+ * ```typescript
848
+ * const fee = await chain.getFee({
849
+ * router: routerAddress,
850
+ * destChainSelector: destSelector,
851
+ * message: { receiver: '0x...', data: '0x' },
852
+ * })
853
+ * console.log(`Fee: ${fee} wei`)
854
+ * ```
541
855
  */
542
856
  abstract getFee(opts: Omit<SendMessageOpts, 'approveMax'>): Promise<bigint>
543
857
  /**
544
- * Generate unsigned txs for ccipSend'ing a message
858
+ * Generate unsigned txs for ccipSend'ing a message.
859
+ *
545
860
  * @param opts - {@link SendMessageOpts} with sender address
546
- * @returns chain-family specific unsigned txs
861
+ * @returns Promise resolving to chain-family specific unsigned txs
862
+ *
863
+ * @example Generate unsigned transaction
864
+ * ```typescript
865
+ * const unsignedTx = await chain.generateUnsignedSendMessage({
866
+ * router: routerAddress,
867
+ * destChainSelector: destSelector,
868
+ * message: { receiver: '0x...', data: '0x1337' },
869
+ * sender: walletAddress,
870
+ * })
871
+ * // Sign and send with external wallet
872
+ * ```
547
873
  */
548
874
  abstract generateUnsignedSendMessage(
549
875
  opts: SendMessageOpts & {
@@ -553,10 +879,14 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
553
879
  ): Promise<UnsignedTx[F]>
554
880
  /**
555
881
  * Send a CCIP message through a router using provided wallet.
882
+ *
556
883
  * @param opts - {@link SendMessageOpts} with chain-specific wallet for signing
557
- * @returns CCIP request
884
+ * @returns Promise resolving to CCIP request with message details
558
885
  *
559
- * @example
886
+ * @throws {@link CCIPWalletNotSignerError} if wallet is not a valid signer
887
+ * @throws {@link CCIPLaneNotFoundError} if no lane exists to destination
888
+ *
889
+ * @example Send cross-chain message
560
890
  * ```typescript
561
891
  * const request = await chain.sendMessage({
562
892
  * router: '0x...',
@@ -579,72 +909,101 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
579
909
  },
580
910
  ): Promise<CCIPRequest>
581
911
  /**
582
- * Fetch supported offchain token data for a request from this network
912
+ * Fetch supported offchain token data for a request from this network.
913
+ *
583
914
  * @param request - CCIP request, with tx, logs and message
584
- * @returns array with one offchain token data for each token transfer in request
915
+ * @returns Promise resolving to array with one offchain token data for each token transfer
916
+ *
917
+ * @throws {@link CCIPUsdcAttestationError} if USDC attestation fetch fails (transient)
918
+ * @throws {@link CCIPLbtcAttestationError} if LBTC attestation fetch fails (transient)
919
+ *
920
+ * @example Get offchain token data for USDC transfer
921
+ * ```typescript
922
+ * const offchainData = await source.getOffchainTokenData(request)
923
+ * // Use in execution report
924
+ * ```
585
925
  */
586
926
  abstract getOffchainTokenData(request: CCIPRequest): Promise<OffchainTokenData[]>
587
927
  /**
588
- * Generate unsigned tx to manuallyExecute a message
589
- * @param opts - {@link ExecuteReportOpts} with payer address which will send the exec tx
590
- * @returns chain-family specific unsigned txs
928
+ * Generate unsigned tx to manuallyExecute a message.
929
+ *
930
+ * @param opts - {@link ExecuteOpts} with payer address which will send the exec tx
931
+ * @returns Promise resolving to chain-family specific unsigned txs
932
+ *
933
+ * @example Generate unsigned execution tx
934
+ * ```typescript
935
+ * const unsignedTx = await dest.generateUnsignedExecute({
936
+ * offRamp: offRampAddress,
937
+ * execReport,
938
+ * payer: walletAddress,
939
+ * })
940
+ * // Sign and send with external wallet
941
+ * ```
591
942
  */
592
- abstract generateUnsignedExecuteReport(
593
- opts: ExecuteReportOpts & {
943
+ abstract generateUnsignedExecute(
944
+ opts: ExecuteOpts & {
594
945
  /** address which will be used to send the report tx */
595
946
  payer: string
596
947
  },
597
948
  ): Promise<UnsignedTx[F]>
598
949
  /**
599
- * Execute messages in report in an offRamp
600
- * @param opts - {@link ExecuteReportOpts} with chain-specific wallet to sign and send tx
601
- * @returns transaction of the execution
950
+ * Execute messages in report in an offRamp.
602
951
  *
603
- * @example
952
+ * @param opts - {@link ExecuteOpts} with chain-specific wallet to sign and send tx
953
+ * @returns Promise resolving to transaction of the execution
954
+ *
955
+ * @throws {@link CCIPWalletNotSignerError} if wallet is not a valid signer
956
+ * @throws {@link CCIPExecTxRevertedError} if execution transaction reverts
957
+ * @throws {@link CCIPMerkleRootMismatchError} if merkle proof is invalid
958
+ *
959
+ * @example Manual execution of pending message
604
960
  * ```typescript
605
- * const execReportProof = calculateManualExecProof(
606
- * messagesInBatch: await source.getMessagesInBatch(request, commit.report),
607
- * request.lane,
608
- * request.message.messageId,
609
- * commit.report.merkleRoot,
610
- * dest,
611
- * )
612
- * const receipt = await dest.executeReport({
613
- * offRamp,
614
- * execReport: {
615
- * ...execReportProof,
616
- * message: request.message,
617
- * offchainTokenData: await source.getOffchainTokenData(request),
618
- * },
619
- * wallet,
620
- * })
621
- * console.log(`Message ID: ${request.message.messageId}`)
961
+ * const input = await source.getExecutionInput({ request, verifications })
962
+ * const receipt = await dest.execute({ offRamp, input, wallet })
963
+ * console.log(`Executed: ${receipt.log.transactionHash}`)
622
964
  * ```
965
+ * @throws {@link CCIPWalletNotSignerError} if wallet cannot sign transactions
966
+ * @throws {@link CCIPExecTxNotConfirmedError} if execution transaction fails to confirm
623
967
  */
624
- abstract executeReport(
625
- opts: ExecuteReportOpts & {
968
+ abstract execute(
969
+ opts: ExecuteOpts & {
626
970
  // Signer instance (chain-dependent)
627
971
  wallet: unknown
628
972
  },
629
973
  ): Promise<CCIPExecution>
630
974
 
631
975
  /**
632
- * Look for a CommitReport at dest for given CCIP request
633
- * May be specialized by some subclasses
634
- * @param opts - getCommitReport options
635
- * @returns CCIPCommit info, or reject if none found
636
- **/
637
- async getCommitReport({
638
- commitStore,
976
+ * Look for a CommitReport at dest for given CCIP request.
977
+ * May be specialized by some subclasses.
978
+ *
979
+ * @param opts - getVerifications options
980
+ * @returns CCIPCommit info
981
+ *
982
+ * @throws {@link CCIPCommitNotFoundError} if no commit found for the request (transient)
983
+ *
984
+ * @example Get commit for a request
985
+ * ```typescript
986
+ * const verifications = await dest.getVerifications({
987
+ * offRamp: offRampAddress,
988
+ * request,
989
+ * })
990
+ * console.log(`Committed at block: ${verifications.log.blockNumber}`)
991
+ * ```
992
+ */
993
+ async getVerifications({
994
+ offRamp,
639
995
  request,
640
996
  ...hints
641
997
  }: {
642
- /** address of commitStore (OffRamp in \>=v1.6) */
643
- commitStore: string
998
+ /** address of offRamp or commitStore contract */
999
+ offRamp: string
644
1000
  /** CCIPRequest subset object */
645
- request: PickDeep<CCIPRequest, 'lane' | 'message.sequenceNumber' | 'tx.timestamp'>
646
- } & Pick<LogFilter, 'page' | 'watch' | 'startBlock'>): Promise<CCIPCommit> {
647
- return getCommitReport(this, commitStore, request, hints)
1001
+ request: PickDeep<
1002
+ CCIPRequest,
1003
+ 'lane' | `message.${'sequenceNumber' | 'messageId'}` | 'tx.timestamp'
1004
+ >
1005
+ } & Pick<LogFilter, 'page' | 'watch' | 'startBlock'>): Promise<CCIPVerifications> {
1006
+ return getOnchainCommitReport(this, offRamp, request, hints)
648
1007
  }
649
1008
 
650
1009
  /**
@@ -687,15 +1046,29 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
687
1046
  }
688
1047
 
689
1048
  /**
690
- * Default/generic implementation of getExecutionReceipts
1049
+ * Default/generic implementation of getExecutionReceipts.
1050
+ * Yields execution receipts for a given offRamp.
1051
+ *
691
1052
  * @param opts - getExecutionReceipts options
692
1053
  * @returns Async generator of CCIPExecution receipts
1054
+ *
1055
+ * @example Watch for execution receipts
1056
+ * ```typescript
1057
+ * for await (const exec of dest.getExecutionReceipts({
1058
+ * offRamp: offRampAddress,
1059
+ * messageId: request.message.messageId,
1060
+ * startBlock: commit.log.blockNumber,
1061
+ * })) {
1062
+ * console.log(`State: ${exec.receipt.state}`)
1063
+ * if (exec.receipt.state === ExecutionState.Success) break
1064
+ * }
1065
+ * ```
693
1066
  */
694
1067
  async *getExecutionReceipts({
695
1068
  offRamp,
696
1069
  messageId,
697
1070
  sourceChainSelector,
698
- commit,
1071
+ verifications,
699
1072
  ...hints
700
1073
  }: {
701
1074
  /** address of OffRamp contract */
@@ -705,12 +1078,12 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
705
1078
  /** filter: yield only executions for this source chain */
706
1079
  sourceChainSelector?: bigint
707
1080
  /** optional commit associated with the request, can be used for optimizations in some families */
708
- commit?: CCIPCommit
1081
+ verifications?: CCIPVerifications
709
1082
  } & Pick<
710
1083
  LogFilter,
711
1084
  'page' | 'watch' | 'startBlock' | 'startTime'
712
1085
  >): AsyncIterableIterator<CCIPExecution> {
713
- hints.startBlock ??= commit?.log.blockNumber
1086
+ if (verifications && 'log' in verifications) hints.startBlock ??= verifications.log.blockNumber
714
1087
  const onlyLast = !hints.startTime && !hints.startBlock // backwards
715
1088
  for await (const log of this.getLogs({
716
1089
  address: offRamp,
@@ -736,9 +1109,18 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
736
1109
 
737
1110
  /**
738
1111
  * Fetch first execution receipt inside a transaction.
1112
+ *
739
1113
  * @internal
740
- * @param tx - transaction hash or transaction object
1114
+ * @param tx - Transaction hash or transaction object
741
1115
  * @returns CCIP execution object
1116
+ *
1117
+ * @throws {@link CCIPExecTxRevertedError} if no execution receipt found in transaction
1118
+ *
1119
+ * @example Get receipt from execution tx
1120
+ * ```typescript
1121
+ * const exec = await dest.getExecutionReceiptInTx(execTxHash)
1122
+ * console.log(`State: ${exec.receipt.state}`)
1123
+ * ```
742
1124
  */
743
1125
  async getExecutionReceiptInTx(tx: string | ChainTransaction): Promise<CCIPExecution> {
744
1126
  if (typeof tx === 'string') tx = await this.getTransaction(tx)
@@ -754,63 +1136,198 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
754
1136
 
755
1137
  /**
756
1138
  * List tokens supported by given TokenAdminRegistry contract.
1139
+ *
757
1140
  * @param address - Usually TokenAdminRegistry, but chain may support receiving Router, OnRamp, etc.
758
- * @param opts - Optional parameters (e.g., `page` for pagination range).
759
- * @returns Array of supported token addresses.
1141
+ * @param opts - Optional parameters (e.g., `page` for pagination range)
1142
+ * @returns Promise resolving to array of supported token addresses
1143
+ *
1144
+ * @example Get all supported tokens
1145
+ * ```typescript
1146
+ * const tokens = await chain.getSupportedTokens(registryAddress)
1147
+ * console.log(`${tokens.length} tokens supported`)
1148
+ * ```
760
1149
  */
761
1150
  abstract getSupportedTokens(address: string, opts?: { page?: number }): Promise<string[]>
762
1151
 
763
1152
  /**
764
- * Get TokenConfig for a given token address in a TokenAdminRegistry
765
- * @param address - TokenAdminRegistry contract address
766
- * @param token - Token address
1153
+ * Fetch token configuration from a TokenAdminRegistry.
1154
+ *
1155
+ * @remarks
1156
+ * The TokenAdminRegistry is a contract that tracks token administrators and their
1157
+ * associated pools. Each token has an administrator who can update pool configurations.
1158
+ *
1159
+ * @example Query a token's registry configuration
1160
+ * ```typescript
1161
+ * const config = await chain.getRegistryTokenConfig(registryAddress, tokenAddress)
1162
+ * console.log(`Administrator: ${config.administrator}`)
1163
+ * if (config.tokenPool) {
1164
+ * console.log(`Pool: ${config.tokenPool}`)
1165
+ * }
1166
+ * ```
1167
+ *
1168
+ * @param registry - TokenAdminRegistry contract address.
1169
+ * @param token - Token address to query.
1170
+ * @returns {@link RegistryTokenConfig} containing administrator and pool information.
1171
+ * @throws {@link CCIPTokenNotInRegistryError} if token is not registered.
767
1172
  */
768
- abstract getRegistryTokenConfig(
769
- registry: string,
770
- token: string,
771
- ): Promise<{
772
- administrator: string
773
- pendingAdministrator?: string
774
- tokenPool?: string
775
- }>
1173
+ abstract getRegistryTokenConfig(registry: string, token: string): Promise<RegistryTokenConfig>
776
1174
 
777
1175
  /**
778
- * Get TokenPool state and configurations
779
- * @param tokenPool - Token pool address
1176
+ * Fetch configuration of a token pool.
1177
+ *
1178
+ * @remarks
1179
+ * Return type varies by chain:
1180
+ * - **EVM**: `typeAndVersion` is always present (required)
1181
+ * - **Solana**: Includes extra `tokenPoolProgram` field
1182
+ * - **Aptos**: Standard fields only
1183
+ * - **Sui/TON**: Throws {@link CCIPNotImplementedError}
1184
+ *
1185
+ * @example Type-safe access to chain-specific fields
1186
+ * ```typescript
1187
+ * // Use instanceof to narrow the chain type
1188
+ * if (chain instanceof SolanaChain) {
1189
+ * const config = await chain.getTokenPoolConfig(poolAddress)
1190
+ * console.log(config.tokenPoolProgram) // TypeScript knows this exists!
1191
+ * } else if (chain instanceof EVMChain) {
1192
+ * const config = await chain.getTokenPoolConfig(poolAddress)
1193
+ * console.log(config.typeAndVersion) // TypeScript knows this is required!
1194
+ * }
1195
+ * ```
1196
+ *
1197
+ * @param tokenPool - Token pool contract address.
1198
+ * @returns {@link TokenPoolConfig} containing token, router, and version info.
1199
+ * @throws {@link CCIPNotImplementedError} on Sui or TON chains
780
1200
  */
781
- abstract getTokenPoolConfigs(tokenPool: string): Promise<{
782
- token: string
783
- router: string
784
- typeAndVersion?: string
785
- }>
1201
+ abstract getTokenPoolConfig(tokenPool: string): Promise<TokenPoolConfig>
786
1202
 
787
1203
  /**
788
- * Get TokenPool remote configurations.
789
- * @param tokenPool - Token pool address.
790
- * @param remoteChainSelector - If provided, only return remotes for the specified chain (may error if remote not supported).
791
- * @returns Record of network names and remote configurations (remoteToken, remotePools, rateLimitStates).
1204
+ * Fetch remote chain configurations for a token pool.
1205
+ *
1206
+ * @remarks
1207
+ * A token pool maintains configurations for each destination chain it supports.
1208
+ * The returned Record maps chain names to their respective configurations.
1209
+ *
1210
+ * @example Get all supported destinations
1211
+ * ```typescript
1212
+ * const remotes = await chain.getTokenPoolRemotes(poolAddress)
1213
+ * // Returns: {
1214
+ * // "ethereum-mainnet": { remoteToken: "0x...", remotePools: [...], ... },
1215
+ * // "ethereum-mainnet-arbitrum-1": { remoteToken: "0x...", remotePools: [...], ... },
1216
+ * // "solana-mainnet": { remoteToken: "...", remotePools: [...], ... }
1217
+ * // }
1218
+ *
1219
+ * // Access a specific chain's config
1220
+ * const arbConfig = remotes['ethereum-mainnet']
1221
+ * console.log(`Remote token: ${arbConfig.remoteToken}`)
1222
+ * ```
1223
+ *
1224
+ * @example Filter to a specific destination
1225
+ * ```typescript
1226
+ * import { networkInfo } from '@chainlink/ccip-sdk'
1227
+ *
1228
+ * const arbitrumSelector = 4949039107694359620n
1229
+ * const remotes = await chain.getTokenPoolRemotes(poolAddress, arbitrumSelector)
1230
+ * // Returns only: { "arbitrum-mainnet": { ... } }
1231
+ *
1232
+ * const chainName = networkInfo(arbitrumSelector).name
1233
+ * const config = remotes[chainName]
1234
+ * ```
1235
+ *
1236
+ * @param tokenPool - Token pool address on the current chain.
1237
+ * @param remoteChainSelector - Optional chain selector to filter results to a single destination.
1238
+ * @returns Record where keys are chain names (e.g., "ethereum-mainnet") and values are {@link TokenPoolRemote} configs.
1239
+ * @throws {@link CCIPTokenPoolChainConfigNotFoundError} if remoteChainSelector is specified but not configured.
792
1240
  */
793
1241
  abstract getTokenPoolRemotes(
794
1242
  tokenPool: string,
795
1243
  remoteChainSelector?: bigint,
796
1244
  ): Promise<Record<string, TokenPoolRemote>>
1245
+ /**
1246
+ * Fetch remote chain configuration for a token pool for a specific destination.
1247
+ *
1248
+ * @remarks
1249
+ * Convenience wrapper around {@link getTokenPoolRemotes} that returns a single
1250
+ * configuration instead of a Record. Use this when you need configuration for
1251
+ * a specific destination chain.
1252
+ *
1253
+ * @example
1254
+ * ```typescript
1255
+ * const arbitrumSelector = 4949039107694359620n
1256
+ * const remote = await chain.getTokenPoolRemote(poolAddress, arbitrumSelector)
1257
+ * console.log(`Remote token: ${remote.remoteToken}`)
1258
+ * console.log(`Remote pools: ${remote.remotePools.join(', ')}`)
1259
+ * ```
1260
+ *
1261
+ * @param tokenPool - Token pool address on the current chain.
1262
+ * @param remoteChainSelector - Chain selector of the desired remote chain.
1263
+ * @returns TokenPoolRemote config for the specified remote chain.
1264
+ * @throws {@link CCIPTokenPoolChainConfigNotFoundError} if no configuration found for the specified remote chain.
1265
+ */
1266
+ async getTokenPoolRemote(
1267
+ tokenPool: string,
1268
+ remoteChainSelector: bigint,
1269
+ ): Promise<TokenPoolRemote> {
1270
+ const remotes = await this.getTokenPoolRemotes(tokenPool, remoteChainSelector)
1271
+ const network = networkInfo(remoteChainSelector)
1272
+ const remoteConfig = remotes[network.name]
1273
+ if (!remoteConfig) {
1274
+ throw new CCIPTokenPoolChainConfigNotFoundError(tokenPool, tokenPool, network.name)
1275
+ }
1276
+ return remoteConfig
1277
+ }
797
1278
 
798
1279
  /**
799
1280
  * Fetch list and info of supported feeTokens.
800
- * @param router - Router address on this chain.
801
- * @returns Mapping of token addresses to respective TokenInfo objects.
1281
+ *
1282
+ * @param router - Router address on this chain
1283
+ * @returns Promise resolving to mapping of token addresses to TokenInfo objects
1284
+ *
1285
+ * @example Get available fee tokens
1286
+ * ```typescript
1287
+ * const feeTokens = await chain.getFeeTokens(routerAddress)
1288
+ * for (const [addr, info] of Object.entries(feeTokens)) {
1289
+ * console.log(`${info.symbol}: ${addr}`)
1290
+ * }
1291
+ * ```
802
1292
  */
803
1293
  abstract getFeeTokens(router: string): Promise<Record<string, TokenInfo>>
804
1294
 
805
- /** {@inheritDoc ChainStatic.buildMessageForDest} */
1295
+ /**
1296
+ * Returns a copy of a message, populating missing fields like `extraArgs` with defaults.
1297
+ * It's expected to return a message suitable at least for basic token transfers.
1298
+ *
1299
+ * @param message - AnyMessage (from source), containing at least `receiver`
1300
+ * @returns A message suitable for `sendMessage` to this destination chain family
1301
+ */
806
1302
  static buildMessageForDest(
807
1303
  message: Parameters<ChainStatic['buildMessageForDest']>[0],
808
1304
  ): AnyMessage {
809
- // default to GenericExtraArgsV2, aka EVMExtraArgsV2
1305
+ const gasLimit = message.data && dataLength(message.data) ? DEFAULT_GAS_LIMIT : 0n
1306
+
1307
+ // Detect if user wants V3 by checking for any V3-only field
1308
+ if (hasV3ExtraArgs(message.extraArgs)) {
1309
+ // V3 defaults (GenericExtraArgsV3)
1310
+ return {
1311
+ ...message,
1312
+ extraArgs: {
1313
+ gasLimit,
1314
+ blockConfirmations: 0,
1315
+ ccvs: [],
1316
+ ccvArgs: [],
1317
+ executor: '',
1318
+ executorArgs: '0x',
1319
+ tokenReceiver: '',
1320
+ tokenArgs: '0x',
1321
+ ...message.extraArgs,
1322
+ },
1323
+ }
1324
+ }
1325
+
1326
+ // Default to V2 (GenericExtraArgsV2, aka EVMExtraArgsV2)
810
1327
  return {
811
1328
  ...message,
812
1329
  extraArgs: {
813
- gasLimit: message.data && dataLength(message.data) ? DEFAULT_GAS_LIMIT : 0n,
1330
+ gasLimit,
814
1331
  allowOutOfOrderExecution: true,
815
1332
  ...message.extraArgs,
816
1333
  },
@@ -838,87 +1355,216 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
838
1355
  }): Promise<number>
839
1356
  }
840
1357
 
841
- /** Static methods and properties available on Chain class constructors. */
1358
+ /**
1359
+ * Static methods and properties available on Chain class constructors.
1360
+ *
1361
+ * @example Using static methods
1362
+ * ```typescript
1363
+ * // Create chain from URL
1364
+ * const chain = await EVMChain.fromUrl('https://eth-mainnet.example.com')
1365
+ *
1366
+ * // Decode message from log
1367
+ * const message = EVMChain.decodeMessage(log)
1368
+ *
1369
+ * // Validate address format
1370
+ * const normalized = EVMChain.getAddress('0xABC...')
1371
+ * ```
1372
+ */
842
1373
  // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
843
1374
  export type ChainStatic<F extends ChainFamily = ChainFamily> = Function & {
844
1375
  readonly family: F
845
1376
  readonly decimals: number
846
1377
  /**
847
- * async constructor: builds a Chain from a rpc endpoint url
848
- * @param url - rpc endpoint url
849
- * @param ctx - optional context with logger and API client configuration
1378
+ * Async constructor: builds a Chain from an RPC endpoint URL.
1379
+ *
1380
+ * @param url - RPC endpoint URL
1381
+ * @param ctx - Optional context with logger and API client configuration
1382
+ * @returns Promise resolving to Chain instance
1383
+ *
1384
+ * @throws {@link CCIPChainNotFoundError} if chain cannot be identified
1385
+ *
1386
+ * @example Create chain from RPC
1387
+ * ```typescript
1388
+ * const chain = await EVMChain.fromUrl('https://eth-mainnet.example.com')
1389
+ * console.log(`Connected to: ${chain.network.name}`)
1390
+ * ```
850
1391
  */
851
1392
  fromUrl(url: string, ctx?: ChainContext): Promise<Chain<F>>
852
1393
  /**
853
- * Try to decode a CCIP message *from* a log/event *originated* from this *source* chain,
854
- * but which may *target* other dest chain families
855
- * iow: the parsing is specific to this chain family, but content may be intended to alien chains
856
- * e.g: EVM-born (abi.encoded) bytearray may output message.computeUnits for Solana
1394
+ * Try to decode a CCIP message from a log/event originated from this source chain.
1395
+ * The parsing is specific to this chain family, but content may target other chains.
1396
+ *
857
1397
  * @param log - Chain generic log
858
- * @returns decoded CCIP message with merged extraArgs
1398
+ * @returns Decoded CCIP message with merged extraArgs, or undefined if not a CCIP message
1399
+ *
1400
+ * @example Decode message from log
1401
+ * ```typescript
1402
+ * const message = EVMChain.decodeMessage(log)
1403
+ * if (message) {
1404
+ * console.log(`Message ID: ${message.messageId}`)
1405
+ * }
1406
+ * ```
859
1407
  */
860
1408
  decodeMessage(log: Pick<Log_, 'data'>): CCIPMessage | undefined
861
1409
  /**
862
- * Try to decode an extraArgs array serialized for this chain family
863
- * @param extraArgs - extra args bytes (Uint8Array, HexString or base64)
864
- * @returns object containing decoded extraArgs and their tags
1410
+ * Try to decode an extraArgs array serialized for this chain family.
1411
+ *
1412
+ * @param extraArgs - Extra args bytes (Uint8Array, HexString or base64)
1413
+ * @returns Object containing decoded extraArgs and their tag, or undefined
1414
+ *
1415
+ * @throws {@link CCIPExtraArgsParseError} if bytes cannot be decoded
1416
+ *
1417
+ * @example Decode extra args
1418
+ * ```typescript
1419
+ * const decoded = EVMChain.decodeExtraArgs(message.extraArgs)
1420
+ * if (decoded?._tag === 'EVMExtraArgsV2') {
1421
+ * console.log(`Gas limit: ${decoded.gasLimit}`)
1422
+ * }
1423
+ * ```
865
1424
  */
866
1425
  decodeExtraArgs(
867
1426
  extraArgs: BytesLike,
868
1427
  ):
869
1428
  | (EVMExtraArgsV1 & { _tag: 'EVMExtraArgsV1' })
870
1429
  | (EVMExtraArgsV2 & { _tag: 'EVMExtraArgsV2' })
1430
+ | (GenericExtraArgsV3 & { _tag: 'GenericExtraArgsV3' })
871
1431
  | (SVMExtraArgsV1 & { _tag: 'SVMExtraArgsV1' })
872
1432
  | (SuiExtraArgsV1 & { _tag: 'SuiExtraArgsV1' })
873
1433
  | undefined
1434
+ /**
1435
+ * Encode extraArgs for this chain family.
1436
+ *
1437
+ * @param extraArgs - Extra args object to encode
1438
+ * @returns Encoded hex string
1439
+ *
1440
+ * @example Encode extra args
1441
+ * ```typescript
1442
+ * const encoded = EVMChain.encodeExtraArgs({
1443
+ * gasLimit: 200000n,
1444
+ * strict: false,
1445
+ * })
1446
+ * ```
1447
+ */
874
1448
  encodeExtraArgs(extraArgs: ExtraArgs): string
875
1449
  /**
876
- * Decode a commit (CommitReportAccepted) event
1450
+ * Decode a commit (CommitReportAccepted) event.
1451
+ *
877
1452
  * @param log - Chain generic log
878
- * @param lane - if passed, filter or validate reports by lane
879
- * @returns Array of commit reports contained in the log
1453
+ * @param lane - If passed, filter or validate reports by lane
1454
+ * @returns Array of commit reports contained in the log, or undefined
1455
+ *
1456
+ * @example Decode commit from log
1457
+ * ```typescript
1458
+ * const commits = EVMChain.decodeCommits(log, lane)
1459
+ * if (commits) {
1460
+ * console.log(`Found ${commits.length} commit(s)`)
1461
+ * }
1462
+ * ```
880
1463
  */
881
1464
  decodeCommits(log: Pick<Log_, 'data'>, lane?: Lane): CommitReport[] | undefined
882
1465
  /**
883
- * Decode a receipt (ExecutioStateChanged) event
1466
+ * Decode a receipt (ExecutionStateChanged) event.
1467
+ *
884
1468
  * @param log - Chain generic log
885
1469
  * @returns ExecutionReceipt or undefined if not a recognized receipt
1470
+ *
1471
+ * @example Decode execution receipt
1472
+ * ```typescript
1473
+ * const receipt = EVMChain.decodeReceipt(log)
1474
+ * if (receipt) {
1475
+ * console.log(`State: ${receipt.state}, Message: ${receipt.messageId}`)
1476
+ * }
1477
+ * ```
886
1478
  */
887
1479
  decodeReceipt(log: Pick<Log_, 'data'>): ExecutionReceipt | undefined
888
1480
  /**
889
- * Receive a bytes array and try to decode and normalize it as an address of this chain family
1481
+ * Receive a bytes array and try to decode and normalize it as an address of this chain family.
1482
+ *
890
1483
  * @param bytes - Bytes array (Uint8Array, HexString or Base64)
891
1484
  * @returns Address in this chain family's format
1485
+ *
1486
+ * @throws {@link CCIPAddressInvalidEvmError} if invalid EVM address
1487
+ * @throws {@link CCIPDataFormatUnsupportedError} if invalid Aptos/Sui address
1488
+ *
1489
+ * @example Normalize address
1490
+ * ```typescript
1491
+ * const normalized = EVMChain.getAddress('0xABC123...')
1492
+ * console.log(normalized) // checksummed address
1493
+ * ```
892
1494
  */
893
1495
  getAddress(bytes: BytesLike): string
894
1496
  /**
895
- * Validates a transaction hash format for this chain family
1497
+ * Validates a transaction hash format for this chain family.
1498
+ *
1499
+ * @param v - Value to validate
1500
+ * @returns True if value is a valid transaction hash format
1501
+ *
1502
+ * @example Validate transaction hash
1503
+ * ```typescript
1504
+ * if (EVMChain.isTxHash(userInput)) {
1505
+ * const tx = await chain.getTransaction(userInput)
1506
+ * }
1507
+ * ```
896
1508
  */
897
1509
  isTxHash(v: unknown): v is string
898
1510
  /**
899
1511
  * Format an address for human-friendly display.
900
1512
  * Defaults to getAddress if not overridden.
1513
+ *
901
1514
  * @param address - Address string in any recognized format
902
1515
  * @returns Human-friendly address string for display
1516
+ *
1517
+ * @example Format address for display
1518
+ * ```typescript
1519
+ * const display = EVMChain.formatAddress?.(rawAddress) ?? rawAddress
1520
+ * console.log(display)
1521
+ * ```
903
1522
  */
904
1523
  formatAddress?(address: string): string
905
1524
  /**
906
1525
  * Format a transaction hash for human-friendly display.
1526
+ *
907
1527
  * @param hash - Transaction hash string
908
1528
  * @returns Human-friendly hash string for display
1529
+ *
1530
+ * @example Format tx hash for display
1531
+ * ```typescript
1532
+ * const display = EVMChain.formatTxHash?.(rawHash) ?? rawHash
1533
+ * console.log(display)
1534
+ * ```
909
1535
  */
910
1536
  formatTxHash?(hash: string): string
911
1537
  /**
912
- * Create a leaf hasher for this dest chain and lane
913
- * @param lane - source, dest and onramp lane info
914
- * @param ctx - context object containing logger
915
- * @returns LeafHasher is a function that takes a message and returns a hash of it
1538
+ * Create a leaf hasher for this dest chain and lane.
1539
+ *
1540
+ * @param lane - Source, dest and onramp lane info
1541
+ * @param ctx - Context object containing logger
1542
+ * @returns LeafHasher function that takes a message and returns its hash
1543
+ *
1544
+ * @throws {@link CCIPHasherVersionUnsupportedError} if hasher version unsupported
1545
+ *
1546
+ * @example Create leaf hasher
1547
+ * ```typescript
1548
+ * const hasher = EVMChain.getDestLeafHasher(lane, { logger })
1549
+ * const leafHash = hasher(message)
1550
+ * ```
916
1551
  */
917
1552
  getDestLeafHasher(lane: Lane, ctx?: WithLogger): LeafHasher
918
1553
  /**
919
- * Try to parse an error or bytearray generated by this chain family
1554
+ * Try to parse an error or bytearray generated by this chain family.
1555
+ *
920
1556
  * @param data - Caught object, string or bytearray
921
- * @returns Ordered record with messages/properties, or undefined if not a recognized error
1557
+ * @returns Ordered record with messages/properties, or undefined/null if not recognized
1558
+ *
1559
+ * @example Parse contract error
1560
+ * ```typescript
1561
+ * try {
1562
+ * await chain.sendMessage(opts)
1563
+ * } catch (err) {
1564
+ * const parsed = EVMChain.parse?.(err)
1565
+ * if (parsed) console.log('Contract error:', parsed)
1566
+ * }
1567
+ * ```
922
1568
  */
923
1569
  parse?(data: unknown): Record<string, unknown> | undefined | null
924
1570
  /**