@chainlink/ccip-cli 0.0.0 → 0.90.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 (69) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +238 -0
  3. package/dist/commands/index.d.ts +2 -0
  4. package/dist/commands/index.d.ts.map +1 -0
  5. package/dist/commands/index.js +2 -0
  6. package/dist/commands/index.js.map +1 -0
  7. package/dist/commands/manual-exec.d.ts +56 -0
  8. package/dist/commands/manual-exec.d.ts.map +1 -0
  9. package/dist/commands/manual-exec.js +405 -0
  10. package/dist/commands/manual-exec.js.map +1 -0
  11. package/dist/commands/parse.d.ts +9 -0
  12. package/dist/commands/parse.d.ts.map +1 -0
  13. package/dist/commands/parse.js +47 -0
  14. package/dist/commands/parse.js.map +1 -0
  15. package/dist/commands/send.d.ts +80 -0
  16. package/dist/commands/send.d.ts.map +1 -0
  17. package/dist/commands/send.js +258 -0
  18. package/dist/commands/send.js.map +1 -0
  19. package/dist/commands/show.d.ts +18 -0
  20. package/dist/commands/show.d.ts.map +1 -0
  21. package/dist/commands/show.js +112 -0
  22. package/dist/commands/show.js.map +1 -0
  23. package/dist/commands/supported-tokens.d.ts +37 -0
  24. package/dist/commands/supported-tokens.d.ts.map +1 -0
  25. package/dist/commands/supported-tokens.js +214 -0
  26. package/dist/commands/supported-tokens.js.map +1 -0
  27. package/dist/commands/types.d.ts +7 -0
  28. package/dist/commands/types.d.ts.map +1 -0
  29. package/dist/commands/types.js +6 -0
  30. package/dist/commands/types.js.map +1 -0
  31. package/dist/commands/utils.d.ts +40 -0
  32. package/dist/commands/utils.d.ts.map +1 -0
  33. package/dist/commands/utils.js +330 -0
  34. package/dist/commands/utils.js.map +1 -0
  35. package/dist/index.d.ts +34 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +63 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/providers/aptos.d.ts +15 -0
  40. package/dist/providers/aptos.d.ts.map +1 -0
  41. package/dist/providers/aptos.js +74 -0
  42. package/dist/providers/aptos.js.map +1 -0
  43. package/dist/providers/evm.d.ts +2 -0
  44. package/dist/providers/evm.d.ts.map +1 -0
  45. package/dist/providers/evm.js +42 -0
  46. package/dist/providers/evm.js.map +1 -0
  47. package/dist/providers/index.d.ts +13 -0
  48. package/dist/providers/index.d.ts.map +1 -0
  49. package/dist/providers/index.js +104 -0
  50. package/dist/providers/index.js.map +1 -0
  51. package/dist/providers/solana.d.ts +13 -0
  52. package/dist/providers/solana.d.ts.map +1 -0
  53. package/dist/providers/solana.js +79 -0
  54. package/dist/providers/solana.js.map +1 -0
  55. package/package.json +57 -8
  56. package/src/commands/index.ts +1 -0
  57. package/src/commands/manual-exec.ts +468 -0
  58. package/src/commands/parse.ts +52 -0
  59. package/src/commands/send.ts +316 -0
  60. package/src/commands/show.ts +151 -0
  61. package/src/commands/supported-tokens.ts +245 -0
  62. package/src/commands/types.ts +6 -0
  63. package/src/commands/utils.ts +404 -0
  64. package/src/index.ts +70 -0
  65. package/src/providers/aptos.ts +100 -0
  66. package/src/providers/evm.ts +48 -0
  67. package/src/providers/index.ts +141 -0
  68. package/src/providers/solana.ts +93 -0
  69. package/tsconfig.json +18 -0
@@ -0,0 +1,141 @@
1
+ import { readFile } from 'node:fs/promises'
2
+
3
+ import {
4
+ type Chain,
5
+ type ChainGetter,
6
+ type ChainTransaction,
7
+ networkInfo,
8
+ supportedChains,
9
+ } from '@chainlink/ccip-sdk/src/index.ts'
10
+
11
+ import './aptos.ts'
12
+ import './evm.ts'
13
+ import './solana.ts'
14
+
15
+ const RPCS_RE = /\b(?:http|ws)s?:\/\/[\w/\\@&?%~#.,;:=+-]+/
16
+
17
+ async function collectEndpoints({
18
+ rpcs,
19
+ 'rpcs-file': rpcsFile,
20
+ }: {
21
+ rpcs?: string[]
22
+ 'rpcs-file'?: string
23
+ }): Promise<Set<string>> {
24
+ const endpoints = new Set<string>(rpcs || [])
25
+ for (const [env, val] of Object.entries(process.env)) {
26
+ if (env.startsWith('RPC_') && val && RPCS_RE.test(val)) endpoints.add(val)
27
+ }
28
+ if (rpcsFile) {
29
+ try {
30
+ const fileContent = await readFile(rpcsFile, 'utf8')
31
+ for (const line of fileContent.toString().split(/(?:\r\n|\r|\n)/g)) {
32
+ const match = line.match(RPCS_RE)
33
+ if (match) endpoints.add(match[0])
34
+ }
35
+ } catch (error) {
36
+ console.debug('Error reading RPCs file', error)
37
+ }
38
+ }
39
+ return endpoints
40
+ }
41
+
42
+ export function fetchChainsFromRpcs(
43
+ argv: { rpcs?: string[]; 'rpcs-file'?: string },
44
+ txHash?: undefined,
45
+ destroy?: Promise<unknown>,
46
+ ): ChainGetter
47
+ export function fetchChainsFromRpcs(
48
+ argv: { rpcs?: string[]; 'rpcs-file'?: string },
49
+ txHash: string,
50
+ destroy?: Promise<unknown>,
51
+ ): [ChainGetter, Promise<ChainTransaction>]
52
+
53
+ /**
54
+ * Receives a list of rpcs and/or rpcs file, and loads them all concurrently
55
+ * Returns a ChainGetter function and optinoally a ChainTransaction promise
56
+ * @param argv - Options containing rpcs (list) and/or rpcs file
57
+ * @param txHash - Optional txHash to fetch concurrently; causes the function to return a [ChainGetter, Promise<ChainTransaction>]
58
+ * @param destroy - A promise to signal when to stop fetching chains
59
+ * @returns a ChainGetter (alone if no txHash was provided), or a tuple of [ChainGetter, Promise<ChainTransaction>]
60
+ */
61
+ export function fetchChainsFromRpcs(
62
+ argv: { rpcs?: string[]; 'rpcs-file'?: string },
63
+ txHash?: string,
64
+ destroy?: Promise<unknown>,
65
+ ) {
66
+ const chains: Record<string, Promise<Chain>> = {}
67
+ const chainsCbs: Record<
68
+ string,
69
+ readonly [resolve: (value: Chain) => void, reject: (reason?: unknown) => void]
70
+ > = {}
71
+ let finished = false
72
+ const txs: Promise<ChainTransaction>[] = []
73
+
74
+ const init$ = collectEndpoints(argv).then((endpoints) => {
75
+ const pendingPromises: Promise<unknown>[] = []
76
+ let txFound = false
77
+ for (const C of Object.values(supportedChains)) {
78
+ for (const url of endpoints) {
79
+ const chain$ = C.fromUrl(url)
80
+ if (txHash) {
81
+ const tx$ = chain$.then((chain) => chain.getTransaction(txHash))
82
+ void tx$.then(
83
+ ({ chain }) => {
84
+ if (txFound) return
85
+ txFound = true
86
+ // in case tx is found, prefer it over any previously found chain
87
+ chains[chain.network.name] = chain$
88
+ delete chainsCbs[chain.network.name]
89
+ },
90
+ () => {},
91
+ )
92
+ txs.push(tx$)
93
+ }
94
+
95
+ pendingPromises.push(
96
+ chain$.then((chain) => {
97
+ if (chain.network.name in chains && !(chain.network.name in chainsCbs))
98
+ return chain.destroy?.() // lost race
99
+ void destroy?.finally(() => {
100
+ void chain.destroy?.() // cleanup
101
+ })
102
+ if (!(chain.network.name in chains)) {
103
+ chains[chain.network.name] = Promise.resolve(chain)
104
+ } else if (chain.network.name in chainsCbs) {
105
+ const [resolve] = chainsCbs[chain.network.name]
106
+ resolve(chain)
107
+ }
108
+ }),
109
+ )
110
+ }
111
+ }
112
+ const res = Promise.allSettled(pendingPromises)
113
+ void (destroy ? Promise.race([res, destroy]) : res).finally(() => {
114
+ finished = true
115
+ Object.entries(chainsCbs).forEach(([name, [_, reject]]) =>
116
+ reject(new Error(`No provider/chain found for network=${name}`)),
117
+ )
118
+ })
119
+ return Promise.any(txs)
120
+ })
121
+
122
+ const chainGetter = async (idOrSelectorOrName: number | string | bigint): Promise<Chain> => {
123
+ const network = networkInfo(idOrSelectorOrName)
124
+ if (network.name in chains) return chains[network.name]
125
+ if (finished) throw new Error(`No provider/chain found for network=${network.name}`)
126
+ chains[network.name] = new Promise((resolve, reject) => {
127
+ chainsCbs[network.name] = [resolve, reject]
128
+ })
129
+ void chains[network.name].finally(() => {
130
+ delete chainsCbs[network.name]
131
+ })
132
+ return chains[network.name]
133
+ }
134
+
135
+ if (txHash) {
136
+ return [chainGetter, init$]
137
+ } else {
138
+ void init$.catch(() => {})
139
+ return chainGetter
140
+ }
141
+ }
@@ -0,0 +1,93 @@
1
+ import { existsSync, readFileSync } from 'node:fs'
2
+ import util from 'node:util'
3
+
4
+ import { SolanaChain } from '@chainlink/ccip-sdk/src/index.ts'
5
+ import { Wallet as SolanaWallet } from '@coral-xyz/anchor'
6
+ import SolanaLedger from '@ledgerhq/hw-app-solana'
7
+ import HIDTransport from '@ledgerhq/hw-transport-node-hid'
8
+ import {
9
+ type Message,
10
+ type MessageV0,
11
+ type VersionedTransaction,
12
+ Keypair,
13
+ PublicKey,
14
+ Transaction,
15
+ } from '@solana/web3.js'
16
+ import bs58 from 'bs58'
17
+ import { getBytes, hexlify } from 'ethers'
18
+
19
+ export class LedgerSolanaWallet {
20
+ publicKey: PublicKey
21
+ wallet: SolanaLedger.default
22
+ path: string
23
+
24
+ private constructor(solanaLW: SolanaLedger.default, pubKey: PublicKey, path: string) {
25
+ this.wallet = solanaLW
26
+ this.publicKey = pubKey
27
+ this.path = path
28
+ }
29
+
30
+ static async create(path: string) {
31
+ try {
32
+ const transport = await HIDTransport.default.create()
33
+ const solana = new SolanaLedger.default(transport)
34
+ const { address } = await solana.getAddress(path, false)
35
+ const pubkey = new PublicKey(address)
36
+ console.info('Ledger connected:', pubkey.toBase58(), `, derivationPath:`, path)
37
+ return new LedgerSolanaWallet(solana, pubkey, path)
38
+ } catch (e) {
39
+ console.error('Ledger: Could not access ledger. Is it unlocked and Solana app open?')
40
+ throw e
41
+ }
42
+ }
43
+
44
+ async signTransaction<T extends Transaction | VersionedTransaction>(tx: T) {
45
+ console.debug('Ledger: Request to sign message from', this.publicKey.toBase58())
46
+ // serializeMessage on v0, serialize on v1
47
+
48
+ let msg: Message | MessageV0
49
+ if (tx instanceof Transaction) {
50
+ msg = tx.compileMessage()
51
+ } else {
52
+ msg = tx.message
53
+ }
54
+ const { signature } = await this.wallet.signTransaction(this.path, Buffer.from(msg.serialize()))
55
+ tx.addSignature(this.publicKey, signature)
56
+ return tx
57
+ }
58
+
59
+ async signAllTransactions<T extends Transaction | VersionedTransaction>(txs: T[]) {
60
+ console.info('Signing multiple transactions with Ledger')
61
+ const signedTxs: T[] = []
62
+ for (const tx of txs) {
63
+ signedTxs.push(await this.signTransaction(tx))
64
+ }
65
+ return signedTxs
66
+ }
67
+
68
+ get payer(): Keypair {
69
+ throw new Error('Payer method not available on Ledger')
70
+ }
71
+ }
72
+
73
+ SolanaChain.getWallet = async function loadSolanaWallet({
74
+ wallet: walletOpt,
75
+ }: { wallet?: unknown } = {}): Promise<SolanaWallet> {
76
+ if (!walletOpt)
77
+ walletOpt = process.env['USER_KEY'] || process.env['OWNER_KEY'] || '~/.config/solana/id.json'
78
+ let wallet: string
79
+ if (typeof walletOpt !== 'string')
80
+ throw new Error(`Invalid wallet option: ${util.inspect(walletOpt)}`)
81
+ wallet = walletOpt
82
+ if (walletOpt === 'ledger' || walletOpt.startsWith('ledger:')) {
83
+ let derivationPath = walletOpt.split(':')[1]
84
+ if (!derivationPath) derivationPath = "44'/501'/0'"
85
+ else if (!isNaN(Number(derivationPath))) derivationPath = `44'/501'/${derivationPath}'`
86
+ return (await LedgerSolanaWallet.create(derivationPath)) as SolanaWallet
87
+ } else if (existsSync(walletOpt)) {
88
+ wallet = hexlify(new Uint8Array(JSON.parse(readFileSync(walletOpt, 'utf8'))))
89
+ }
90
+ return new SolanaWallet(
91
+ Keypair.fromSecretKey(wallet.startsWith('0x') ? getBytes(wallet) : bs58.decode(wallet)),
92
+ )
93
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "lib": ["ES2023"],
6
+ "strict": true,
7
+ "skipLibCheck": true,
8
+ "declaration": true,
9
+ "declarationMap": true,
10
+ "sourceMap": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "allowImportingTsExtensions": true,
13
+ "rewriteRelativeImportExtensions": true,
14
+ "verbatimModuleSyntax": true,
15
+ "erasableSyntaxOnly": true,
16
+ "resolveJsonModule": true
17
+ }
18
+ }