@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.
Files changed (64) hide show
  1. package/IMPLEMENTATION_SUMMARY.md +288 -0
  2. package/NEW_FEATURES.md +228 -0
  3. package/dist/src/commands/bridge.d.ts +6 -0
  4. package/dist/src/commands/bridge.d.ts.map +1 -0
  5. package/dist/src/commands/bridge.js +273 -0
  6. package/dist/src/commands/bridge.js.map +1 -0
  7. package/dist/src/commands/cascade.d.ts +3 -0
  8. package/dist/src/commands/cascade.d.ts.map +1 -0
  9. package/dist/src/commands/cascade.js +242 -0
  10. package/dist/src/commands/cascade.js.map +1 -0
  11. package/dist/src/commands/compliance.d.ts +3 -0
  12. package/dist/src/commands/compliance.d.ts.map +1 -0
  13. package/dist/src/commands/compliance.js +121 -0
  14. package/dist/src/commands/compliance.js.map +1 -0
  15. package/dist/src/commands/credit-score.d.ts +3 -0
  16. package/dist/src/commands/credit-score.d.ts.map +1 -0
  17. package/dist/src/commands/credit-score.js +174 -0
  18. package/dist/src/commands/credit-score.js.map +1 -0
  19. package/dist/src/commands/dispute.d.ts +3 -0
  20. package/dist/src/commands/dispute.d.ts.map +1 -0
  21. package/dist/src/commands/dispute.js +241 -0
  22. package/dist/src/commands/dispute.js.map +1 -0
  23. package/dist/src/commands/intent.d.ts +3 -0
  24. package/dist/src/commands/intent.d.ts.map +1 -0
  25. package/dist/src/commands/intent.js +227 -0
  26. package/dist/src/commands/intent.js.map +1 -0
  27. package/dist/src/commands/oracle.d.ts +3 -0
  28. package/dist/src/commands/oracle.d.ts.map +1 -0
  29. package/dist/src/commands/oracle.js +114 -0
  30. package/dist/src/commands/oracle.js.map +1 -0
  31. package/dist/src/commands/portfolio.d.ts +6 -0
  32. package/dist/src/commands/portfolio.d.ts.map +1 -0
  33. package/dist/src/commands/portfolio.js +179 -0
  34. package/dist/src/commands/portfolio.js.map +1 -0
  35. package/dist/src/commands/revenue-share.d.ts +3 -0
  36. package/dist/src/commands/revenue-share.d.ts.map +1 -0
  37. package/dist/src/commands/revenue-share.js +185 -0
  38. package/dist/src/commands/revenue-share.js.map +1 -0
  39. package/dist/src/commands/stream.d.ts +3 -0
  40. package/dist/src/commands/stream.d.ts.map +1 -0
  41. package/dist/src/commands/stream.js +213 -0
  42. package/dist/src/commands/stream.js.map +1 -0
  43. package/dist/src/commands/swap.d.ts +6 -0
  44. package/dist/src/commands/swap.d.ts.map +1 -0
  45. package/dist/src/commands/swap.js +278 -0
  46. package/dist/src/commands/swap.js.map +1 -0
  47. package/dist/src/index.js +23 -1
  48. package/dist/src/index.js.map +1 -1
  49. package/dist/src/lib/types.d.ts +1 -0
  50. package/dist/src/lib/types.d.ts.map +1 -1
  51. package/package.json +1 -1
  52. package/src/commands/bridge.ts +443 -0
  53. package/src/commands/cascade.ts +280 -0
  54. package/src/commands/compliance.ts +123 -0
  55. package/src/commands/credit-score.ts +193 -0
  56. package/src/commands/dispute.ts +274 -0
  57. package/src/commands/intent.ts +261 -0
  58. package/src/commands/oracle.ts +116 -0
  59. package/src/commands/portfolio.ts +227 -0
  60. package/src/commands/revenue-share.ts +213 -0
  61. package/src/commands/stream.ts +244 -0
  62. package/src/commands/swap.ts +365 -0
  63. package/src/index.ts +23 -1
  64. 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
+ }