@paylobster/cli 4.0.2 → 4.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/IMPLEMENTATION_SUMMARY.md +288 -0
- package/NEW_FEATURES.md +228 -0
- package/dist/src/commands/bridge.d.ts +6 -0
- package/dist/src/commands/bridge.d.ts.map +1 -0
- package/dist/src/commands/bridge.js +273 -0
- package/dist/src/commands/bridge.js.map +1 -0
- package/dist/src/commands/cascade.d.ts +3 -0
- package/dist/src/commands/cascade.d.ts.map +1 -0
- package/dist/src/commands/cascade.js +242 -0
- package/dist/src/commands/cascade.js.map +1 -0
- package/dist/src/commands/compliance.d.ts +3 -0
- package/dist/src/commands/compliance.d.ts.map +1 -0
- package/dist/src/commands/compliance.js +121 -0
- package/dist/src/commands/compliance.js.map +1 -0
- package/dist/src/commands/credit-score.d.ts +3 -0
- package/dist/src/commands/credit-score.d.ts.map +1 -0
- package/dist/src/commands/credit-score.js +174 -0
- package/dist/src/commands/credit-score.js.map +1 -0
- package/dist/src/commands/dispute.d.ts +3 -0
- package/dist/src/commands/dispute.d.ts.map +1 -0
- package/dist/src/commands/dispute.js +241 -0
- package/dist/src/commands/dispute.js.map +1 -0
- package/dist/src/commands/intent.d.ts +3 -0
- package/dist/src/commands/intent.d.ts.map +1 -0
- package/dist/src/commands/intent.js +227 -0
- package/dist/src/commands/intent.js.map +1 -0
- package/dist/src/commands/oracle.d.ts +3 -0
- package/dist/src/commands/oracle.d.ts.map +1 -0
- package/dist/src/commands/oracle.js +114 -0
- package/dist/src/commands/oracle.js.map +1 -0
- package/dist/src/commands/portfolio.d.ts +6 -0
- package/dist/src/commands/portfolio.d.ts.map +1 -0
- package/dist/src/commands/portfolio.js +179 -0
- package/dist/src/commands/portfolio.js.map +1 -0
- package/dist/src/commands/revenue-share.d.ts +3 -0
- package/dist/src/commands/revenue-share.d.ts.map +1 -0
- package/dist/src/commands/revenue-share.js +185 -0
- package/dist/src/commands/revenue-share.js.map +1 -0
- package/dist/src/commands/stream.d.ts +3 -0
- package/dist/src/commands/stream.d.ts.map +1 -0
- package/dist/src/commands/stream.js +213 -0
- package/dist/src/commands/stream.js.map +1 -0
- package/dist/src/commands/swap.d.ts +6 -0
- package/dist/src/commands/swap.d.ts.map +1 -0
- package/dist/src/commands/swap.js +278 -0
- package/dist/src/commands/swap.js.map +1 -0
- package/dist/src/index.js +23 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/lib/types.d.ts +1 -0
- package/dist/src/lib/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/commands/bridge.ts +443 -0
- package/src/commands/cascade.ts +280 -0
- package/src/commands/compliance.ts +123 -0
- package/src/commands/credit-score.ts +193 -0
- package/src/commands/dispute.ts +274 -0
- package/src/commands/intent.ts +261 -0
- package/src/commands/oracle.ts +116 -0
- package/src/commands/portfolio.ts +227 -0
- package/src/commands/revenue-share.ts +213 -0
- package/src/commands/stream.ts +244 -0
- package/src/commands/swap.ts +365 -0
- package/src/index.ts +23 -1
- package/src/lib/types.ts +1 -0
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { getWalletAddress } from '../lib/wallet';
|
|
3
|
+
import { success, error, info, withSpinner, outputJSON, confirm } from '../lib/display';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import type { OutputOptions } from '../lib/types';
|
|
6
|
+
|
|
7
|
+
const LIFI_API_URL = 'https://li.quest/v1';
|
|
8
|
+
|
|
9
|
+
interface LiFiChain {
|
|
10
|
+
id: number;
|
|
11
|
+
name: string;
|
|
12
|
+
key: string;
|
|
13
|
+
chainType: string;
|
|
14
|
+
nativeToken: {
|
|
15
|
+
symbol: string;
|
|
16
|
+
decimals: number;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface LiFiQuoteResponse {
|
|
21
|
+
estimate: {
|
|
22
|
+
fromAmount: string;
|
|
23
|
+
toAmount: string;
|
|
24
|
+
approvalAddress: string;
|
|
25
|
+
gasCosts: Array<{
|
|
26
|
+
amount: string;
|
|
27
|
+
amountUSD: string;
|
|
28
|
+
}>;
|
|
29
|
+
feeCosts: Array<{
|
|
30
|
+
amount: string;
|
|
31
|
+
amountUSD: string;
|
|
32
|
+
token: {
|
|
33
|
+
symbol: string;
|
|
34
|
+
};
|
|
35
|
+
}>;
|
|
36
|
+
executionDuration: number;
|
|
37
|
+
};
|
|
38
|
+
toolDetails: {
|
|
39
|
+
name: string;
|
|
40
|
+
};
|
|
41
|
+
action: {
|
|
42
|
+
fromChainId: number;
|
|
43
|
+
toChainId: number;
|
|
44
|
+
fromToken: {
|
|
45
|
+
symbol: string;
|
|
46
|
+
decimals: number;
|
|
47
|
+
};
|
|
48
|
+
toToken: {
|
|
49
|
+
symbol: string;
|
|
50
|
+
decimals: number;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
transactionRequest?: {
|
|
54
|
+
to: string;
|
|
55
|
+
data: string;
|
|
56
|
+
value: string;
|
|
57
|
+
gasLimit: string;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface LiFiStatusResponse {
|
|
62
|
+
status: string;
|
|
63
|
+
substatus: string;
|
|
64
|
+
sending: {
|
|
65
|
+
txHash: string;
|
|
66
|
+
amount: string;
|
|
67
|
+
token: {
|
|
68
|
+
symbol: string;
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
receiving?: {
|
|
72
|
+
txHash: string;
|
|
73
|
+
amount: string;
|
|
74
|
+
token: {
|
|
75
|
+
symbol: string;
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Chain name to ID mapping
|
|
81
|
+
const CHAIN_IDS: Record<string, number> = {
|
|
82
|
+
base: 8453,
|
|
83
|
+
solana: 1151111081099710,
|
|
84
|
+
ethereum: 1,
|
|
85
|
+
polygon: 137,
|
|
86
|
+
arbitrum: 42161,
|
|
87
|
+
optimism: 10,
|
|
88
|
+
avalanche: 43114,
|
|
89
|
+
bsc: 56,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Resolve chain name to chain ID
|
|
94
|
+
*/
|
|
95
|
+
function resolveChainId(input: string): number {
|
|
96
|
+
const lowerInput = input.toLowerCase();
|
|
97
|
+
if (CHAIN_IDS[lowerInput]) {
|
|
98
|
+
return CHAIN_IDS[lowerInput];
|
|
99
|
+
}
|
|
100
|
+
// Try to parse as number
|
|
101
|
+
const parsed = parseInt(input, 10);
|
|
102
|
+
if (!isNaN(parsed)) {
|
|
103
|
+
return parsed;
|
|
104
|
+
}
|
|
105
|
+
throw new Error(`Unknown chain: ${input}`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Format amount with decimals
|
|
110
|
+
*/
|
|
111
|
+
function formatTokenAmount(amount: string, decimals: number): string {
|
|
112
|
+
const num = BigInt(amount);
|
|
113
|
+
const divisor = BigInt(10 ** decimals);
|
|
114
|
+
const whole = num / divisor;
|
|
115
|
+
const fraction = num % divisor;
|
|
116
|
+
|
|
117
|
+
if (fraction === 0n) {
|
|
118
|
+
return whole.toString();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const fractionStr = fraction.toString().padStart(decimals, '0');
|
|
122
|
+
const trimmed = fractionStr.replace(/0+$/, '');
|
|
123
|
+
return `${whole}.${trimmed}`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Parse amount with decimals
|
|
128
|
+
*/
|
|
129
|
+
function parseTokenAmount(amount: string, decimals: number): string {
|
|
130
|
+
const [whole = '0', fraction = '0'] = amount.split('.');
|
|
131
|
+
const paddedFraction = fraction.padEnd(decimals, '0').slice(0, decimals);
|
|
132
|
+
return (BigInt(whole) * BigInt(10 ** decimals) + BigInt(paddedFraction)).toString();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Call Li.Fi API
|
|
137
|
+
*/
|
|
138
|
+
async function callLiFiAPI(endpoint: string): Promise<any> {
|
|
139
|
+
const response = await fetch(`${LIFI_API_URL}${endpoint}`);
|
|
140
|
+
|
|
141
|
+
if (!response.ok) {
|
|
142
|
+
const errorText = await response.text();
|
|
143
|
+
throw new Error(`Li.Fi API error: ${response.status} - ${errorText}`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return response.json();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Get bridge quote
|
|
151
|
+
*/
|
|
152
|
+
async function getBridgeQuote(
|
|
153
|
+
fromChain: number,
|
|
154
|
+
toChain: number,
|
|
155
|
+
fromToken: string,
|
|
156
|
+
toToken: string,
|
|
157
|
+
amount: string,
|
|
158
|
+
fromAddress: string
|
|
159
|
+
): Promise<LiFiQuoteResponse> {
|
|
160
|
+
const endpoint = `/quote?fromChain=${fromChain}&toChain=${toChain}&fromToken=${fromToken}&toToken=${toToken}&fromAmount=${amount}&fromAddress=${fromAddress}`;
|
|
161
|
+
const response = await callLiFiAPI(endpoint);
|
|
162
|
+
return response;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Get bridge status
|
|
167
|
+
*/
|
|
168
|
+
async function getBridgeStatus(txHash: string): Promise<LiFiStatusResponse> {
|
|
169
|
+
const endpoint = `/status?txHash=${txHash}`;
|
|
170
|
+
return callLiFiAPI(endpoint);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Get supported chains
|
|
175
|
+
*/
|
|
176
|
+
async function getChains(): Promise<LiFiChain[]> {
|
|
177
|
+
const response = await callLiFiAPI('/chains');
|
|
178
|
+
return response.chains;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Create bridge command
|
|
183
|
+
*/
|
|
184
|
+
export function createBridgeCommand(): Command {
|
|
185
|
+
const cmd = new Command('bridge')
|
|
186
|
+
.description('Cross-chain token bridges using Li.Fi');
|
|
187
|
+
|
|
188
|
+
// Bridge quote subcommand
|
|
189
|
+
cmd
|
|
190
|
+
.command('quote')
|
|
191
|
+
.description('Get bridge quote')
|
|
192
|
+
.requiredOption('--from <chain>', 'Source chain (e.g., base, ethereum, solana)')
|
|
193
|
+
.requiredOption('--to <chain>', 'Destination chain')
|
|
194
|
+
.requiredOption('--token <token>', 'Token symbol (e.g., USDC)')
|
|
195
|
+
.requiredOption('--amount <amount>', 'Amount to bridge')
|
|
196
|
+
.option('--json', 'Output as JSON')
|
|
197
|
+
.action(async (options: {
|
|
198
|
+
from: string;
|
|
199
|
+
to: string;
|
|
200
|
+
token: string;
|
|
201
|
+
amount: string;
|
|
202
|
+
} & OutputOptions) => {
|
|
203
|
+
try {
|
|
204
|
+
const address = getWalletAddress() as `0x${string}`;
|
|
205
|
+
|
|
206
|
+
const fromChainId = resolveChainId(options.from);
|
|
207
|
+
const toChainId = resolveChainId(options.to);
|
|
208
|
+
|
|
209
|
+
// Assume 6 decimals for USDC, 18 for others
|
|
210
|
+
const decimals = options.token.toUpperCase() === 'USDC' ? 6 : 18;
|
|
211
|
+
const amount = parseTokenAmount(options.amount, decimals);
|
|
212
|
+
|
|
213
|
+
const quote = await withSpinner(
|
|
214
|
+
'Fetching bridge quote...',
|
|
215
|
+
async () => getBridgeQuote(
|
|
216
|
+
fromChainId,
|
|
217
|
+
toChainId,
|
|
218
|
+
options.token,
|
|
219
|
+
options.token,
|
|
220
|
+
amount,
|
|
221
|
+
address
|
|
222
|
+
)
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
const fromAmount = formatTokenAmount(
|
|
226
|
+
quote.estimate.fromAmount,
|
|
227
|
+
quote.action.fromToken.decimals
|
|
228
|
+
);
|
|
229
|
+
const toAmount = formatTokenAmount(
|
|
230
|
+
quote.estimate.toAmount,
|
|
231
|
+
quote.action.toToken.decimals
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
// Calculate fee
|
|
235
|
+
const totalFeeUSD = quote.estimate.feeCosts.reduce(
|
|
236
|
+
(sum, fee) => sum + parseFloat(fee.amountUSD || '0'),
|
|
237
|
+
0
|
|
238
|
+
);
|
|
239
|
+
const feeAmount = parseFloat(fromAmount) - parseFloat(toAmount);
|
|
240
|
+
|
|
241
|
+
// Format time
|
|
242
|
+
const timeMinutes = Math.ceil(quote.estimate.executionDuration / 60);
|
|
243
|
+
|
|
244
|
+
if (outputJSON({
|
|
245
|
+
fromChain: options.from,
|
|
246
|
+
toChain: options.to,
|
|
247
|
+
token: options.token.toUpperCase(),
|
|
248
|
+
fromAmount,
|
|
249
|
+
toAmount,
|
|
250
|
+
fee: feeAmount.toFixed(decimals),
|
|
251
|
+
feeUSD: totalFeeUSD.toFixed(2),
|
|
252
|
+
estimatedTime: `${timeMinutes} min`,
|
|
253
|
+
via: quote.toolDetails.name,
|
|
254
|
+
}, options)) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Pretty output
|
|
259
|
+
console.log();
|
|
260
|
+
console.log(chalk.bold.cyan('🌉 Bridge Quote'));
|
|
261
|
+
console.log();
|
|
262
|
+
console.log(' From:', chalk.white.bold(fromAmount), chalk.gray(`${options.token.toUpperCase()} (${options.from})`));
|
|
263
|
+
console.log(' To: ', chalk.white.bold(toAmount), chalk.gray(`${options.token.toUpperCase()} (${options.to})`));
|
|
264
|
+
console.log(' Fee: ', chalk.yellow(feeAmount.toFixed(decimals)), chalk.gray(`${options.token.toUpperCase()} (~$${totalFeeUSD.toFixed(2)})`));
|
|
265
|
+
console.log(' Time:', chalk.gray(`~${timeMinutes} min`));
|
|
266
|
+
console.log(' Via: ', chalk.gray(quote.toolDetails.name));
|
|
267
|
+
console.log();
|
|
268
|
+
} catch (err) {
|
|
269
|
+
error(`Failed to get bridge quote: ${err}`);
|
|
270
|
+
process.exit(1);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
// Bridge execute subcommand
|
|
275
|
+
cmd
|
|
276
|
+
.command('execute')
|
|
277
|
+
.description('Execute cross-chain bridge')
|
|
278
|
+
.requiredOption('--from <chain>', 'Source chain (e.g., base, ethereum, solana)')
|
|
279
|
+
.requiredOption('--to <chain>', 'Destination chain')
|
|
280
|
+
.requiredOption('--token <token>', 'Token symbol (e.g., USDC)')
|
|
281
|
+
.requiredOption('--amount <amount>', 'Amount to bridge')
|
|
282
|
+
.option('--yes', 'Skip confirmation')
|
|
283
|
+
.option('--json', 'Output as JSON')
|
|
284
|
+
.action(async (options: {
|
|
285
|
+
from: string;
|
|
286
|
+
to: string;
|
|
287
|
+
token: string;
|
|
288
|
+
amount: string;
|
|
289
|
+
yes?: boolean;
|
|
290
|
+
} & OutputOptions) => {
|
|
291
|
+
try {
|
|
292
|
+
const address = getWalletAddress() as `0x${string}`;
|
|
293
|
+
|
|
294
|
+
const fromChainId = resolveChainId(options.from);
|
|
295
|
+
const toChainId = resolveChainId(options.to);
|
|
296
|
+
|
|
297
|
+
const decimals = options.token.toUpperCase() === 'USDC' ? 6 : 18;
|
|
298
|
+
const amount = parseTokenAmount(options.amount, decimals);
|
|
299
|
+
|
|
300
|
+
const quote = await withSpinner(
|
|
301
|
+
'Fetching bridge quote...',
|
|
302
|
+
async () => getBridgeQuote(
|
|
303
|
+
fromChainId,
|
|
304
|
+
toChainId,
|
|
305
|
+
options.token,
|
|
306
|
+
options.token,
|
|
307
|
+
amount,
|
|
308
|
+
address
|
|
309
|
+
)
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
const fromAmount = formatTokenAmount(
|
|
313
|
+
quote.estimate.fromAmount,
|
|
314
|
+
quote.action.fromToken.decimals
|
|
315
|
+
);
|
|
316
|
+
const toAmount = formatTokenAmount(
|
|
317
|
+
quote.estimate.toAmount,
|
|
318
|
+
quote.action.toToken.decimals
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
// Show quote and confirm
|
|
322
|
+
if (!options.yes) {
|
|
323
|
+
console.log();
|
|
324
|
+
console.log(chalk.bold.cyan('🌉 Bridge Quote'));
|
|
325
|
+
console.log();
|
|
326
|
+
console.log(' From:', chalk.white.bold(fromAmount), chalk.gray(`${options.token.toUpperCase()} (${options.from})`));
|
|
327
|
+
console.log(' To: ', chalk.white.bold(toAmount), chalk.gray(`${options.token.toUpperCase()} (${options.to})`));
|
|
328
|
+
console.log();
|
|
329
|
+
|
|
330
|
+
const confirmed = await confirm('Confirm bridge?');
|
|
331
|
+
if (!confirmed) {
|
|
332
|
+
info('Cancelled');
|
|
333
|
+
process.exit(0);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
info('Bridge execution not yet implemented - requires viem integration');
|
|
338
|
+
info('Transaction data available in quote response');
|
|
339
|
+
|
|
340
|
+
if (outputJSON({
|
|
341
|
+
quote,
|
|
342
|
+
note: 'Execute bridge by sending transaction with quote.transactionRequest',
|
|
343
|
+
}, options)) {
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (quote.transactionRequest) {
|
|
348
|
+
console.log();
|
|
349
|
+
info('To execute this bridge, send a transaction with the following data:');
|
|
350
|
+
console.log(chalk.gray(' To: '), quote.transactionRequest.to);
|
|
351
|
+
console.log(chalk.gray(' Data: '), quote.transactionRequest.data.slice(0, 66) + '...');
|
|
352
|
+
console.log();
|
|
353
|
+
}
|
|
354
|
+
} catch (err) {
|
|
355
|
+
error(`Failed to execute bridge: ${err}`);
|
|
356
|
+
process.exit(1);
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
// Bridge status subcommand
|
|
361
|
+
cmd
|
|
362
|
+
.command('status')
|
|
363
|
+
.description('Check bridge transaction status')
|
|
364
|
+
.argument('<txHash>', 'Transaction hash')
|
|
365
|
+
.option('--json', 'Output as JSON')
|
|
366
|
+
.action(async (txHash: string, options: OutputOptions) => {
|
|
367
|
+
try {
|
|
368
|
+
const status = await withSpinner(
|
|
369
|
+
'Fetching bridge status...',
|
|
370
|
+
async () => getBridgeStatus(txHash)
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
if (outputJSON(status, options)) {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
console.log();
|
|
378
|
+
console.log(chalk.bold.cyan('🌉 Bridge Status'));
|
|
379
|
+
console.log();
|
|
380
|
+
console.log(' Status: ', chalk.white.bold(status.status));
|
|
381
|
+
console.log(' Substatus:', chalk.gray(status.substatus));
|
|
382
|
+
console.log();
|
|
383
|
+
console.log(' Sending:');
|
|
384
|
+
console.log(' Tx: ', chalk.gray(status.sending.txHash));
|
|
385
|
+
console.log(' Amount:', chalk.white(status.sending.amount), chalk.gray(status.sending.token.symbol));
|
|
386
|
+
|
|
387
|
+
if (status.receiving) {
|
|
388
|
+
console.log();
|
|
389
|
+
console.log(' Receiving:');
|
|
390
|
+
console.log(' Tx: ', chalk.gray(status.receiving.txHash));
|
|
391
|
+
console.log(' Amount:', chalk.white(status.receiving.amount), chalk.gray(status.receiving.token.symbol));
|
|
392
|
+
}
|
|
393
|
+
console.log();
|
|
394
|
+
} catch (err) {
|
|
395
|
+
error(`Failed to get bridge status: ${err}`);
|
|
396
|
+
process.exit(1);
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
// List chains subcommand
|
|
401
|
+
cmd
|
|
402
|
+
.command('chains')
|
|
403
|
+
.description('List supported chains')
|
|
404
|
+
.option('--json', 'Output as JSON')
|
|
405
|
+
.action(async (options: OutputOptions) => {
|
|
406
|
+
try {
|
|
407
|
+
const chains = await withSpinner(
|
|
408
|
+
'Fetching supported chains...',
|
|
409
|
+
async () => getChains()
|
|
410
|
+
);
|
|
411
|
+
|
|
412
|
+
if (outputJSON({ chains }, options)) {
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
console.log();
|
|
417
|
+
console.log(chalk.bold.cyan('🌐 Supported Chains'));
|
|
418
|
+
console.log();
|
|
419
|
+
|
|
420
|
+
// Show popular chains first
|
|
421
|
+
const popularChains = chains.filter(c =>
|
|
422
|
+
['bas', 'eth', 'pol', 'arb', 'opt', 'sol', 'ava', 'bsc'].includes(c.key.toLowerCase())
|
|
423
|
+
);
|
|
424
|
+
|
|
425
|
+
for (const chain of popularChains) {
|
|
426
|
+
console.log(
|
|
427
|
+
chalk.white.bold(chain.name.padEnd(15)),
|
|
428
|
+
chalk.gray(`ID: ${chain.id}`),
|
|
429
|
+
chalk.dim(`(${chain.nativeToken.symbol})`)
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
console.log();
|
|
434
|
+
console.log(chalk.dim(`${chains.length} total chains supported`));
|
|
435
|
+
console.log();
|
|
436
|
+
} catch (err) {
|
|
437
|
+
error(`Failed to fetch chains: ${err}`);
|
|
438
|
+
process.exit(1);
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
return cmd;
|
|
443
|
+
}
|