@chainlink/ccip-cli 0.93.0 → 0.95.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/README.md +96 -40
- package/dist/commands/manual-exec.d.ts.map +1 -1
- package/dist/commands/manual-exec.js +11 -11
- package/dist/commands/manual-exec.js.map +1 -1
- package/dist/commands/send.d.ts +6 -10
- package/dist/commands/send.d.ts.map +1 -1
- package/dist/commands/send.js +72 -89
- package/dist/commands/send.js.map +1 -1
- package/dist/commands/show.d.ts.map +1 -1
- package/dist/commands/show.js +2 -4
- package/dist/commands/show.js.map +1 -1
- package/dist/commands/supported-tokens.d.ts +2 -2
- package/dist/commands/supported-tokens.d.ts.map +1 -1
- package/dist/commands/supported-tokens.js +22 -10
- package/dist/commands/supported-tokens.js.map +1 -1
- package/dist/commands/token.d.ts +26 -0
- package/dist/commands/token.d.ts.map +1 -0
- package/dist/commands/token.js +105 -0
- package/dist/commands/token.js.map +1 -0
- package/dist/commands/utils.d.ts.map +1 -1
- package/dist/commands/utils.js +21 -7
- package/dist/commands/utils.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +3 -3
- package/dist/providers/index.js.map +1 -1
- package/package.json +17 -17
- package/src/commands/manual-exec.ts +13 -23
- package/src/commands/send.ts +78 -107
- package/src/commands/show.ts +1 -4
- package/src/commands/supported-tokens.ts +21 -10
- package/src/commands/token.ts +132 -0
- package/src/commands/utils.ts +23 -6
- package/src/index.ts +2 -2
- package/src/providers/index.ts +7 -3
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token balance query command.
|
|
3
|
+
* Queries native or token balance for an address.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { type ChainStatic, networkInfo } from '@chainlink/ccip-sdk/src/index.ts'
|
|
7
|
+
import { formatUnits } from 'ethers'
|
|
8
|
+
import type { Argv } from 'yargs'
|
|
9
|
+
|
|
10
|
+
import { type Ctx, Format } from './types.ts'
|
|
11
|
+
import { getCtx, logParsedError, prettyTable } from './utils.ts'
|
|
12
|
+
import type { GlobalOpts } from '../index.ts'
|
|
13
|
+
import { fetchChainsFromRpcs } from '../providers/index.ts'
|
|
14
|
+
|
|
15
|
+
export const command = 'token'
|
|
16
|
+
export const describe = 'Query token balance for an address'
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Yargs builder for the token command.
|
|
20
|
+
* @param yargs - Yargs instance.
|
|
21
|
+
* @returns Configured yargs instance with command options.
|
|
22
|
+
*/
|
|
23
|
+
export const builder = (yargs: Argv) =>
|
|
24
|
+
yargs
|
|
25
|
+
.option('network', {
|
|
26
|
+
alias: 'n',
|
|
27
|
+
type: 'string',
|
|
28
|
+
demandOption: true,
|
|
29
|
+
describe: 'Network: chainId or name (e.g., ethereum-mainnet, solana-devnet)',
|
|
30
|
+
})
|
|
31
|
+
.option('holder', {
|
|
32
|
+
alias: 'H',
|
|
33
|
+
type: 'string',
|
|
34
|
+
demandOption: true,
|
|
35
|
+
describe: 'Wallet address to query balance for',
|
|
36
|
+
})
|
|
37
|
+
.option('token', {
|
|
38
|
+
alias: 't',
|
|
39
|
+
type: 'string',
|
|
40
|
+
demandOption: false,
|
|
41
|
+
describe: 'Token address (omit for native token balance)',
|
|
42
|
+
})
|
|
43
|
+
.example([
|
|
44
|
+
['ccip-cli token -n ethereum-mainnet -H 0x1234...abcd', 'Query native ETH balance'],
|
|
45
|
+
[
|
|
46
|
+
'ccip-cli token -n ethereum-mainnet -H 0x1234... -t 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
|
|
47
|
+
'Query USDC token balance',
|
|
48
|
+
],
|
|
49
|
+
[
|
|
50
|
+
'ccip-cli token -n solana-devnet -H EPUjBP3Xf76K1VKsDSc6GupBWE8uykNksCLJgXZn87CB',
|
|
51
|
+
'Query native SOL balance',
|
|
52
|
+
],
|
|
53
|
+
])
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Handler for the token command.
|
|
57
|
+
* @param argv - Command line arguments.
|
|
58
|
+
*/
|
|
59
|
+
export async function handler(argv: Awaited<ReturnType<typeof builder>['argv']> & GlobalOpts) {
|
|
60
|
+
const [ctx, destroy] = getCtx(argv)
|
|
61
|
+
return queryTokenBalance(ctx, argv)
|
|
62
|
+
.catch((err) => {
|
|
63
|
+
process.exitCode = 1
|
|
64
|
+
if (!logParsedError.call(ctx, err)) ctx.logger.error(err)
|
|
65
|
+
})
|
|
66
|
+
.finally(destroy)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function queryTokenBalance(ctx: Ctx, argv: Parameters<typeof handler>[0]) {
|
|
70
|
+
const { logger } = ctx
|
|
71
|
+
const networkName = networkInfo(argv.network).name
|
|
72
|
+
const getChain = fetchChainsFromRpcs(ctx, argv)
|
|
73
|
+
const chain = await getChain(networkName)
|
|
74
|
+
|
|
75
|
+
const balance = await chain.getBalance({
|
|
76
|
+
holder: argv.holder,
|
|
77
|
+
token: argv.token,
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
// Get token info for formatting (only for tokens, not native)
|
|
81
|
+
let tokenInfo
|
|
82
|
+
if (argv.token) {
|
|
83
|
+
argv.token = (chain.constructor as ChainStatic).getAddress(argv.token)
|
|
84
|
+
tokenInfo = await chain.getTokenInfo(argv.token)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const tokenLabel = tokenInfo?.symbol ?? 'native'
|
|
88
|
+
const formatted = formatUnits(
|
|
89
|
+
balance,
|
|
90
|
+
tokenInfo ? tokenInfo.decimals : (chain.constructor as ChainStatic).decimals,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
switch (argv.format) {
|
|
94
|
+
case Format.json:
|
|
95
|
+
logger.log(
|
|
96
|
+
JSON.stringify(
|
|
97
|
+
{
|
|
98
|
+
network: networkName,
|
|
99
|
+
holder: argv.holder,
|
|
100
|
+
token: tokenLabel,
|
|
101
|
+
balance: balance.toString(),
|
|
102
|
+
formatted,
|
|
103
|
+
...tokenInfo,
|
|
104
|
+
},
|
|
105
|
+
null,
|
|
106
|
+
2,
|
|
107
|
+
),
|
|
108
|
+
)
|
|
109
|
+
return
|
|
110
|
+
case Format.log:
|
|
111
|
+
logger.log(
|
|
112
|
+
`Balance of`,
|
|
113
|
+
tokenInfo ? argv.token : tokenLabel,
|
|
114
|
+
':',
|
|
115
|
+
balance,
|
|
116
|
+
`=`,
|
|
117
|
+
tokenInfo ? `${formatted} ${tokenLabel}` : formatted,
|
|
118
|
+
)
|
|
119
|
+
return
|
|
120
|
+
case Format.pretty:
|
|
121
|
+
default:
|
|
122
|
+
prettyTable.call(ctx, {
|
|
123
|
+
network: networkName,
|
|
124
|
+
holder: argv.holder,
|
|
125
|
+
token: argv.token ?? tokenLabel,
|
|
126
|
+
balance,
|
|
127
|
+
formatted,
|
|
128
|
+
...tokenInfo,
|
|
129
|
+
})
|
|
130
|
+
return
|
|
131
|
+
}
|
|
132
|
+
}
|
package/src/commands/utils.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
CCIPErrorCode,
|
|
13
13
|
ExecutionState,
|
|
14
14
|
getCCIPExplorerUrl,
|
|
15
|
+
getDataBytes,
|
|
15
16
|
networkInfo,
|
|
16
17
|
supportedChains,
|
|
17
18
|
} from '@chainlink/ccip-sdk/src/index.ts'
|
|
@@ -19,11 +20,11 @@ import { select } from '@inquirer/prompts'
|
|
|
19
20
|
import {
|
|
20
21
|
dataLength,
|
|
21
22
|
formatUnits,
|
|
22
|
-
getBytes,
|
|
23
23
|
hexlify,
|
|
24
24
|
isBytesLike,
|
|
25
25
|
isHexString,
|
|
26
26
|
parseUnits,
|
|
27
|
+
toBigInt,
|
|
27
28
|
toUtf8String,
|
|
28
29
|
} from 'ethers'
|
|
29
30
|
import type { PickDeep } from 'type-fest'
|
|
@@ -215,6 +216,26 @@ function omit<T extends Record<string, unknown>, K extends string>(
|
|
|
215
216
|
return result
|
|
216
217
|
}
|
|
217
218
|
|
|
219
|
+
// while formatData just breaks 0x bytes into 32B chunks for readability, this function first
|
|
220
|
+
// tests if the data looks like a UTF-8 string (with length prefix) and decode that before
|
|
221
|
+
function formatDataString(data: string): Record<string, string> {
|
|
222
|
+
const bytes = getDataBytes(data)
|
|
223
|
+
const isPrintableChars = (bytes_: Uint8Array) => bytes_.every((b) => 32 <= b && b <= 126)
|
|
224
|
+
if (bytes.length > 64 && toBigInt(bytes.subarray(0, 32)) === 32n) {
|
|
225
|
+
const len = toBigInt(bytes.subarray(32, 64))
|
|
226
|
+
if (
|
|
227
|
+
len < 512 &&
|
|
228
|
+
bytes.length - 64 === Math.ceil(Number(len) / 32) * 32 &&
|
|
229
|
+
isPrintableChars(bytes.subarray(64, 64 + Number(len))) &&
|
|
230
|
+
bytes.subarray(64 + Number(len)).every((b) => b === 0)
|
|
231
|
+
) {
|
|
232
|
+
return { data: toUtf8String(bytes.subarray(64, 64 + Number(len))) }
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (bytes.length > 0 && isPrintableChars(bytes)) return { data: toUtf8String(bytes) }
|
|
236
|
+
return formatData('data', data)
|
|
237
|
+
}
|
|
238
|
+
|
|
218
239
|
/**
|
|
219
240
|
* Prints a CCIP request in a human-readable format.
|
|
220
241
|
* @param source - Source chain instance.
|
|
@@ -289,11 +310,7 @@ export async function prettyRequest(this: Ctx, source: Chain, request: CCIPReque
|
|
|
289
310
|
'tokens',
|
|
290
311
|
await Promise.all(request.message.tokenAmounts.map(formatToken.bind(null, source))),
|
|
291
312
|
),
|
|
292
|
-
...(
|
|
293
|
-
dataLength(request.message.data) > 0 &&
|
|
294
|
-
getBytes(request.message.data).every((b) => 32 <= b && b <= 126) // printable characters
|
|
295
|
-
? { data: toUtf8String(request.message.data) }
|
|
296
|
-
: formatData('data', request.message.data)),
|
|
313
|
+
...formatDataString(request.message.data),
|
|
297
314
|
...('accounts' in request.message ? formatArray('accounts', request.message.accounts) : {}),
|
|
298
315
|
...rest,
|
|
299
316
|
})
|
package/src/index.ts
CHANGED
|
@@ -11,13 +11,13 @@ 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.95.0-5f1a7cb'
|
|
15
15
|
// generate:end
|
|
16
16
|
|
|
17
17
|
const globalOpts = {
|
|
18
18
|
rpcs: {
|
|
19
19
|
type: 'array',
|
|
20
|
-
alias:
|
|
20
|
+
alias: 'rpc',
|
|
21
21
|
describe: 'List of RPC endpoint URLs, ws[s] or http[s]',
|
|
22
22
|
string: true,
|
|
23
23
|
},
|
package/src/providers/index.ts
CHANGED
|
@@ -8,10 +8,10 @@ import {
|
|
|
8
8
|
type EVMChain,
|
|
9
9
|
type TONChain,
|
|
10
10
|
CCIPChainFamilyUnsupportedError,
|
|
11
|
-
CCIPNetworkFamilyUnsupportedError,
|
|
12
11
|
CCIPRpcNotFoundError,
|
|
13
12
|
CCIPTransactionNotFoundError,
|
|
14
13
|
ChainFamily,
|
|
14
|
+
NetworkType,
|
|
15
15
|
networkInfo,
|
|
16
16
|
supportedChains,
|
|
17
17
|
} from '@chainlink/ccip-sdk/src/index.ts'
|
|
@@ -94,7 +94,7 @@ export function fetchChainsFromRpcs(
|
|
|
94
94
|
const loadChainFamily = (F: ChainFamily, txHash?: string) =>
|
|
95
95
|
(initFamily$[F] ??= (endpoints$ ??= collectEndpoints.call(ctx, argv)).then((endpoints) => {
|
|
96
96
|
const C = supportedChains[F]
|
|
97
|
-
if (!C) throw new
|
|
97
|
+
if (!C) throw new CCIPChainFamilyUnsupportedError(F)
|
|
98
98
|
ctx.logger.debug('Racing', endpoints.size, 'RPC endpoints for', F)
|
|
99
99
|
|
|
100
100
|
const chains$: Promise<Chain>[] = []
|
|
@@ -214,7 +214,11 @@ export async function loadChainWallet(chain: Chain, argv: { wallet?: unknown; rp
|
|
|
214
214
|
wallet = loadSuiWallet(argv)
|
|
215
215
|
return [wallet.toSuiAddress(), wallet] as const
|
|
216
216
|
case ChainFamily.TON:
|
|
217
|
-
wallet = await loadTonWallet(
|
|
217
|
+
wallet = await loadTonWallet(
|
|
218
|
+
(chain as TONChain).provider,
|
|
219
|
+
argv,
|
|
220
|
+
chain.network.networkType === NetworkType.Testnet,
|
|
221
|
+
)
|
|
218
222
|
return [wallet.getAddress(), wallet] as const
|
|
219
223
|
default:
|
|
220
224
|
// TypeScript exhaustiveness check - this should never be reached
|