@chainlink/ccip-sdk 1.1.0 → 1.2.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.
- package/dist/api/index.d.ts +165 -15
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +236 -61
- package/dist/api/index.js.map +1 -1
- package/dist/api/types.d.ts +119 -1
- package/dist/api/types.d.ts.map +1 -1
- package/dist/aptos/index.d.ts.map +1 -1
- package/dist/aptos/index.js +5 -5
- package/dist/aptos/index.js.map +1 -1
- package/dist/chain.d.ts +65 -24
- package/dist/chain.d.ts.map +1 -1
- package/dist/chain.js +84 -11
- package/dist/chain.js.map +1 -1
- package/dist/errors/codes.d.ts +1 -0
- package/dist/errors/codes.d.ts.map +1 -1
- package/dist/errors/codes.js +1 -0
- package/dist/errors/codes.js.map +1 -1
- package/dist/errors/index.d.ts +1 -1
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +1 -1
- package/dist/errors/index.js.map +1 -1
- package/dist/errors/recovery.d.ts.map +1 -1
- package/dist/errors/recovery.js +1 -0
- package/dist/errors/recovery.js.map +1 -1
- package/dist/errors/specialized.d.ts +21 -0
- package/dist/errors/specialized.d.ts.map +1 -1
- package/dist/errors/specialized.js +31 -1
- package/dist/errors/specialized.js.map +1 -1
- package/dist/evm/abi/OffRamp_2_0.d.ts +18 -17
- package/dist/evm/abi/OffRamp_2_0.d.ts.map +1 -1
- package/dist/evm/abi/OffRamp_2_0.js +19 -21
- package/dist/evm/abi/OffRamp_2_0.js.map +1 -1
- package/dist/evm/abi/TokenPool_2_0.d.ts +0 -4
- package/dist/evm/abi/TokenPool_2_0.d.ts.map +1 -1
- package/dist/evm/abi/TokenPool_2_0.js +0 -1
- package/dist/evm/abi/TokenPool_2_0.js.map +1 -1
- package/dist/evm/gas.d.ts +14 -4
- package/dist/evm/gas.d.ts.map +1 -1
- package/dist/evm/gas.js +7 -6
- package/dist/evm/gas.js.map +1 -1
- package/dist/evm/index.d.ts +39 -8
- package/dist/evm/index.d.ts.map +1 -1
- package/dist/evm/index.js +106 -36
- package/dist/evm/index.js.map +1 -1
- package/dist/extra-args.d.ts +18 -8
- package/dist/extra-args.d.ts.map +1 -1
- package/dist/extra-args.js +6 -6
- package/dist/extra-args.js.map +1 -1
- package/dist/gas.d.ts +1 -1
- package/dist/gas.d.ts.map +1 -1
- package/dist/gas.js +7 -2
- package/dist/gas.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/requests.d.ts +11 -5
- package/dist/requests.d.ts.map +1 -1
- package/dist/requests.js +4 -7
- package/dist/requests.js.map +1 -1
- package/dist/solana/index.d.ts +2 -2
- package/dist/solana/index.d.ts.map +1 -1
- package/dist/solana/index.js +3 -2
- package/dist/solana/index.js.map +1 -1
- package/dist/solana/utils.js +2 -2
- package/dist/solana/utils.js.map +1 -1
- package/dist/sui/exec.d.ts +30 -0
- package/dist/sui/exec.d.ts.map +1 -0
- package/dist/sui/exec.js +92 -0
- package/dist/sui/exec.js.map +1 -0
- package/dist/sui/index.d.ts +7 -2
- package/dist/sui/index.d.ts.map +1 -1
- package/dist/sui/index.js +23 -65
- package/dist/sui/index.js.map +1 -1
- package/dist/sui/manuallyExec/index.d.ts.map +1 -1
- package/dist/sui/manuallyExec/index.js +10 -13
- package/dist/sui/manuallyExec/index.js.map +1 -1
- package/dist/sui/objects.d.ts.map +1 -1
- package/dist/sui/objects.js +4 -2
- package/dist/sui/objects.js.map +1 -1
- package/dist/sui/types.d.ts +9 -1
- package/dist/sui/types.d.ts.map +1 -1
- package/dist/sui/types.js.map +1 -1
- package/dist/ton/index.d.ts.map +1 -1
- package/dist/ton/index.js +34 -26
- package/dist/ton/index.js.map +1 -1
- package/dist/utils.d.ts +10 -4
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +10 -4
- package/dist/utils.js.map +1 -1
- package/package.json +7 -7
- package/src/api/index.ts +271 -59
- package/src/api/types.ts +126 -1
- package/src/aptos/index.ts +7 -9
- package/src/chain.ts +136 -38
- package/src/errors/codes.ts +1 -0
- package/src/errors/index.ts +1 -0
- package/src/errors/recovery.ts +2 -0
- package/src/errors/specialized.ts +33 -1
- package/src/evm/abi/OffRamp_2_0.ts +19 -21
- package/src/evm/abi/TokenPool_2_0.ts +0 -1
- package/src/evm/gas.ts +18 -20
- package/src/evm/index.ts +126 -34
- package/src/extra-args.ts +18 -8
- package/src/gas.ts +8 -3
- package/src/index.ts +5 -0
- package/src/requests.ts +18 -12
- package/src/solana/index.ts +3 -2
- package/src/solana/utils.ts +2 -2
- package/src/sui/exec.ts +131 -0
- package/src/sui/index.ts +33 -98
- package/src/sui/manuallyExec/index.ts +11 -17
- package/src/sui/objects.ts +4 -2
- package/src/sui/types.ts +10 -1
- package/src/ton/index.ts +47 -26
- package/src/utils.ts +10 -4
package/src/api/index.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { memoize } from 'micro-memoize'
|
|
|
2
2
|
import type { SetRequired } from 'type-fest'
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
|
+
CCIPAbortError,
|
|
5
6
|
CCIPApiClientNotAvailableError,
|
|
6
7
|
CCIPHttpError,
|
|
7
8
|
CCIPLaneNotFoundError,
|
|
@@ -28,10 +29,13 @@ import {
|
|
|
28
29
|
MessageStatus,
|
|
29
30
|
NetworkType,
|
|
30
31
|
} from '../types.ts'
|
|
31
|
-
import { bigIntReviver, parseJson } from '../utils.ts'
|
|
32
|
+
import { bigIntReviver, decodeAddress, parseJson } from '../utils.ts'
|
|
32
33
|
import type {
|
|
33
34
|
APIErrorResponse,
|
|
34
35
|
LaneLatencyResponse,
|
|
36
|
+
MessageSearchFilters,
|
|
37
|
+
MessageSearchPage,
|
|
38
|
+
MessageSearchResult,
|
|
35
39
|
RawExecutionInputsResult,
|
|
36
40
|
RawLaneLatencyResponse,
|
|
37
41
|
RawMessageResponse,
|
|
@@ -40,7 +44,14 @@ import type {
|
|
|
40
44
|
} from './types.ts'
|
|
41
45
|
import { calculateManualExecProof } from '../execution.ts'
|
|
42
46
|
|
|
43
|
-
export type {
|
|
47
|
+
export type {
|
|
48
|
+
APICCIPRequestMetadata,
|
|
49
|
+
APIErrorResponse,
|
|
50
|
+
LaneLatencyResponse,
|
|
51
|
+
MessageSearchFilters,
|
|
52
|
+
MessageSearchPage,
|
|
53
|
+
MessageSearchResult,
|
|
54
|
+
} from './types.ts'
|
|
44
55
|
|
|
45
56
|
/** Default CCIP API base URL */
|
|
46
57
|
export const DEFAULT_API_BASE_URL = 'https://api.ccip.chain.link'
|
|
@@ -51,7 +62,7 @@ export const DEFAULT_TIMEOUT_MS = 30000
|
|
|
51
62
|
/** SDK version string for telemetry header */
|
|
52
63
|
// generate:nofail
|
|
53
64
|
// `export const SDK_VERSION = '${require('./package.json').version}-${require('child_process').execSync('git rev-parse --short HEAD').toString().trim()}'`
|
|
54
|
-
export const SDK_VERSION = '1.
|
|
65
|
+
export const SDK_VERSION = '1.2.0-7dcdd16'
|
|
55
66
|
// generate:end
|
|
56
67
|
|
|
57
68
|
/** SDK telemetry header name */
|
|
@@ -87,6 +98,7 @@ const validateMessageStatus = (value: string, logger: Logger): MessageStatus =>
|
|
|
87
98
|
|
|
88
99
|
const ensureNetworkInfo = (o: RawNetworkInfo, logger: Logger): NetworkInfo => {
|
|
89
100
|
return Object.assign(o, {
|
|
101
|
+
chainSelector: BigInt(o.chainSelector),
|
|
90
102
|
networkType: o.name.includes('-mainnet') ? NetworkType.Mainnet : NetworkType.Testnet,
|
|
91
103
|
...(!('family' in o) && { family: validateChainFamily(o.chainFamily, logger) }),
|
|
92
104
|
}) as unknown as NetworkInfo
|
|
@@ -191,25 +203,33 @@ export class CCIPAPIClient {
|
|
|
191
203
|
* @throws CCIPTimeoutError if request times out
|
|
192
204
|
* @internal
|
|
193
205
|
*/
|
|
194
|
-
private async _fetchWithTimeout(
|
|
195
|
-
|
|
196
|
-
|
|
206
|
+
private async _fetchWithTimeout(
|
|
207
|
+
url: string,
|
|
208
|
+
operation: string,
|
|
209
|
+
signal?: AbortSignal,
|
|
210
|
+
): Promise<Response> {
|
|
211
|
+
const timeoutSignal = AbortSignal.timeout(this.timeoutMs)
|
|
212
|
+
const combinedSignal = signal ? AbortSignal.any([timeoutSignal, signal]) : timeoutSignal
|
|
197
213
|
|
|
198
214
|
try {
|
|
199
215
|
return await this._fetch(url, {
|
|
200
|
-
signal:
|
|
216
|
+
signal: combinedSignal,
|
|
201
217
|
headers: {
|
|
202
218
|
'Content-Type': 'application/json',
|
|
203
219
|
[SDK_VERSION_HEADER]: `CCIP SDK v${SDK_VERSION}`,
|
|
204
220
|
},
|
|
205
221
|
})
|
|
206
222
|
} catch (error) {
|
|
207
|
-
if (
|
|
223
|
+
if (
|
|
224
|
+
error instanceof Error &&
|
|
225
|
+
(error.name === 'AbortError' || error.name === 'TimeoutError')
|
|
226
|
+
) {
|
|
227
|
+
if (signal?.aborted) {
|
|
228
|
+
throw new CCIPAbortError(operation)
|
|
229
|
+
}
|
|
208
230
|
throw new CCIPTimeoutError(operation, this.timeoutMs)
|
|
209
231
|
}
|
|
210
232
|
throw error
|
|
211
|
-
} finally {
|
|
212
|
-
clearTimeout(timeoutId)
|
|
213
233
|
}
|
|
214
234
|
}
|
|
215
235
|
|
|
@@ -218,10 +238,15 @@ export class CCIPAPIClient {
|
|
|
218
238
|
*
|
|
219
239
|
* @param sourceChainSelector - Source chain selector (bigint)
|
|
220
240
|
* @param destChainSelector - Destination chain selector (bigint)
|
|
241
|
+
* @param numberOfBlocks - Optional number of block confirmations for latency calculation.
|
|
242
|
+
* When omitted or 0, uses the lane's default finality. When provided as a positive
|
|
243
|
+
* integer, the API returns latency for that custom finality value (sent as `numOfBlocks`
|
|
244
|
+
* query parameter).
|
|
221
245
|
* @returns Promise resolving to {@link LaneLatencyResponse} with totalMs
|
|
222
246
|
*
|
|
223
247
|
* @throws {@link CCIPLaneNotFoundError} when lane not found (404)
|
|
224
248
|
* @throws {@link CCIPTimeoutError} if request times out
|
|
249
|
+
* @throws {@link CCIPAbortError} if request is aborted via signal
|
|
225
250
|
* @throws {@link CCIPHttpError} on other HTTP errors with context:
|
|
226
251
|
* - `status` - HTTP status code (e.g., 500)
|
|
227
252
|
* - `statusText` - HTTP status message
|
|
@@ -237,6 +262,15 @@ export class CCIPAPIClient {
|
|
|
237
262
|
* console.log(`Estimated delivery: ${Math.round(latency.totalMs / 60000)} minutes`)
|
|
238
263
|
* ```
|
|
239
264
|
*
|
|
265
|
+
* @example Custom block confirmations
|
|
266
|
+
* ```typescript
|
|
267
|
+
* const latency = await api.getLaneLatency(
|
|
268
|
+
* 5009297550715157269n, // Ethereum mainnet
|
|
269
|
+
* 4949039107694359620n, // Arbitrum mainnet
|
|
270
|
+
* 10, // 10 block confirmations
|
|
271
|
+
* )
|
|
272
|
+
* ```
|
|
273
|
+
*
|
|
240
274
|
* @example Handling specific API errors
|
|
241
275
|
* ```typescript
|
|
242
276
|
* try {
|
|
@@ -251,14 +285,19 @@ export class CCIPAPIClient {
|
|
|
251
285
|
async getLaneLatency(
|
|
252
286
|
sourceChainSelector: bigint,
|
|
253
287
|
destChainSelector: bigint,
|
|
288
|
+
numberOfBlocks?: number,
|
|
289
|
+
options?: { signal?: AbortSignal },
|
|
254
290
|
): Promise<LaneLatencyResponse> {
|
|
255
291
|
const url = new URL(`${this.baseUrl}/v2/lanes/latency`)
|
|
256
292
|
url.searchParams.set('sourceChainSelector', sourceChainSelector.toString())
|
|
257
293
|
url.searchParams.set('destChainSelector', destChainSelector.toString())
|
|
294
|
+
if (numberOfBlocks) {
|
|
295
|
+
url.searchParams.set('numOfBlocks', numberOfBlocks.toString())
|
|
296
|
+
}
|
|
258
297
|
|
|
259
298
|
this.logger.debug(`CCIPAPIClient: GET ${url.toString()}`)
|
|
260
299
|
|
|
261
|
-
const response = await this._fetchWithTimeout(url.toString(), 'getLaneLatency')
|
|
300
|
+
const response = await this._fetchWithTimeout(url.toString(), 'getLaneLatency', options?.signal)
|
|
262
301
|
|
|
263
302
|
if (!response.ok) {
|
|
264
303
|
// Try to parse structured error response from API
|
|
@@ -308,6 +347,7 @@ export class CCIPAPIClient {
|
|
|
308
347
|
*
|
|
309
348
|
* @throws {@link CCIPMessageIdNotFoundError} when message not found (404)
|
|
310
349
|
* @throws {@link CCIPTimeoutError} if request times out
|
|
350
|
+
* @throws {@link CCIPAbortError} if request is aborted via signal
|
|
311
351
|
* @throws {@link CCIPHttpError} on HTTP errors with context:
|
|
312
352
|
* - `status` - HTTP status code
|
|
313
353
|
* - `statusText` - HTTP status message
|
|
@@ -334,12 +374,15 @@ export class CCIPAPIClient {
|
|
|
334
374
|
* }
|
|
335
375
|
* ```
|
|
336
376
|
*/
|
|
337
|
-
async getMessageById(
|
|
377
|
+
async getMessageById(
|
|
378
|
+
messageId: string,
|
|
379
|
+
options?: { signal?: AbortSignal },
|
|
380
|
+
): Promise<SetRequired<CCIPRequest, 'metadata'>> {
|
|
338
381
|
const url = `${this.baseUrl}/v2/messages/${encodeURIComponent(messageId)}`
|
|
339
382
|
|
|
340
383
|
this.logger.debug(`CCIPAPIClient: GET ${url}`)
|
|
341
384
|
|
|
342
|
-
const response = await this._fetchWithTimeout(url, 'getMessageById')
|
|
385
|
+
const response = await this._fetchWithTimeout(url, 'getMessageById', options?.signal)
|
|
343
386
|
|
|
344
387
|
if (!response.ok) {
|
|
345
388
|
// Try to parse structured error response from API
|
|
@@ -379,93 +422,260 @@ export class CCIPAPIClient {
|
|
|
379
422
|
}
|
|
380
423
|
|
|
381
424
|
/**
|
|
382
|
-
*
|
|
425
|
+
* Searches CCIP messages using filters with cursor-based pagination.
|
|
383
426
|
*
|
|
384
|
-
* @param
|
|
385
|
-
*
|
|
427
|
+
* @param filters - Optional search filters. Ignored when `options.cursor` is provided
|
|
428
|
+
* (the cursor already encodes the original filters).
|
|
429
|
+
* @param options - Optional pagination options: `limit` (max results per page) and
|
|
430
|
+
* `cursor` (opaque token from a previous {@link MessageSearchPage} for the next page).
|
|
431
|
+
* @returns Promise resolving to a {@link MessageSearchPage} with results and pagination info.
|
|
386
432
|
*
|
|
387
|
-
* @
|
|
388
|
-
*
|
|
389
|
-
*
|
|
390
|
-
*
|
|
433
|
+
* @remarks
|
|
434
|
+
* A 404 response is treated as "no results found" and returns an empty page,
|
|
435
|
+
* unlike {@link CCIPAPIClient.getMessageById} which throws on 404.
|
|
436
|
+
* When paginating with a cursor, the `filters` parameter is ignored because
|
|
437
|
+
* the cursor encodes the original filters.
|
|
391
438
|
*
|
|
392
|
-
* @
|
|
439
|
+
* @throws {@link CCIPTimeoutError} if request times out.
|
|
440
|
+
* @throws {@link CCIPAbortError} if request is aborted via signal.
|
|
441
|
+
* @throws {@link CCIPHttpError} on HTTP errors (4xx/5xx, except 404 which returns empty).
|
|
442
|
+
*
|
|
443
|
+
* @see {@link MessageSearchFilters} — available filter fields
|
|
444
|
+
* @see {@link MessageSearchPage} — return type with pagination
|
|
445
|
+
* @see {@link CCIPAPIClient.searchAllMessages} — async generator that handles pagination automatically
|
|
446
|
+
* @see {@link CCIPAPIClient.getMessageById} — fetch full message details for a search result
|
|
447
|
+
* @see {@link CCIPAPIClient.getMessageIdsInTx} — convenience wrapper using `sourceTransactionHash`
|
|
448
|
+
*
|
|
449
|
+
* @example Search by sender
|
|
393
450
|
* ```typescript
|
|
394
|
-
* const
|
|
395
|
-
* '
|
|
396
|
-
* )
|
|
397
|
-
*
|
|
451
|
+
* const page = await api.searchMessages({
|
|
452
|
+
* sender: '0x9d087fC03ae39b088326b67fA3C788236645b717',
|
|
453
|
+
* })
|
|
454
|
+
* for (const msg of page.data) {
|
|
455
|
+
* console.log(`${msg.messageId}: ${msg.status}`)
|
|
456
|
+
* }
|
|
457
|
+
* ```
|
|
458
|
+
*
|
|
459
|
+
* @example Paginate through all results
|
|
460
|
+
* ```typescript
|
|
461
|
+
* let page = await api.searchMessages({ sender: '0x...' }, { limit: 10 })
|
|
462
|
+
* const all = [...page.data]
|
|
463
|
+
* while (page.hasNextPage) {
|
|
464
|
+
* page = await api.searchMessages(undefined, { cursor: page.cursor! })
|
|
465
|
+
* all.push(...page.data)
|
|
466
|
+
* }
|
|
467
|
+
* ```
|
|
468
|
+
*
|
|
469
|
+
* @example Filter by lane and sender
|
|
470
|
+
* ```typescript
|
|
471
|
+
* const page = await api.searchMessages({
|
|
472
|
+
* sender: '0x9d087fC03ae39b088326b67fA3C788236645b717',
|
|
473
|
+
* sourceChainSelector: 16015286601757825753n,
|
|
474
|
+
* destChainSelector: 14767482510784806043n,
|
|
475
|
+
* })
|
|
398
476
|
* ```
|
|
399
477
|
*/
|
|
400
|
-
async
|
|
478
|
+
async searchMessages(
|
|
479
|
+
filters?: MessageSearchFilters,
|
|
480
|
+
options?: { limit?: number; cursor?: string; signal?: AbortSignal },
|
|
481
|
+
): Promise<MessageSearchPage> {
|
|
401
482
|
const url = new URL(`${this.baseUrl}/v2/messages`)
|
|
402
|
-
|
|
403
|
-
|
|
483
|
+
|
|
484
|
+
if (options?.cursor) {
|
|
485
|
+
// Cursor encodes all original filters — only send cursor (and optional limit)
|
|
486
|
+
url.searchParams.set('cursor', options.cursor)
|
|
487
|
+
} else if (filters) {
|
|
488
|
+
if (filters.sender) url.searchParams.set('sender', filters.sender)
|
|
489
|
+
if (filters.receiver) url.searchParams.set('receiver', filters.receiver)
|
|
490
|
+
if (filters.sourceChainSelector != null)
|
|
491
|
+
url.searchParams.set('sourceChainSelector', filters.sourceChainSelector.toString())
|
|
492
|
+
if (filters.destChainSelector != null)
|
|
493
|
+
url.searchParams.set('destChainSelector', filters.destChainSelector.toString())
|
|
494
|
+
if (filters.sourceTransactionHash)
|
|
495
|
+
url.searchParams.set('sourceTransactionHash', filters.sourceTransactionHash)
|
|
496
|
+
if (filters.readyForManualExecOnly != null)
|
|
497
|
+
url.searchParams.set('readyForManualExecOnly', String(filters.readyForManualExecOnly))
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
if (options?.limit != null) url.searchParams.set('limit', options.limit.toString())
|
|
404
501
|
|
|
405
502
|
this.logger.debug(`CCIPAPIClient: GET ${url.toString()}`)
|
|
406
503
|
|
|
407
|
-
const response = await this._fetchWithTimeout(url.toString(), '
|
|
504
|
+
const response = await this._fetchWithTimeout(url.toString(), 'searchMessages', options?.signal)
|
|
408
505
|
|
|
409
506
|
if (!response.ok) {
|
|
410
|
-
//
|
|
507
|
+
// 404 → empty results (search found nothing)
|
|
508
|
+
if (response.status === HttpStatus.NOT_FOUND) {
|
|
509
|
+
return { data: [], hasNextPage: false }
|
|
510
|
+
}
|
|
511
|
+
|
|
411
512
|
let apiError: APIErrorResponse | undefined
|
|
412
513
|
try {
|
|
413
514
|
apiError = parseJson<APIErrorResponse>(await response.text())
|
|
414
515
|
} catch {
|
|
415
|
-
// Response body not JSON
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
// 404 - No messages found
|
|
419
|
-
if (response.status === HttpStatus.NOT_FOUND) {
|
|
420
|
-
throw new CCIPMessageNotFoundInTxError(txHash, {
|
|
421
|
-
context: apiError
|
|
422
|
-
? {
|
|
423
|
-
apiErrorCode: apiError.error,
|
|
424
|
-
apiErrorMessage: apiError.message,
|
|
425
|
-
}
|
|
426
|
-
: undefined,
|
|
427
|
-
})
|
|
516
|
+
// Response body not JSON
|
|
428
517
|
}
|
|
429
518
|
|
|
430
|
-
// Generic HTTP error for other cases
|
|
431
519
|
throw new CCIPHttpError(response.status, response.statusText, {
|
|
432
520
|
context: apiError
|
|
433
|
-
? {
|
|
434
|
-
apiErrorCode: apiError.error,
|
|
435
|
-
apiErrorMessage: apiError.message,
|
|
436
|
-
}
|
|
521
|
+
? { apiErrorCode: apiError.error, apiErrorMessage: apiError.message }
|
|
437
522
|
: undefined,
|
|
438
523
|
})
|
|
439
524
|
}
|
|
440
525
|
|
|
441
526
|
const raw = parseJson<RawMessagesResponse>(await response.text())
|
|
442
527
|
|
|
443
|
-
this.logger.debug('
|
|
528
|
+
this.logger.debug('searchMessages raw response:', raw)
|
|
444
529
|
|
|
445
|
-
|
|
446
|
-
|
|
530
|
+
return {
|
|
531
|
+
data: raw.data.map((msg) => {
|
|
532
|
+
const sourceInfo = ensureNetworkInfo(msg.sourceNetworkInfo, this.logger)
|
|
533
|
+
const destInfo = ensureNetworkInfo(msg.destNetworkInfo, this.logger)
|
|
534
|
+
return {
|
|
535
|
+
...msg,
|
|
536
|
+
status: validateMessageStatus(msg.status, this.logger),
|
|
537
|
+
sourceNetworkInfo: sourceInfo,
|
|
538
|
+
destNetworkInfo: destInfo,
|
|
539
|
+
sender: decodeAddress(msg.sender, sourceInfo.family),
|
|
540
|
+
receiver: decodeAddress(msg.receiver, destInfo.family),
|
|
541
|
+
origin: decodeAddress(msg.origin, sourceInfo.family),
|
|
542
|
+
}
|
|
543
|
+
}),
|
|
544
|
+
hasNextPage: raw.pagination.hasNextPage,
|
|
545
|
+
cursor: raw.pagination.cursor ?? undefined,
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* Async generator that streams all messages matching the given filters,
|
|
551
|
+
* handling cursor-based pagination automatically.
|
|
552
|
+
*
|
|
553
|
+
* @param filters - Optional search filters (same as {@link CCIPAPIClient.searchMessages}).
|
|
554
|
+
* @param options - Optional `limit` controlling the per-page fetch size (number of
|
|
555
|
+
* results fetched per API call). The total number of results is controlled by the
|
|
556
|
+
* consumer — break out of the loop to stop early.
|
|
557
|
+
* @returns AsyncGenerator yielding {@link MessageSearchResult} one at a time, across all pages.
|
|
558
|
+
*
|
|
559
|
+
* @throws {@link CCIPTimeoutError} if a page request times out.
|
|
560
|
+
* @throws {@link CCIPAbortError} if a page request is aborted via signal.
|
|
561
|
+
* @throws {@link CCIPHttpError} on HTTP errors (4xx/5xx, except 404 which yields nothing).
|
|
562
|
+
*
|
|
563
|
+
* @see {@link CCIPAPIClient.searchMessages} — for page-level control and explicit cursor handling
|
|
564
|
+
* @see {@link CCIPAPIClient.getMessageById} — fetch full message details for a search result
|
|
565
|
+
*
|
|
566
|
+
* @example Iterate all messages for a sender
|
|
567
|
+
* ```typescript
|
|
568
|
+
* for await (const msg of api.searchAllMessages({ sender: '0x...' })) {
|
|
569
|
+
* console.log(`${msg.messageId}: ${msg.status}`)
|
|
570
|
+
* }
|
|
571
|
+
* ```
|
|
572
|
+
*
|
|
573
|
+
* @example Stop after collecting 5 results
|
|
574
|
+
* ```typescript
|
|
575
|
+
* const results: MessageSearchResult[] = []
|
|
576
|
+
* for await (const msg of api.searchAllMessages({ sender: '0x...' })) {
|
|
577
|
+
* results.push(msg)
|
|
578
|
+
* if (results.length >= 5) break
|
|
579
|
+
* }
|
|
580
|
+
* ```
|
|
581
|
+
*/
|
|
582
|
+
async *searchAllMessages(
|
|
583
|
+
filters?: MessageSearchFilters,
|
|
584
|
+
options?: { limit?: number; signal?: AbortSignal },
|
|
585
|
+
): AsyncGenerator<MessageSearchResult> {
|
|
586
|
+
let cursor: string | undefined
|
|
587
|
+
do {
|
|
588
|
+
const page = await this.searchMessages(filters, {
|
|
589
|
+
limit: options?.limit,
|
|
590
|
+
cursor,
|
|
591
|
+
signal: options?.signal,
|
|
592
|
+
})
|
|
593
|
+
yield* page.data
|
|
594
|
+
cursor = page.cursor
|
|
595
|
+
} while (cursor)
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Fetches message IDs from a source transaction hash.
|
|
600
|
+
*
|
|
601
|
+
* @remarks
|
|
602
|
+
* Uses {@link CCIPAPIClient.searchMessages} internally with `sourceTransactionHash` filter and `limit: 100`.
|
|
603
|
+
*
|
|
604
|
+
* @param txHash - Source transaction hash.
|
|
605
|
+
* @returns Promise resolving to array of message IDs.
|
|
606
|
+
*
|
|
607
|
+
* @throws {@link CCIPMessageNotFoundInTxError} when no messages found (404 or empty).
|
|
608
|
+
* @throws {@link CCIPUnexpectedPaginationError} when hasNextPage is true.
|
|
609
|
+
* @throws {@link CCIPTimeoutError} if request times out.
|
|
610
|
+
* @throws {@link CCIPAbortError} if request is aborted via signal.
|
|
611
|
+
* @throws {@link CCIPHttpError} on HTTP errors.
|
|
612
|
+
*
|
|
613
|
+
* @example Basic usage
|
|
614
|
+
* ```typescript
|
|
615
|
+
* const messageIds = await api.getMessageIdsInTx(
|
|
616
|
+
* '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'
|
|
617
|
+
* )
|
|
618
|
+
* console.log(`Found ${messageIds.length} messages`)
|
|
619
|
+
* ```
|
|
620
|
+
*
|
|
621
|
+
* @example Fetch full details for each message
|
|
622
|
+
* ```typescript
|
|
623
|
+
* const api = CCIPAPIClient.fromUrl()
|
|
624
|
+
* const messageIds = await api.getMessageIdsInTx(txHash)
|
|
625
|
+
* for (const id of messageIds) {
|
|
626
|
+
* const request = await api.getMessageById(id)
|
|
627
|
+
* console.log(`${id}: ${request.metadata.status}`)
|
|
628
|
+
* }
|
|
629
|
+
* ```
|
|
630
|
+
*/
|
|
631
|
+
async getMessageIdsInTx(txHash: string, options?: { signal?: AbortSignal }): Promise<string[]> {
|
|
632
|
+
const result = await this.searchMessages(
|
|
633
|
+
{ sourceTransactionHash: txHash },
|
|
634
|
+
{ limit: 100, signal: options?.signal },
|
|
635
|
+
)
|
|
636
|
+
|
|
637
|
+
if (result.data.length === 0) {
|
|
447
638
|
throw new CCIPMessageNotFoundInTxError(txHash)
|
|
448
639
|
}
|
|
449
640
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
throw new CCIPUnexpectedPaginationError(txHash, raw.data.length)
|
|
641
|
+
if (result.hasNextPage) {
|
|
642
|
+
throw new CCIPUnexpectedPaginationError(txHash, result.data.length)
|
|
453
643
|
}
|
|
454
644
|
|
|
455
|
-
return
|
|
645
|
+
return result.data.map((msg) => msg.messageId)
|
|
456
646
|
}
|
|
457
647
|
|
|
458
648
|
/**
|
|
459
649
|
* Fetches the execution input for a given message by id.
|
|
460
|
-
*
|
|
461
|
-
*
|
|
650
|
+
* For v2.0 messages, returns `{ encodedMessage, verifications }`.
|
|
651
|
+
* For pre-v2 messages, returns `{ message, offchainTokenData, proofs, ... }` with merkle proof.
|
|
652
|
+
*
|
|
653
|
+
* @param messageId - The CCIP message ID (32-byte hex string)
|
|
654
|
+
* @returns Execution input with offRamp address and lane info
|
|
655
|
+
*
|
|
656
|
+
* @throws {@link CCIPMessageIdNotFoundError} when message not found (404)
|
|
657
|
+
* @throws {@link CCIPTimeoutError} if request times out
|
|
658
|
+
* @throws {@link CCIPAbortError} if request is aborted via signal
|
|
659
|
+
* @throws {@link CCIPHttpError} on other HTTP errors
|
|
660
|
+
*
|
|
661
|
+
* @example
|
|
662
|
+
* ```typescript
|
|
663
|
+
* const api = CCIPAPIClient.fromUrl()
|
|
664
|
+
* const execInput = await api.getExecutionInput('0x1234...')
|
|
665
|
+
* // Use with dest.execute():
|
|
666
|
+
* const { offRamp, ...input } = execInput
|
|
667
|
+
* await dest.execute({ offRamp, input, wallet })
|
|
668
|
+
* ```
|
|
462
669
|
*/
|
|
463
|
-
async getExecutionInput(
|
|
670
|
+
async getExecutionInput(
|
|
671
|
+
messageId: string,
|
|
672
|
+
options?: { signal?: AbortSignal },
|
|
673
|
+
): Promise<ExecutionInput & Lane & { offRamp: string }> {
|
|
464
674
|
const url = `${this.baseUrl}/v2/messages/${encodeURIComponent(messageId)}/execution-inputs`
|
|
465
675
|
|
|
466
676
|
this.logger.debug(`CCIPAPIClient: GET ${url}`)
|
|
467
677
|
|
|
468
|
-
const response = await this._fetchWithTimeout(url, 'getExecutionInput')
|
|
678
|
+
const response = await this._fetchWithTimeout(url, 'getExecutionInput', options?.signal)
|
|
469
679
|
if (!response.ok) {
|
|
470
680
|
// Try to parse structured error response from API
|
|
471
681
|
let apiError: APIErrorResponse | undefined
|
|
@@ -578,6 +788,7 @@ export class CCIPAPIClient {
|
|
|
578
788
|
status,
|
|
579
789
|
origin,
|
|
580
790
|
onramp,
|
|
791
|
+
offramp,
|
|
581
792
|
version,
|
|
582
793
|
readyForManualExecution,
|
|
583
794
|
sendTransactionHash,
|
|
@@ -647,6 +858,7 @@ export class CCIPAPIClient {
|
|
|
647
858
|
deliveryTime,
|
|
648
859
|
sourceNetworkInfo: ensureNetworkInfo(sourceNetworkInfo, this.logger),
|
|
649
860
|
destNetworkInfo: ensureNetworkInfo(destNetworkInfo, this.logger),
|
|
861
|
+
offRamp: offramp,
|
|
650
862
|
},
|
|
651
863
|
}
|
|
652
864
|
}
|
package/src/api/types.ts
CHANGED
|
@@ -105,6 +105,7 @@ export type RawMessageResponse = {
|
|
|
105
105
|
origin: string
|
|
106
106
|
sequenceNumber: string
|
|
107
107
|
onramp: string
|
|
108
|
+
offramp?: string
|
|
108
109
|
sendBlockNumber: bigint
|
|
109
110
|
sendLogIndex: bigint
|
|
110
111
|
// Optional fields
|
|
@@ -144,6 +145,121 @@ export type RawMessagesResponse = {
|
|
|
144
145
|
}
|
|
145
146
|
}
|
|
146
147
|
|
|
148
|
+
// ============================================================================
|
|
149
|
+
// searchMessages public types
|
|
150
|
+
// ============================================================================
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Filters for searching CCIP messages via the API.
|
|
154
|
+
*
|
|
155
|
+
* All fields are optional — omit a field to leave it unfiltered.
|
|
156
|
+
* Chain selectors are accepted as `bigint` and converted to strings for the API.
|
|
157
|
+
*
|
|
158
|
+
* @see {@link CCIPAPIClient.searchMessages}
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```typescript
|
|
162
|
+
* const api = CCIPAPIClient.fromUrl()
|
|
163
|
+
* // Find messages from a specific sender on a specific lane
|
|
164
|
+
* const page = await api.searchMessages({
|
|
165
|
+
* sender: '0x9d087fC03ae39b088326b67fA3C788236645b717',
|
|
166
|
+
* sourceChainSelector: 16015286601757825753n,
|
|
167
|
+
* destChainSelector: 14767482510784806043n,
|
|
168
|
+
* })
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
export type MessageSearchFilters = {
|
|
172
|
+
/** Filter by sender address */
|
|
173
|
+
sender?: string
|
|
174
|
+
/** Filter by receiver address */
|
|
175
|
+
receiver?: string
|
|
176
|
+
/** Filter by source chain selector */
|
|
177
|
+
sourceChainSelector?: bigint
|
|
178
|
+
/** Filter by destination chain selector */
|
|
179
|
+
destChainSelector?: bigint
|
|
180
|
+
/** Filter by source transaction hash */
|
|
181
|
+
sourceTransactionHash?: string
|
|
182
|
+
/** When `true`, return only messages eligible for manual execution (stuck/failed messages) */
|
|
183
|
+
readyForManualExecOnly?: boolean
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* A single message search result from the CCIP API.
|
|
188
|
+
*
|
|
189
|
+
* @remarks
|
|
190
|
+
* This is a lightweight summary — it does not include `extraArgs`, `tokenAmounts`,
|
|
191
|
+
* `fees`, or other detailed fields available via {@link CCIPAPIClient.getMessageById}.
|
|
192
|
+
*
|
|
193
|
+
* @see {@link CCIPAPIClient.getMessageById} — to fetch full message details
|
|
194
|
+
* @see {@link CCIPAPIClient.searchMessages}
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```typescript
|
|
198
|
+
* const page = await api.searchMessages({ sender: '0x...' })
|
|
199
|
+
* for (const msg of page.data) {
|
|
200
|
+
* console.log(`${msg.messageId}: ${msg.status} (${msg.sourceNetworkInfo.name} → ${msg.destNetworkInfo.name})`)
|
|
201
|
+
* }
|
|
202
|
+
* ```
|
|
203
|
+
*/
|
|
204
|
+
export type MessageSearchResult = {
|
|
205
|
+
/** Unique CCIP message ID (0x-prefixed, 32-byte hex string) */
|
|
206
|
+
messageId: string
|
|
207
|
+
/** Transaction originator address (EOA that submitted the send transaction) */
|
|
208
|
+
origin: string
|
|
209
|
+
/** Message sender address */
|
|
210
|
+
sender: string
|
|
211
|
+
/** Message receiver address */
|
|
212
|
+
receiver: string
|
|
213
|
+
/** Message lifecycle status */
|
|
214
|
+
status: MessageStatus
|
|
215
|
+
/** Source network metadata */
|
|
216
|
+
sourceNetworkInfo: NetworkInfo
|
|
217
|
+
/** Destination network metadata */
|
|
218
|
+
destNetworkInfo: NetworkInfo
|
|
219
|
+
/** Source chain transaction hash */
|
|
220
|
+
sendTransactionHash: string
|
|
221
|
+
/** ISO 8601 timestamp of the send transaction */
|
|
222
|
+
sendTimestamp: string
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* A page of message search results with cursor-based pagination.
|
|
227
|
+
*
|
|
228
|
+
* @remarks
|
|
229
|
+
* When `hasNextPage` is `true`, pass the `cursor` value to
|
|
230
|
+
* {@link CCIPAPIClient.searchMessages} to fetch the next page.
|
|
231
|
+
* The cursor encodes all original filters, so you do not need
|
|
232
|
+
* to re-supply them when paginating.
|
|
233
|
+
*
|
|
234
|
+
* @see {@link MessageSearchFilters}
|
|
235
|
+
* @see {@link CCIPAPIClient.searchMessages}
|
|
236
|
+
* @see {@link CCIPAPIClient.searchAllMessages} — async generator alternative that handles
|
|
237
|
+
* pagination automatically
|
|
238
|
+
*
|
|
239
|
+
* @example Manual pagination
|
|
240
|
+
* ```typescript
|
|
241
|
+
* let page = await api.searchMessages({ sender: '0x...' }, { limit: 10 })
|
|
242
|
+
* while (page.hasNextPage) {
|
|
243
|
+
* page = await api.searchMessages(undefined, { cursor: page.cursor! })
|
|
244
|
+
* }
|
|
245
|
+
* ```
|
|
246
|
+
*
|
|
247
|
+
* @example Automatic pagination (preferred for most use cases)
|
|
248
|
+
* ```typescript
|
|
249
|
+
* for await (const msg of api.searchAllMessages({ sender: '0x...' })) {
|
|
250
|
+
* console.log(msg.messageId)
|
|
251
|
+
* }
|
|
252
|
+
* ```
|
|
253
|
+
*/
|
|
254
|
+
export type MessageSearchPage = {
|
|
255
|
+
/** Array of message search results */
|
|
256
|
+
data: MessageSearchResult[]
|
|
257
|
+
/** Whether more results are available */
|
|
258
|
+
hasNextPage: boolean
|
|
259
|
+
/** Opaque cursor for fetching the next page */
|
|
260
|
+
cursor?: string
|
|
261
|
+
}
|
|
262
|
+
|
|
147
263
|
// ============================================================================
|
|
148
264
|
// APICCIPRequest type - derived from CCIPRequest
|
|
149
265
|
// ============================================================================
|
|
@@ -181,13 +297,22 @@ export type APICCIPRequestMetadata = {
|
|
|
181
297
|
sourceNetworkInfo: NetworkInfo
|
|
182
298
|
/** Destination network metadata. */
|
|
183
299
|
destNetworkInfo: NetworkInfo
|
|
300
|
+
/** OffRamp address on dest */
|
|
301
|
+
offRamp?: string
|
|
184
302
|
}
|
|
185
303
|
|
|
186
304
|
// ============================================================================
|
|
187
305
|
// GET /v2/messages/${messageId}/execution-inputs search endpoint types
|
|
188
306
|
// ============================================================================
|
|
189
307
|
|
|
190
|
-
/**
|
|
308
|
+
/**
|
|
309
|
+
* Raw API response from GET /v2/messages/:messageId/execution-inputs.
|
|
310
|
+
*
|
|
311
|
+
* @remarks
|
|
312
|
+
* The response has two union branches:
|
|
313
|
+
* - **v2.0+**: contains `encodedMessage` (MessageV1Codec-serialized), optional `ccvData` array, and `verifierAddresses`
|
|
314
|
+
* - **pre-v2**: contains `messageBatch` array with decoded messages, `merkleRoot`, and optional USDC/LBTC attestation data
|
|
315
|
+
*/
|
|
191
316
|
export type RawExecutionInputsResult = {
|
|
192
317
|
offramp: string
|
|
193
318
|
} & (
|
package/src/aptos/index.ts
CHANGED
|
@@ -560,13 +560,11 @@ export class AptosChain extends Chain<typeof ChainFamily.Aptos> {
|
|
|
560
560
|
payer,
|
|
561
561
|
...opts
|
|
562
562
|
}: Parameters<Chain['generateUnsignedExecute']>[0]): Promise<UnsignedAptosTx> {
|
|
563
|
+
const resolved = await this.resolveExecuteOpts(opts)
|
|
563
564
|
if (
|
|
564
|
-
!(
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
'allowOutOfOrderExecution' in opts.input.message &&
|
|
568
|
-
'gasLimit' in opts.input.message
|
|
569
|
-
)
|
|
565
|
+
!('message' in resolved.input) ||
|
|
566
|
+
!('allowOutOfOrderExecution' in resolved.input.message) ||
|
|
567
|
+
!('gasLimit' in resolved.input.message)
|
|
570
568
|
) {
|
|
571
569
|
throw new CCIPAptosExtraArgsV2RequiredError()
|
|
572
570
|
}
|
|
@@ -574,9 +572,9 @@ export class AptosChain extends Chain<typeof ChainFamily.Aptos> {
|
|
|
574
572
|
const tx = await generateUnsignedExecuteReport(
|
|
575
573
|
this.provider,
|
|
576
574
|
payer,
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
575
|
+
resolved.offRamp,
|
|
576
|
+
resolved.input as ExecutionInput<CCIPMessage_V1_6_EVM>,
|
|
577
|
+
resolved,
|
|
580
578
|
)
|
|
581
579
|
return {
|
|
582
580
|
family: ChainFamily.Aptos,
|