@chainlink/ccip-cli 0.96.0 → 0.97.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/lane-latency.d.ts +17 -0
- package/dist/commands/lane-latency.d.ts.map +1 -1
- package/dist/commands/lane-latency.js +18 -1
- package/dist/commands/lane-latency.js.map +1 -1
- package/dist/commands/manual-exec.d.ts +20 -0
- package/dist/commands/manual-exec.d.ts.map +1 -1
- package/dist/commands/manual-exec.js +34 -18
- package/dist/commands/manual-exec.js.map +1 -1
- package/dist/commands/parse.d.ts +17 -0
- package/dist/commands/parse.d.ts.map +1 -1
- package/dist/commands/parse.js +17 -0
- package/dist/commands/parse.js.map +1 -1
- package/dist/commands/send.d.ts +20 -0
- package/dist/commands/send.d.ts.map +1 -1
- package/dist/commands/send.js +22 -4
- package/dist/commands/send.js.map +1 -1
- package/dist/commands/show.d.ts +22 -6
- package/dist/commands/show.d.ts.map +1 -1
- package/dist/commands/show.js +77 -43
- package/dist/commands/show.js.map +1 -1
- package/dist/commands/utils.d.ts +7 -7
- package/dist/commands/utils.d.ts.map +1 -1
- package/dist/commands/utils.js +89 -43
- package/dist/commands/utils.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/loadtest.d.ts +2 -0
- package/dist/loadtest.d.ts.map +1 -0
- package/dist/loadtest.js +132 -0
- package/dist/loadtest.js.map +1 -0
- package/package.json +8 -8
- package/src/commands/lane-latency.ts +19 -1
- package/src/commands/manual-exec.ts +35 -27
- package/src/commands/parse.ts +18 -0
- package/src/commands/send.ts +23 -4
- package/src/commands/show.ts +85 -44
- package/src/commands/utils.ts +105 -43
- package/src/index.ts +1 -1
- package/src/loadtest.ts +171 -0
package/src/commands/utils.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Console } from 'node:console'
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
-
type CCIPCommit,
|
|
5
4
|
type CCIPExecution,
|
|
6
5
|
type CCIPRequest,
|
|
6
|
+
type CCIPVerifications,
|
|
7
7
|
type Chain,
|
|
8
8
|
type ChainFamily,
|
|
9
9
|
type ChainStatic,
|
|
@@ -18,7 +18,6 @@ import {
|
|
|
18
18
|
} from '@chainlink/ccip-sdk/src/index.ts'
|
|
19
19
|
import { select } from '@inquirer/prompts'
|
|
20
20
|
import {
|
|
21
|
-
dataLength,
|
|
22
21
|
formatUnits,
|
|
23
22
|
hexlify,
|
|
24
23
|
isBytesLike,
|
|
@@ -125,14 +124,17 @@ export function formatDisplayTxHash(hash: string, family: ChainFamily): string {
|
|
|
125
124
|
}
|
|
126
125
|
|
|
127
126
|
async function formatToken(
|
|
128
|
-
source: Chain,
|
|
129
|
-
ta: { amount: bigint } & (
|
|
127
|
+
source: Chain | undefined,
|
|
128
|
+
ta: { amount: bigint } & (
|
|
129
|
+
| { token: string }
|
|
130
|
+
| { sourceTokenAddress?: string; sourcePoolAddress: string }
|
|
131
|
+
),
|
|
130
132
|
): Promise<string> {
|
|
133
|
+
if (!source) return `${ta.amount} ${'sourcePoolAddress' in ta ? ta.sourcePoolAddress : ta.token}`
|
|
131
134
|
let token
|
|
132
135
|
if ('token' in ta) token = ta.token
|
|
133
|
-
else
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
+
else if (ta.sourceTokenAddress) token = ta.sourceTokenAddress
|
|
137
|
+
else token = await source.getTokenForTokenPool(ta.sourcePoolAddress)
|
|
136
138
|
const { symbol, decimals } = await source.getTokenInfo(token)
|
|
137
139
|
return `${formatUnits(ta.amount, decimals)} ${symbol}`
|
|
138
140
|
}
|
|
@@ -143,21 +145,37 @@ async function formatToken(
|
|
|
143
145
|
* @param values - Array values to format.
|
|
144
146
|
* @returns Record with indexed keys.
|
|
145
147
|
*/
|
|
146
|
-
export function formatArray<T>(
|
|
148
|
+
export function formatArray<T>(
|
|
149
|
+
name: string,
|
|
150
|
+
values: readonly T[],
|
|
151
|
+
edges = '[]',
|
|
152
|
+
): Record<string, T> {
|
|
147
153
|
if (values.length <= 1) return { [name]: values[0]! }
|
|
148
|
-
return Object.fromEntries(values.map((v, i) => [`${name}[${i}]`, v] as const))
|
|
154
|
+
return Object.fromEntries(values.map((v, i) => [`${name}${edges[0]}${i}${edges[1]}`, v] as const))
|
|
149
155
|
}
|
|
150
156
|
|
|
151
157
|
// join truthy property names, separated by a dot
|
|
152
|
-
function j(...args: string[]): string {
|
|
153
|
-
return args.
|
|
158
|
+
function j(...args: (string | number)[]): string {
|
|
159
|
+
return args.reduce(
|
|
160
|
+
(acc: string, v): string =>
|
|
161
|
+
v === ''
|
|
162
|
+
? acc
|
|
163
|
+
: acc
|
|
164
|
+
? acc + (typeof v === 'number' ? `[${v}]` : (v.match(/^\w/) ? '.' : '') + v)
|
|
165
|
+
: v.toString(),
|
|
166
|
+
'',
|
|
167
|
+
)
|
|
154
168
|
}
|
|
155
169
|
|
|
156
170
|
function formatData(name: string, data: string, parseError = false): Record<string, string> {
|
|
157
171
|
if (parseError) {
|
|
158
172
|
let parsed
|
|
159
173
|
for (const chain of Object.values(supportedChains)) {
|
|
160
|
-
|
|
174
|
+
try {
|
|
175
|
+
parsed = chain.parse?.(data)
|
|
176
|
+
} catch {
|
|
177
|
+
// ignore
|
|
178
|
+
}
|
|
161
179
|
if (parsed) break
|
|
162
180
|
}
|
|
163
181
|
if (parsed) {
|
|
@@ -176,7 +194,7 @@ function formatData(name: string, data: string, parseError = false): Record<stri
|
|
|
176
194
|
for (let i = data.length; i > 2; i -= 64) {
|
|
177
195
|
split.unshift(data.substring(Math.max(i - 64, 0), i))
|
|
178
196
|
}
|
|
179
|
-
return formatArray(name, split)
|
|
197
|
+
return formatArray(name, split, '{}')
|
|
180
198
|
}
|
|
181
199
|
|
|
182
200
|
function formatDate(timestamp: number) {
|
|
@@ -238,20 +256,21 @@ function formatDataString(data: string): Record<string, string> {
|
|
|
238
256
|
|
|
239
257
|
/**
|
|
240
258
|
* Prints a CCIP request in a human-readable format.
|
|
241
|
-
* @param source - Source chain instance.
|
|
242
259
|
* @param request - CCIP request to print.
|
|
260
|
+
* @param source - Source chain instance.
|
|
243
261
|
*/
|
|
244
|
-
export async function prettyRequest(this: Ctx,
|
|
262
|
+
export async function prettyRequest(this: Ctx, request: CCIPRequest, source?: Chain) {
|
|
245
263
|
prettyLane.call(this, request.lane)
|
|
246
264
|
this.logger.info('Request (source):')
|
|
247
265
|
|
|
248
266
|
let finalized
|
|
249
267
|
try {
|
|
250
|
-
finalized = await source.getBlockTimestamp('finalized')
|
|
268
|
+
if (source) finalized = await source.getBlockTimestamp('finalized')
|
|
251
269
|
} catch (_) {
|
|
252
270
|
// no finalized tag support
|
|
253
271
|
}
|
|
254
|
-
|
|
272
|
+
let nonce
|
|
273
|
+
if ('nonce' in request.message) nonce = Number(request.message.nonce)
|
|
255
274
|
|
|
256
275
|
const sourceFamily = networkInfo(request.lane.sourceChainSelector).family
|
|
257
276
|
const destFamily = networkInfo(request.lane.destChainSelector).family
|
|
@@ -280,6 +299,8 @@ export async function prettyRequest(this: Ctx, source: Chain, request: CCIPReque
|
|
|
280
299
|
'destChainSelector',
|
|
281
300
|
'extraArgs',
|
|
282
301
|
'accounts',
|
|
302
|
+
'receipts',
|
|
303
|
+
'encodedMessage',
|
|
283
304
|
)
|
|
284
305
|
prettyTable.call(this, {
|
|
285
306
|
messageId: request.message.messageId,
|
|
@@ -287,7 +308,7 @@ export async function prettyRequest(this: Ctx, source: Chain, request: CCIPReque
|
|
|
287
308
|
sender: displaySender,
|
|
288
309
|
receiver: displayReceiver,
|
|
289
310
|
sequenceNumber: Number(request.message.sequenceNumber),
|
|
290
|
-
nonce: nonce === 0 ? '0 => allow out-of-order exec' : nonce,
|
|
311
|
+
...(nonce != null && { nonce: nonce === 0 ? '0 => allow out-of-order exec' : nonce }),
|
|
291
312
|
...('gasLimit' in request.message
|
|
292
313
|
? { gasLimit: Number(request.message.gasLimit) }
|
|
293
314
|
: 'computeUnits' in request.message
|
|
@@ -312,37 +333,53 @@ export async function prettyRequest(this: Ctx, source: Chain, request: CCIPReque
|
|
|
312
333
|
),
|
|
313
334
|
...formatDataString(request.message.data),
|
|
314
335
|
...('accounts' in request.message ? formatArray('accounts', request.message.accounts) : {}),
|
|
336
|
+
...('receipts' in request.message ? formatArray('receipts', request.message.receipts) : {}),
|
|
315
337
|
...rest,
|
|
316
338
|
})
|
|
317
339
|
this.logger.info('CCIP Explorer:', getCCIPExplorerUrl('msg', request.message.messageId))
|
|
318
340
|
}
|
|
319
341
|
|
|
320
342
|
/**
|
|
321
|
-
* Prints
|
|
343
|
+
* Prints CCIP Verifications in a human-readable format.
|
|
322
344
|
* @param dest - Destination chain instance.
|
|
323
|
-
* @param
|
|
345
|
+
* @param verifications - CCIP verifications to print.
|
|
324
346
|
* @param request - CCIP request for timestamp comparison.
|
|
325
347
|
*/
|
|
326
|
-
export async function
|
|
348
|
+
export async function prettyVerifications(
|
|
327
349
|
this: Ctx,
|
|
328
350
|
dest: Chain,
|
|
329
|
-
|
|
351
|
+
verifications: CCIPVerifications,
|
|
330
352
|
request: PickDeep<CCIPRequest, 'tx.timestamp' | 'lane.destChainSelector'>,
|
|
331
353
|
) {
|
|
332
|
-
const timestamp = await dest.getBlockTimestamp(commit.log.blockNumber)
|
|
333
354
|
const destFamily = networkInfo(request.lane.destChainSelector).family
|
|
334
|
-
const origin = commit.log.tx?.from ?? (await dest.getTransaction(commit.log.transactionHash)).from
|
|
335
355
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
356
|
+
if ('report' in verifications) {
|
|
357
|
+
const timestamp = await dest.getBlockTimestamp(verifications.log.blockNumber)
|
|
358
|
+
const origin =
|
|
359
|
+
verifications.log.tx?.from ??
|
|
360
|
+
(await dest.getTransaction(verifications.log.transactionHash)).from
|
|
361
|
+
prettyTable.call(this, {
|
|
362
|
+
merkleRoot: verifications.report.merkleRoot,
|
|
363
|
+
min: Number(verifications.report.minSeqNr),
|
|
364
|
+
max: Number(verifications.report.maxSeqNr),
|
|
365
|
+
origin: formatDisplayAddress(origin, destFamily),
|
|
366
|
+
contract: formatDisplayAddress(verifications.log.address, destFamily),
|
|
367
|
+
transactionHash: formatDisplayTxHash(verifications.log.transactionHash, destFamily),
|
|
368
|
+
blockNumber: verifications.log.blockNumber,
|
|
369
|
+
timestamp: `${formatDate(timestamp)} (${formatDuration(timestamp - request.tx.timestamp)} after request)`,
|
|
370
|
+
})
|
|
371
|
+
} else {
|
|
372
|
+
let ts = 0
|
|
373
|
+
for (const { timestamp } of verifications.verifications)
|
|
374
|
+
if (timestamp && timestamp > ts) ts = timestamp
|
|
375
|
+
|
|
376
|
+
prettyTable.call(this, {
|
|
377
|
+
...verifications,
|
|
378
|
+
...(ts && {
|
|
379
|
+
timestamp: `${formatDate(ts)} (${formatDuration(ts - request.tx.timestamp)} after request)`,
|
|
380
|
+
}),
|
|
381
|
+
})
|
|
382
|
+
}
|
|
346
383
|
}
|
|
347
384
|
|
|
348
385
|
/**
|
|
@@ -401,6 +438,18 @@ function wrapText(text: string, maxWidth: number, threshold: number = 0.1): stri
|
|
|
401
438
|
return lines
|
|
402
439
|
}
|
|
403
440
|
|
|
441
|
+
function flatten(val: unknown, path: (string | number)[] = []): [(string | number)[], unknown][] {
|
|
442
|
+
if (Array.isArray(val)) {
|
|
443
|
+
if (val.length) return val.map((v: unknown, i: number) => flatten(v, [...path, i])).flat(1)
|
|
444
|
+
} else if (val && typeof val === 'object') {
|
|
445
|
+
if (Object.keys(val).length === 0) return [[path, val]]
|
|
446
|
+
return Object.entries(val)
|
|
447
|
+
.map(([k, v]) => flatten(v, [...path, k]))
|
|
448
|
+
.flat(1)
|
|
449
|
+
}
|
|
450
|
+
return [[path, val]]
|
|
451
|
+
}
|
|
452
|
+
|
|
404
453
|
/**
|
|
405
454
|
* Prints a formatted table of key-value pairs.
|
|
406
455
|
* @param args - Key-value pairs to print.
|
|
@@ -412,23 +461,29 @@ export function prettyTable(
|
|
|
412
461
|
opts = { parseErrorKeys: ['returnData'], spcount: 0 },
|
|
413
462
|
) {
|
|
414
463
|
const out: (readonly [string, unknown])[] = []
|
|
415
|
-
for (const [
|
|
464
|
+
for (const [path, value] of flatten(args)) {
|
|
465
|
+
const last = path[path.length - 1]
|
|
466
|
+
const key = j(...path)
|
|
416
467
|
if (isBytesLike(value)) {
|
|
417
468
|
let parseError
|
|
418
|
-
if (opts.parseErrorKeys.includes(
|
|
419
|
-
|
|
420
|
-
|
|
469
|
+
if (opts.parseErrorKeys.includes(path[path.length - 1]!.toString())) parseError = true
|
|
470
|
+
out.push(
|
|
471
|
+
...Object.entries(
|
|
472
|
+
formatData(key, typeof value !== 'string' ? hexlify(value) : value, parseError),
|
|
473
|
+
),
|
|
474
|
+
)
|
|
421
475
|
} else if (typeof value === 'string') {
|
|
422
476
|
out.push(
|
|
423
477
|
...wrapText(value, Math.max(100, +(process.env.COLUMNS || 80) * 0.9)).map(
|
|
424
478
|
(l, i) => [!i ? key : ' '.repeat(opts.spcount++), l] as const,
|
|
425
479
|
),
|
|
426
480
|
)
|
|
427
|
-
} else if (
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
481
|
+
} else if (
|
|
482
|
+
typeof last === 'string' &&
|
|
483
|
+
last.toLowerCase().includes('timestamp') &&
|
|
484
|
+
typeof value === 'number'
|
|
485
|
+
) {
|
|
486
|
+
out.push([key, formatDate(value)])
|
|
432
487
|
} else out.push([key, value])
|
|
433
488
|
}
|
|
434
489
|
return this.logger.table(Object.fromEntries(out))
|
|
@@ -477,6 +532,13 @@ export function formatCCIPError(err: unknown, verbose = false): string | null {
|
|
|
477
532
|
|
|
478
533
|
lines.push(`error[${err.code}]: ${err.message}`)
|
|
479
534
|
|
|
535
|
+
if (Object.keys(err.context).length > 0) {
|
|
536
|
+
lines.push(' context:')
|
|
537
|
+
for (const [key, value] of Object.entries(err.context)) {
|
|
538
|
+
lines.push(` ${key}: ${value as string}`)
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
480
542
|
if (err.recovery) {
|
|
481
543
|
lines.push(` help: ${err.recovery}`)
|
|
482
544
|
}
|
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.
|
|
14
|
+
const VERSION = '0.97.0-8811550'
|
|
15
15
|
// generate:end
|
|
16
16
|
|
|
17
17
|
const globalOpts = {
|
package/src/loadtest.ts
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { ChainFamily, networkInfo } from '@chainlink/ccip-sdk/src/index.ts'
|
|
2
|
+
import { formatUnits, toUtf8Bytes } from 'ethers'
|
|
3
|
+
|
|
4
|
+
import { getCtx } from './commands/utils.ts'
|
|
5
|
+
import { fetchChainsFromRpcs, loadChainWallet } from './providers/index.ts'
|
|
6
|
+
|
|
7
|
+
const DEST = networkInfo('ethereum-testnet-sepolia-base-1')
|
|
8
|
+
const RECEIVER = '0x'
|
|
9
|
+
const RPCS_FILE = '../../.env'
|
|
10
|
+
const RPCS = [
|
|
11
|
+
'https://ethereum-sepolia-rpc.publicnode.com',
|
|
12
|
+
'https://avalanche-fuji-c-chain-rpc.publicnode.com',
|
|
13
|
+
]
|
|
14
|
+
const PRIVATE_KEYS = {
|
|
15
|
+
[ChainFamily.Solana]: process.env['PRIVATE_KEY_SOLANA'],
|
|
16
|
+
[ChainFamily.Aptos]: process.env['PRIVATE_KEY_APTOS'],
|
|
17
|
+
[ChainFamily.EVM]: process.env['PRIVATE_KEY'],
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// mapping of source_network_name to its router
|
|
21
|
+
const SOURCES: Record<string, string> = {
|
|
22
|
+
'avalanche-testnet-fuji': '0xF694E193200268f9a4868e4Aa017A0118C9a8177',
|
|
23
|
+
}
|
|
24
|
+
// per source:
|
|
25
|
+
const MAX_COUNT = 100
|
|
26
|
+
const MAX_INFLIGHT = 10 // inflight on RPC (tx to be accepted/included), not on CCIP
|
|
27
|
+
const MAX_PER_SECOND = 1
|
|
28
|
+
|
|
29
|
+
/** like Promise.all, but receives Promise factories and spawn a maximum number of them in parallel */
|
|
30
|
+
function promiseAllMax<T>(
|
|
31
|
+
promises: readonly (() => Promise<T>)[],
|
|
32
|
+
maxParallelJobs: number,
|
|
33
|
+
cancel?: Promise<unknown>,
|
|
34
|
+
): Promise<T[]> {
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
const results = new Array(promises.length) as T[]
|
|
37
|
+
let completed = 0
|
|
38
|
+
let started = 0
|
|
39
|
+
let rejected = false
|
|
40
|
+
|
|
41
|
+
if (promises.length === 0) {
|
|
42
|
+
resolve([])
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const startNext = () => {
|
|
47
|
+
if (rejected || started >= promises.length) return
|
|
48
|
+
|
|
49
|
+
const index = started++
|
|
50
|
+
const promiseFactory = promises[index]!
|
|
51
|
+
|
|
52
|
+
promiseFactory()
|
|
53
|
+
.then((result) => {
|
|
54
|
+
if (rejected) return
|
|
55
|
+
results[index] = result
|
|
56
|
+
completed++
|
|
57
|
+
|
|
58
|
+
if (completed === promises.length) {
|
|
59
|
+
resolve(results)
|
|
60
|
+
} else {
|
|
61
|
+
startNext()
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
.catch((err) => {
|
|
65
|
+
rejected = true
|
|
66
|
+
reject(err as Error)
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Handle cancellation
|
|
71
|
+
void cancel?.then(() => {
|
|
72
|
+
if (!rejected && completed < promises.length) {
|
|
73
|
+
rejected = true
|
|
74
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
75
|
+
reject(new Error('Cancelled'))
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
// Start up to maxParallelJobs promises
|
|
80
|
+
for (let i = 0; i < maxParallelJobs && i < promises.length; i++) {
|
|
81
|
+
startNext()
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function main() {
|
|
87
|
+
const [ctx] = getCtx({ verbose: !!process.env['CCIP_VERBOSE'] })
|
|
88
|
+
const { logger } = ctx
|
|
89
|
+
const getChain = fetchChainsFromRpcs(ctx, {
|
|
90
|
+
noApi: true,
|
|
91
|
+
rpcsFile: RPCS_FILE,
|
|
92
|
+
rpcs: RPCS,
|
|
93
|
+
})
|
|
94
|
+
const allLanes = []
|
|
95
|
+
for (const [name, router] of Object.entries(SOURCES)) {
|
|
96
|
+
const source = await getChain(name)
|
|
97
|
+
const [walletAddr, wallet] = await loadChainWallet(source, {
|
|
98
|
+
wallet: PRIVATE_KEYS[source.network.family as keyof typeof PRIVATE_KEYS],
|
|
99
|
+
})
|
|
100
|
+
const initialBalance = await source.getBalance({ holder: walletAddr })
|
|
101
|
+
const nativeToken = await source.getNativeTokenForRouter(router)
|
|
102
|
+
const nativeInfo = await source.getTokenInfo(nativeToken)
|
|
103
|
+
const symbol = nativeInfo.symbol.startsWith('W')
|
|
104
|
+
? nativeInfo.symbol.substring(1)
|
|
105
|
+
: nativeInfo.symbol
|
|
106
|
+
logger.info(
|
|
107
|
+
`Initial balance of ${walletAddr} @ ${name}:`,
|
|
108
|
+
initialBalance,
|
|
109
|
+
'=',
|
|
110
|
+
formatUnits(initialBalance, nativeInfo.decimals),
|
|
111
|
+
symbol,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
const startTime = performance.now()
|
|
115
|
+
let inflight = 0,
|
|
116
|
+
completed = 0
|
|
117
|
+
const tasks = Array.from({ length: MAX_COUNT }, (_, i) => async () => {
|
|
118
|
+
const deltaMs = performance.now() - startTime
|
|
119
|
+
const delay = (1e3 * completed) / MAX_PER_SECOND - deltaMs
|
|
120
|
+
if (delay > 0) await new Promise((resolve) => setTimeout(resolve, delay))
|
|
121
|
+
|
|
122
|
+
inflight++
|
|
123
|
+
const req = await source.sendMessage({
|
|
124
|
+
router,
|
|
125
|
+
destChainSelector: DEST.chainSelector,
|
|
126
|
+
message: {
|
|
127
|
+
receiver: RECEIVER,
|
|
128
|
+
data: toUtf8Bytes(`ccip-cli load test: ${i + 1}/${MAX_COUNT}`),
|
|
129
|
+
extraArgs: { gasLimit: 0n },
|
|
130
|
+
},
|
|
131
|
+
wallet,
|
|
132
|
+
})
|
|
133
|
+
inflight--
|
|
134
|
+
completed++
|
|
135
|
+
logger.info(`[${i + 1}] LOAD TEST`, name, '=>', DEST.name, {
|
|
136
|
+
inflight,
|
|
137
|
+
completed,
|
|
138
|
+
total: MAX_COUNT,
|
|
139
|
+
messageId: req.message.messageId,
|
|
140
|
+
tx: req.log.transactionHash,
|
|
141
|
+
})
|
|
142
|
+
})
|
|
143
|
+
allLanes.push(
|
|
144
|
+
promiseAllMax(tasks, MAX_INFLIGHT).then(async () => {
|
|
145
|
+
const finalBalance = await source.getBalance({ holder: walletAddr })
|
|
146
|
+
logger.info(
|
|
147
|
+
`Final balance of ${walletAddr} @ ${name}:`,
|
|
148
|
+
finalBalance,
|
|
149
|
+
'=',
|
|
150
|
+
formatUnits(finalBalance, nativeInfo.decimals),
|
|
151
|
+
symbol,
|
|
152
|
+
', spent =',
|
|
153
|
+
formatUnits(initialBalance - finalBalance, nativeInfo.decimals),
|
|
154
|
+
)
|
|
155
|
+
const delta = (performance.now() - startTime) / 1e3
|
|
156
|
+
logger.warn(
|
|
157
|
+
`[${name}] Sent`,
|
|
158
|
+
completed,
|
|
159
|
+
`requests in`,
|
|
160
|
+
delta,
|
|
161
|
+
`seconds =~`,
|
|
162
|
+
completed / delta,
|
|
163
|
+
`reqs/s`,
|
|
164
|
+
)
|
|
165
|
+
}),
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
await Promise.all(allLanes)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
await main()
|