@chainlink/ccip-cli 0.92.1 → 0.94.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 (44) hide show
  1. package/dist/commands/lane-latency.d.ts +26 -0
  2. package/dist/commands/lane-latency.d.ts.map +1 -0
  3. package/dist/commands/lane-latency.js +73 -0
  4. package/dist/commands/lane-latency.js.map +1 -0
  5. package/dist/commands/manual-exec.d.ts.map +1 -1
  6. package/dist/commands/manual-exec.js +20 -269
  7. package/dist/commands/manual-exec.js.map +1 -1
  8. package/dist/commands/send.d.ts +1 -1
  9. package/dist/commands/send.js +30 -42
  10. package/dist/commands/send.js.map +1 -1
  11. package/dist/commands/show.d.ts.map +1 -1
  12. package/dist/commands/show.js +36 -16
  13. package/dist/commands/show.js.map +1 -1
  14. package/dist/commands/utils.d.ts.map +1 -1
  15. package/dist/commands/utils.js +2 -1
  16. package/dist/commands/utils.js.map +1 -1
  17. package/dist/index.d.ts +5 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +7 -3
  20. package/dist/index.js.map +1 -1
  21. package/dist/providers/aptos.js +1 -1
  22. package/dist/providers/aptos.js.map +1 -1
  23. package/dist/providers/evm.js +1 -1
  24. package/dist/providers/evm.js.map +1 -1
  25. package/dist/providers/index.d.ts +4 -2
  26. package/dist/providers/index.d.ts.map +1 -1
  27. package/dist/providers/index.js +11 -8
  28. package/dist/providers/index.js.map +1 -1
  29. package/dist/providers/ton.d.ts +8 -5
  30. package/dist/providers/ton.d.ts.map +1 -1
  31. package/dist/providers/ton.js +100 -26
  32. package/dist/providers/ton.js.map +1 -1
  33. package/package.json +20 -13
  34. package/src/commands/lane-latency.ts +93 -0
  35. package/src/commands/manual-exec.ts +18 -267
  36. package/src/commands/send.ts +34 -52
  37. package/src/commands/show.ts +40 -22
  38. package/src/commands/utils.ts +6 -4
  39. package/src/index.ts +8 -4
  40. package/src/providers/aptos.ts +1 -1
  41. package/src/providers/evm.ts +1 -1
  42. package/src/providers/index.ts +18 -14
  43. package/src/providers/ton.ts +109 -27
  44. package/tsconfig.json +3 -2
@@ -1,9 +1,9 @@
1
1
  import {
2
- type AnyMessage,
3
2
  type CCIPVersion,
4
3
  type ChainStatic,
5
4
  type EVMChain,
6
5
  type ExtraArgs,
6
+ type MessageInput,
7
7
  CCIPArgumentInvalidError,
8
8
  CCIPChainFamilyUnsupportedError,
9
9
  CCIPTokenNotFoundError,
@@ -13,7 +13,7 @@ import {
13
13
  networkInfo,
14
14
  sourceToDestTokenAmounts,
15
15
  } from '@chainlink/ccip-sdk/src/index.ts'
16
- import { type BytesLike, dataLength, formatUnits, toUtf8Bytes } from 'ethers'
16
+ import { type BytesLike, formatUnits, toUtf8Bytes } from 'ethers'
17
17
  import type { Argv } from 'yargs'
18
18
 
19
19
  import type { GlobalOpts } from '../index.ts'
@@ -67,7 +67,6 @@ export const builder = (yargs: Argv) =>
67
67
  type: 'number',
68
68
  describe:
69
69
  'Gas limit for receiver callback execution; defaults to default configured on ramps',
70
- default: 0,
71
70
  },
72
71
  'estimate-gas-limit': {
73
72
  type: 'number',
@@ -105,10 +104,11 @@ export const builder = (yargs: Argv) =>
105
104
  describe: "Address of the Solana tokenReceiver (if different than program's receiver)",
106
105
  },
107
106
  account: {
107
+ alias: 'receiver-object-id',
108
108
  type: 'array',
109
109
  string: true,
110
110
  describe:
111
- 'List of accounts needed by Solana receiver program; append `=rw` to specify account as writable; can be specified multiple times',
111
+ 'List of accounts needed by Solana receiver program, or receiverObjectIds needed by Sui; On Solana, append `=rw` to specify account as writable; can be specified multiple times',
112
112
  example: 'requiredPdaAddress=rw',
113
113
  },
114
114
  'only-get-fee': {
@@ -159,15 +159,13 @@ async function sendMessage(
159
159
  const getChain = fetchChainsFromRpcs(ctx, argv)
160
160
  const source = await getChain(sourceNetwork.name)
161
161
 
162
- let data: BytesLike
162
+ let data: BytesLike | undefined
163
163
  if (argv.data) {
164
164
  try {
165
165
  data = getDataBytes(argv.data)
166
166
  } catch (_) {
167
167
  data = toUtf8Bytes(argv.data)
168
168
  }
169
- } else {
170
- data = '0x'
171
169
  }
172
170
 
173
171
  const tokenAmounts: { token: string; amount: bigint }[] = argv.transferTokens?.length
@@ -175,25 +173,11 @@ async function sendMessage(
175
173
  : []
176
174
 
177
175
  let receiver = argv.receiver
178
- let tokenReceiver
179
176
  let accounts,
180
177
  accountIsWritableBitmap = 0n
181
178
  if (destNetwork.family === ChainFamily.Solana) {
182
- if (argv.tokenReceiver) tokenReceiver = argv.tokenReceiver
183
- else if (!tokenAmounts.length) {
184
- tokenReceiver = '11111111111111111111111111111111'
185
- } else if (!dataLength(data)) {
186
- // sending tokens without data, i.e. not for a receiver contract
187
- tokenReceiver = receiver
188
- receiver = '11111111111111111111111111111111'
189
- } else {
190
- throw new CCIPArgumentInvalidError(
191
- 'token-receiver',
192
- 'required when sending tokens with data to Solana',
193
- )
194
- }
195
-
196
- if (argv.account) {
179
+ // parse accounts with or without `=rw` suffix
180
+ if (argv.account?.length) {
197
181
  accounts = argv.account.map((account, i) => {
198
182
  if (account.endsWith('=rw')) {
199
183
  accountIsWritableBitmap |= 1n << BigInt(i)
@@ -201,12 +185,7 @@ async function sendMessage(
201
185
  }
202
186
  return account
203
187
  })
204
- } else accounts = [] as string[]
205
- } else if (argv.tokenReceiver || argv.account?.length) {
206
- throw new CCIPArgumentInvalidError(
207
- 'token-receiver/account',
208
- 'only valid for Solana destination',
209
- )
188
+ }
210
189
  }
211
190
 
212
191
  let walletAddress, wallet
@@ -244,7 +223,7 @@ async function sendMessage(
244
223
  message: {
245
224
  sender: walletAddress,
246
225
  receiver,
247
- data,
226
+ data: data || '0x',
248
227
  tokenAmounts: destTokenAmounts,
249
228
  },
250
229
  })
@@ -253,17 +232,19 @@ async function sendMessage(
253
232
  if (argv.onlyEstimate) return
254
233
  }
255
234
 
256
- // `--allow-out-of-order-exec` forces EVMExtraArgsV2, which shouldn't work on v1.2 lanes;
257
- // otherwise, fallsback to EVMExtraArgsV1 (compatible with v1.2 & v1.5)
235
+ // builds a catch-all extraArgs object, which can be massaged by
236
+ // [[Chain.buildMessageForDest]] to create suitable extraArgs with defaults if needed
258
237
  const extraArgs = {
259
- ...(argv.allowOutOfOrderExec != null || destNetwork.family !== ChainFamily.EVM
260
- ? { allowOutOfOrderExecution: !!argv.allowOutOfOrderExec }
261
- : {}),
262
- ...(destNetwork.family === ChainFamily.Solana
263
- ? { computeUnits: BigInt(argv.gasLimit) }
264
- : { gasLimit: BigInt(argv.gasLimit) }),
265
- ...(tokenReceiver ? { tokenReceiver } : {}),
266
- ...(accounts ? { accounts, accountIsWritableBitmap } : {}),
238
+ ...(argv.allowOutOfOrderExec != null && {
239
+ allowOutOfOrderExecution: !!argv.allowOutOfOrderExec,
240
+ }),
241
+ ...(argv.gasLimit == null
242
+ ? {}
243
+ : destNetwork.family === ChainFamily.Solana
244
+ ? { computeUnits: BigInt(argv.gasLimit) }
245
+ : { gasLimit: BigInt(argv.gasLimit) }),
246
+ ...(!!argv.tokenReceiver && { tokenReceiver: argv.tokenReceiver }),
247
+ ...(!!accounts && { accounts, accountIsWritableBitmap }), // accounts also used as Sui receiverObjectIds
267
248
  }
268
249
 
269
250
  let feeToken, feeTokenInfo
@@ -287,7 +268,7 @@ async function sendMessage(
287
268
  feeTokenInfo = await source.getTokenInfo(nativeToken)
288
269
  }
289
270
 
290
- const message: AnyMessage = {
271
+ const message: MessageInput = {
291
272
  receiver,
292
273
  data,
293
274
  extraArgs: extraArgs as ExtraArgs,
@@ -296,7 +277,11 @@ async function sendMessage(
296
277
  }
297
278
 
298
279
  // calculate fee
299
- const fee = await source.getFee(argv.router, destNetwork.chainSelector, message)
280
+ const fee = await source.getFee({
281
+ ...argv,
282
+ destChainSelector: destNetwork.chainSelector,
283
+ message,
284
+ })
300
285
 
301
286
  logger.info(
302
287
  'Fee:',
@@ -310,17 +295,15 @@ async function sendMessage(
310
295
  if (argv.onlyGetFee) return
311
296
 
312
297
  if (!walletAddress) [walletAddress, wallet] = await loadChainWallet(source, argv)
313
- const request = await source.sendMessage(
314
- argv.router,
315
- destNetwork.chainSelector,
316
- { ...message, fee },
317
- { ...argv, wallet },
318
- )
298
+ const request = await source.sendMessage({
299
+ ...argv,
300
+ destChainSelector: destNetwork.chainSelector,
301
+ message: { ...message, fee },
302
+ wallet,
303
+ })
319
304
  logger.info(
320
305
  '🚀 Sending message to',
321
- tokenReceiver && tokenReceiver !== '11111111111111111111111111111111'
322
- ? tokenReceiver
323
- : receiver,
306
+ receiver,
324
307
  '@',
325
308
  destNetwork.name,
326
309
  ', tx =>',
@@ -328,7 +311,6 @@ async function sendMessage(
328
311
  ', messageId =>',
329
312
  request.message.messageId,
330
313
  )
331
-
332
314
  await showRequests(ctx, {
333
315
  ...argv,
334
316
  txHash: request.tx.hash,
@@ -3,6 +3,8 @@ import {
3
3
  type ChainTransaction,
4
4
  CCIPExecTxRevertedError,
5
5
  CCIPNotImplementedError,
6
+ ExecutionState,
7
+ MessageStatus,
6
8
  bigIntReplacer,
7
9
  discoverOffRamp,
8
10
  isSupportedTxHash,
@@ -82,18 +84,18 @@ export async function showRequests(ctx: Ctx, argv: Parameters<typeof handler>[0]
82
84
  getChain = fetchChainsFromRpcs(ctx, argv)
83
85
  let idFromSource, onRamp
84
86
  if (argv.idFromSource.includes('@')) {
85
- ;[onRamp, idFromSource] = argv.idFromSource.split('@')
87
+ ;[onRamp, idFromSource] = argv.idFromSource.split('@') as [string, string]
86
88
  } else idFromSource = argv.idFromSource
87
89
  const sourceNetwork = networkInfo(idFromSource)
88
90
  source = await getChain(sourceNetwork.chainId)
89
- if (!source.fetchRequestById)
90
- throw new CCIPNotImplementedError(`fetchRequestById for ${source.constructor.name}`)
91
- request = await source.fetchRequestById(argv.txHash, onRamp, argv)
91
+ if (!source.getMessageById)
92
+ throw new CCIPNotImplementedError(`getMessageById for ${source.constructor.name}`)
93
+ request = await source.getMessageById(argv.txHash, onRamp, argv)
92
94
  } else {
93
95
  const [getChain_, tx$] = fetchChainsFromRpcs(ctx, argv, argv.txHash)
94
96
  getChain = getChain_
95
97
  ;[source, tx] = await tx$
96
- request = await selectRequest(await source.fetchRequestsInTx(tx), 'to know more', argv)
98
+ request = await selectRequest(await source.getMessagesInTx(tx), 'to know more', argv)
97
99
  }
98
100
 
99
101
  switch (argv.format) {
@@ -118,17 +120,16 @@ export async function showRequests(ctx: Ctx, argv: Parameters<typeof handler>[0]
118
120
  let cancelWaitFinalized: (() => void) | undefined
119
121
  const finalized$ = (async () => {
120
122
  if (argv.wait) {
121
- logger.info('Waiting for finalization...')
122
- await source.waitFinalized(
123
+ logger.info(`[${MessageStatus.Sent}] Waiting for source chain finalization...`)
124
+ await source.waitFinalized({
123
125
  request,
124
- undefined,
125
- new Promise<void>((resolve) => (cancelWaitFinalized = resolve)),
126
- )
127
- logger.info(`Transaction "${request.log.transactionHash}" finalized ✅`)
126
+ cancel$: new Promise<void>((resolve) => (cancelWaitFinalized = resolve)),
127
+ })
128
+ logger.info(`[${MessageStatus.SourceFinalized}] Source chain finalized`)
128
129
  }
129
130
 
130
- const offchainTokenData = await source.fetchOffchainTokenData(request)
131
- if (offchainTokenData?.length && offchainTokenData.some((d) => !!d)) {
131
+ const offchainTokenData = await source.getOffchainTokenData(request)
132
+ if (offchainTokenData.length && offchainTokenData.some((d) => !!d)) {
132
133
  switch (argv.format) {
133
134
  case Format.log: {
134
135
  logger.log('attestations =', offchainTokenData)
@@ -147,7 +148,8 @@ export async function showRequests(ctx: Ctx, argv: Parameters<typeof handler>[0]
147
148
  }
148
149
  }
149
150
 
150
- if (argv.wait) logger.info('Waiting for Commit (dest)...')
151
+ if (argv.wait)
152
+ logger.info(`[${MessageStatus.SourceFinalized}] Waiting for commit on destination chain...`)
151
153
  else logger.info('Commit (dest):')
152
154
  })()
153
155
 
@@ -157,13 +159,16 @@ export async function showRequests(ctx: Ctx, argv: Parameters<typeof handler>[0]
157
159
 
158
160
  let cancelWaitCommit: (() => void) | undefined
159
161
  const commit$ = (async () => {
160
- const commit = await dest.fetchCommitReport(commitStore, request, {
162
+ const commit = await dest.getCommitReport({
163
+ commitStore,
164
+ request,
161
165
  ...argv,
162
166
  watch: argv.wait && new Promise<void>((resolve) => (cancelWaitCommit = resolve)),
163
167
  })
164
168
  cancelWaitFinalized?.()
165
- if (!commit) return
166
169
  await finalized$
170
+ if (argv.wait)
171
+ logger.info(`[${MessageStatus.Committed}] Commit report accepted on destination chain`)
167
172
  switch (argv.format) {
168
173
  case Format.log:
169
174
  logger.log('commit =', commit)
@@ -175,20 +180,33 @@ export async function showRequests(ctx: Ctx, argv: Parameters<typeof handler>[0]
175
180
  logger.info(JSON.stringify(commit, bigIntReplacer, 2))
176
181
  break
177
182
  }
178
- if (argv.wait) logger.info('Waiting for Receipt (dest):')
183
+ if (argv.wait)
184
+ logger.info(`[${MessageStatus.Blessed}] Waiting for execution on destination chain...`)
179
185
  else logger.info('Receipts (dest):')
180
186
  return commit
181
187
  })()
182
188
 
183
189
  let found = false
184
- for await (const receipt of dest.fetchExecutionReceipts(
190
+ for await (const receipt of dest.getExecutionReceipts({
191
+ ...argv,
185
192
  offRamp,
186
- request,
187
- !argv.wait ? await commit$ : undefined,
188
- { ...argv, watch: argv.wait && ctx.destroy$ },
189
- )) {
193
+ messageId: request.message.messageId,
194
+ sourceChainSelector: request.message.sourceChainSelector,
195
+ startTime: request.tx.timestamp,
196
+ commit: !argv.wait ? await commit$ : undefined,
197
+ watch: argv.wait && ctx.destroy$,
198
+ })) {
190
199
  cancelWaitCommit?.()
191
200
  await commit$
201
+ const status =
202
+ receipt.receipt.state === ExecutionState.Success
203
+ ? MessageStatus.Success
204
+ : MessageStatus.Failed
205
+ const statusMessage =
206
+ receipt.receipt.state === ExecutionState.Success
207
+ ? 'Message executed on destination chain'
208
+ : 'Message execution failed on destination chain'
209
+ logger.info(`[${status}] ${statusMessage}`)
192
210
  switch (argv.format) {
193
211
  case Format.log:
194
212
  logger.log('receipt =', withDateTimestamp(receipt))
@@ -11,6 +11,7 @@ import {
11
11
  CCIPError,
12
12
  CCIPErrorCode,
13
13
  ExecutionState,
14
+ getCCIPExplorerUrl,
14
15
  networkInfo,
15
16
  supportedChains,
16
17
  } from '@chainlink/ccip-sdk/src/index.ts'
@@ -42,7 +43,7 @@ export async function selectRequest(
42
43
  hints?: { logIndex?: number },
43
44
  ): Promise<CCIPRequest> {
44
45
  if (hints?.logIndex != null) requests = requests.filter((req) => req.log.index === hints.logIndex)
45
- if (requests.length === 1) return requests[0]
46
+ if (requests.length === 1) return requests[0]!
46
47
  const answer = await select({
47
48
  message: `${requests.length} messageIds found; select one${promptSuffix ? ' ' + promptSuffix : ''}`,
48
49
  choices: [
@@ -66,7 +67,7 @@ tokenTransfers =\t[${req.message.tokenAmounts.map((ta) => ('token' in ta ? ta.to
66
67
  ],
67
68
  })
68
69
  if (answer < 0) throw new CCIPError(CCIPErrorCode.UNKNOWN, 'User requested exit')
69
- return requests[answer]
70
+ return requests[answer]!
70
71
  }
71
72
 
72
73
  /**
@@ -142,7 +143,7 @@ async function formatToken(
142
143
  * @returns Record with indexed keys.
143
144
  */
144
145
  export function formatArray<T>(name: string, values: readonly T[]): Record<string, T> {
145
- if (values.length <= 1) return { [name]: values[0] }
146
+ if (values.length <= 1) return { [name]: values[0]! }
146
147
  return Object.fromEntries(values.map((v, i) => [`${name}[${i}]`, v] as const))
147
148
  }
148
149
 
@@ -296,6 +297,7 @@ export async function prettyRequest(this: Ctx, source: Chain, request: CCIPReque
296
297
  ...('accounts' in request.message ? formatArray('accounts', request.message.accounts) : {}),
297
298
  ...rest,
298
299
  })
300
+ this.logger.info('CCIP Explorer:', getCCIPExplorerUrl('msg', request.message.messageId))
299
301
  }
300
302
 
301
303
  /**
@@ -521,7 +523,7 @@ export function logParsedError(this: Ctx, err: unknown): boolean {
521
523
  export async function parseTokenAmounts(source: Chain, transferTokens: readonly string[]) {
522
524
  return Promise.all(
523
525
  transferTokens.map(async (tokenAmount) => {
524
- const [token, amount_] = tokenAmount.split('=')
526
+ const [token, amount_] = tokenAmount.split('=') as [string, string]
525
527
  const { decimals } = await source.getTokenInfo(token)
526
528
  const amount = parseUnits(amount_, decimals)
527
529
  return { token, amount }
package/src/index.ts CHANGED
@@ -11,7 +11,7 @@ import { Format } from './commands/index.ts'
11
11
  util.inspect.defaultOptions.depth = 6 // print down to tokenAmounts in requests
12
12
  // generate:nofail
13
13
  // `const VERSION = '${require('./package.json').version}-${require('child_process').execSync('git rev-parse --short HEAD').toString().trim()}'`
14
- const VERSION = '0.92.1-22759cb'
14
+ const VERSION = '0.94.0-aac4ae6'
15
15
  // generate:end
16
16
 
17
17
  const globalOpts = {
@@ -41,7 +41,11 @@ const globalOpts = {
41
41
  page: {
42
42
  type: 'number',
43
43
  describe: 'getLogs page/range size',
44
- default: 10_000,
44
+ },
45
+ 'no-api': {
46
+ type: 'boolean',
47
+ describe: 'Disable CCIP API integration (full decentralization mode)',
48
+ default: false,
45
49
  },
46
50
  } as const
47
51
 
@@ -66,12 +70,12 @@ async function main() {
66
70
  }
67
71
 
68
72
  function wasCalledAsScript() {
69
- const realPath = realpathSync(process.argv[1])
73
+ const realPath = realpathSync(process.argv[1]!)
70
74
  const realPathAsUrl = pathToFileURL(realPath).href
71
75
  return import.meta.url === realPathAsUrl
72
76
  }
73
77
 
74
- if (import.meta?.main || wasCalledAsScript()) {
78
+ if (import.meta.main || wasCalledAsScript()) {
75
79
  const later = setTimeout(() => {}, 2 ** 31 - 1) // keep event-loop alive
76
80
  await main()
77
81
  .catch((err) => {
@@ -99,7 +99,7 @@ export class AptosLedgerSigner /*implements AptosAsyncAccount*/ {
99
99
  */
100
100
  export async function loadAptosWallet({ wallet: walletOpt }: { wallet?: unknown }) {
101
101
  if (typeof walletOpt !== 'string') throw new CCIPArgumentInvalidError('wallet', String(walletOpt))
102
- if ((walletOpt ?? '').startsWith('ledger')) {
102
+ if (walletOpt.startsWith('ledger')) {
103
103
  let derivationPath = walletOpt.split(':')[1]
104
104
  if (!derivationPath) derivationPath = "m/44'/637'/0'/0'/0'"
105
105
  else if (!isNaN(Number(derivationPath))) derivationPath = `m/44'/637'/${derivationPath}'/0'/0'`
@@ -43,7 +43,7 @@ export async function loadEvmWallet(
43
43
  )
44
44
  }
45
45
  if (typeof walletOpt !== 'string') throw new CCIPArgumentInvalidError('wallet', String(walletOpt))
46
- if ((walletOpt ?? '').startsWith('ledger')) {
46
+ if (walletOpt.startsWith('ledger')) {
47
47
  let derivationPath = walletOpt.split(':')[1]
48
48
  if (derivationPath && !isNaN(Number(derivationPath)))
49
49
  derivationPath = `m/44'/60'/${derivationPath}'/0/0`
@@ -6,6 +6,7 @@ import {
6
6
  type ChainGetter,
7
7
  type ChainTransaction,
8
8
  type EVMChain,
9
+ type TONChain,
9
10
  CCIPChainFamilyUnsupportedError,
10
11
  CCIPNetworkFamilyUnsupportedError,
11
12
  CCIPRpcNotFoundError,
@@ -53,11 +54,11 @@ async function collectEndpoints(
53
54
 
54
55
  export function fetchChainsFromRpcs(
55
56
  ctx: Ctx,
56
- argv: { rpcs?: string[]; rpcsFile?: string },
57
+ argv: { rpcs?: string[]; rpcsFile?: string; noApi?: boolean },
57
58
  ): ChainGetter
58
59
  export function fetchChainsFromRpcs(
59
60
  ctx: Ctx,
60
- argv: { rpcs?: string[]; rpcsFile?: string },
61
+ argv: { rpcs?: string[]; rpcsFile?: string; noApi?: boolean },
61
62
  txHash: string,
62
63
  ): [ChainGetter, Promise<[Chain, ChainTransaction]>]
63
64
 
@@ -66,13 +67,13 @@ export function fetchChainsFromRpcs(
66
67
  * If txHash is provided, fetches matching families first and returns [chainGetter, txPromise];
67
68
  * Otherwise, spawns racing URLs for each family asked by `getChain` getter
68
69
  * @param ctx - Context object containing destroy$ promise and logger properties
69
- * @param argv - Options containing rpcs (list) and/or rpcs file
70
+ * @param argv - Options containing rpcs (list), rpcs file and noApi flag
70
71
  * @param txHash - Optional txHash to fetch concurrently; causes the function to return a [ChainGetter, Promise<ChainTransaction>]
71
72
  * @returns a ChainGetter (if txHash was provided), or a tuple of [ChainGetter, Promise<ChainTransaction>]
72
73
  */
73
74
  export function fetchChainsFromRpcs(
74
75
  ctx: Ctx,
75
- argv: { rpcs?: string[]; rpcsFile?: string },
76
+ argv: { rpcs?: string[]; rpcsFile?: string; noApi?: boolean },
76
77
  txHash?: string,
77
78
  ) {
78
79
  const chains: Record<string, Promise<Chain>> = {}
@@ -82,7 +83,7 @@ export function fetchChainsFromRpcs(
82
83
  > = {}
83
84
  const finished: Partial<Record<ChainFamily, boolean>> = {}
84
85
  const initFamily$: Partial<Record<ChainFamily, Promise<unknown>>> = {}
85
- let endpoints$: Promise<Set<string>>
86
+ let endpoints$: Promise<Set<string>> | undefined
86
87
 
87
88
  let txResolve: (value: [Chain, ChainTransaction]) => void, txReject: (reason?: unknown) => void
88
89
  const txResult = new Promise<[Chain, ChainTransaction]>((resolve, reject) => {
@@ -100,7 +101,10 @@ export function fetchChainsFromRpcs(
100
101
  const txs$: Promise<unknown>[] = []
101
102
  let txFound = false
102
103
  for (const url of endpoints) {
103
- const chain$ = C.fromUrl(url, ctx)
104
+ const chain$ = C.fromUrl(url, {
105
+ ...ctx,
106
+ apiClient: argv.noApi ? null : undefined,
107
+ })
104
108
  chains$.push(chain$)
105
109
 
106
110
  void chain$.then(
@@ -116,7 +120,7 @@ export function fetchChainsFromRpcs(
116
120
  chains[chain.network.name] = Promise.resolve(chain)
117
121
  } else if (chain.network.name in chainsCbs) {
118
122
  // chain detected, and there's a "pending request" by getChain: resolve
119
- const [resolve] = chainsCbs[chain.network.name]
123
+ const [resolve] = chainsCbs[chain.network.name]!
120
124
  resolve(chain)
121
125
  }
122
126
  return chain
@@ -152,16 +156,16 @@ export function fetchChainsFromRpcs(
152
156
 
153
157
  const chainGetter = async (idOrSelectorOrName: number | string | bigint): Promise<Chain> => {
154
158
  const network = networkInfo(idOrSelectorOrName)
155
- if (network.name in chains) return chains[network.name]
159
+ if (network.name in chains) return chains[network.name]!
156
160
  if (finished[network.family]) throw new CCIPRpcNotFoundError(network.name)
157
- chains[network.name] = new Promise((resolve, reject) => {
161
+ const c = (chains[network.name] = new Promise((resolve, reject) => {
158
162
  chainsCbs[network.name] = [resolve, reject]
159
- })
160
- void chains[network.name].finally(() => {
163
+ }))
164
+ void c.finally(() => {
161
165
  delete chainsCbs[network.name] // when chain is settled, delete the callbacks
162
166
  })
163
167
  void loadChainFamily(network.family)
164
- return chains[network.name]
168
+ return c
165
169
  }
166
170
 
167
171
  if (!txHash) return chainGetter
@@ -210,8 +214,8 @@ export async function loadChainWallet(chain: Chain, argv: { wallet?: unknown; rp
210
214
  wallet = loadSuiWallet(argv)
211
215
  return [wallet.toSuiAddress(), wallet] as const
212
216
  case ChainFamily.TON:
213
- wallet = await loadTonWallet(argv)
214
- return [wallet.contract.address.toString(), wallet] as const
217
+ wallet = await loadTonWallet((chain as TONChain).provider, argv, chain.network.isTestnet)
218
+ return [wallet.getAddress(), wallet] as const
215
219
  default:
216
220
  // TypeScript exhaustiveness check - this should never be reached
217
221
  throw new CCIPChainFamilyUnsupportedError((chain.network as { family: string }).family)