@paylobster/cli 4.3.0 → 4.5.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 (54) hide show
  1. package/README.md +82 -40
  2. package/dist/src/commands/dashboard.d.ts +3 -0
  3. package/dist/src/commands/dashboard.d.ts.map +1 -0
  4. package/dist/src/commands/dashboard.js +67 -0
  5. package/dist/src/commands/dashboard.js.map +1 -0
  6. package/dist/src/commands/export.d.ts +3 -0
  7. package/dist/src/commands/export.d.ts.map +1 -0
  8. package/dist/src/commands/export.js +97 -0
  9. package/dist/src/commands/export.js.map +1 -0
  10. package/dist/src/commands/health.d.ts +3 -0
  11. package/dist/src/commands/health.d.ts.map +1 -0
  12. package/dist/src/commands/health.js +165 -0
  13. package/dist/src/commands/health.js.map +1 -0
  14. package/dist/src/commands/invest.d.ts +3 -0
  15. package/dist/src/commands/invest.d.ts.map +1 -0
  16. package/dist/src/commands/invest.js +721 -0
  17. package/dist/src/commands/invest.js.map +1 -0
  18. package/dist/src/commands/quickstart.d.ts +3 -0
  19. package/dist/src/commands/quickstart.d.ts.map +1 -0
  20. package/dist/src/commands/quickstart.js +138 -0
  21. package/dist/src/commands/quickstart.js.map +1 -0
  22. package/dist/src/commands/search.d.ts +3 -0
  23. package/dist/src/commands/search.d.ts.map +1 -0
  24. package/dist/src/commands/search.js +126 -0
  25. package/dist/src/commands/search.js.map +1 -0
  26. package/dist/src/commands/treasury.d.ts.map +1 -1
  27. package/dist/src/commands/treasury.js +317 -0
  28. package/dist/src/commands/treasury.js.map +1 -1
  29. package/dist/src/commands/trust-graph.d.ts +3 -0
  30. package/dist/src/commands/trust-graph.d.ts.map +1 -0
  31. package/dist/src/commands/trust-graph.js +282 -0
  32. package/dist/src/commands/trust-graph.js.map +1 -0
  33. package/dist/src/commands/whoami.d.ts +3 -0
  34. package/dist/src/commands/whoami.d.ts.map +1 -0
  35. package/dist/src/commands/whoami.js +160 -0
  36. package/dist/src/commands/whoami.js.map +1 -0
  37. package/dist/src/index.js +18 -2
  38. package/dist/src/index.js.map +1 -1
  39. package/dist/src/lib/contracts.d.ts +177 -23
  40. package/dist/src/lib/contracts.d.ts.map +1 -1
  41. package/dist/src/lib/contracts.js +151 -0
  42. package/dist/src/lib/contracts.js.map +1 -1
  43. package/package.json +4 -2
  44. package/src/commands/dashboard.ts +69 -0
  45. package/src/commands/export.ts +132 -0
  46. package/src/commands/health.ts +212 -0
  47. package/src/commands/invest.ts +810 -0
  48. package/src/commands/quickstart.ts +150 -0
  49. package/src/commands/search.ts +151 -0
  50. package/src/commands/treasury.ts +385 -0
  51. package/src/commands/trust-graph.ts +267 -0
  52. package/src/commands/whoami.ts +172 -0
  53. package/src/index.ts +18 -2
  54. package/src/lib/contracts.ts +159 -0
@@ -0,0 +1,721 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.registerInvestCommand = registerInvestCommand;
7
+ const viem_1 = require("viem");
8
+ const chains_1 = require("viem/chains");
9
+ const wallet_1 = require("../lib/wallet");
10
+ const contracts_1 = require("../lib/contracts");
11
+ const display_1 = require("../lib/display");
12
+ const chalk_1 = __importDefault(require("chalk"));
13
+ // InvestmentTermSheet contract
14
+ const INVESTMENT_CONTRACT = '0xfa4d9933422401e8b0846f14889b383e068860eb';
15
+ var ReturnType;
16
+ (function (ReturnType) {
17
+ ReturnType[ReturnType["REVENUE_SHARE"] = 0] = "REVENUE_SHARE";
18
+ ReturnType[ReturnType["FIXED_RETURN"] = 1] = "FIXED_RETURN";
19
+ ReturnType[ReturnType["MILESTONE_BASED"] = 2] = "MILESTONE_BASED";
20
+ ReturnType[ReturnType["STREAMING"] = 3] = "STREAMING";
21
+ })(ReturnType || (ReturnType = {}));
22
+ var InvestmentStatus;
23
+ (function (InvestmentStatus) {
24
+ InvestmentStatus[InvestmentStatus["PROPOSED"] = 0] = "PROPOSED";
25
+ InvestmentStatus[InvestmentStatus["FUNDED"] = 1] = "FUNDED";
26
+ InvestmentStatus[InvestmentStatus["ACTIVE"] = 2] = "ACTIVE";
27
+ InvestmentStatus[InvestmentStatus["COMPLETED"] = 3] = "COMPLETED";
28
+ InvestmentStatus[InvestmentStatus["DEFAULTED"] = 4] = "DEFAULTED";
29
+ InvestmentStatus[InvestmentStatus["CANCELLED"] = 5] = "CANCELLED";
30
+ })(InvestmentStatus || (InvestmentStatus = {}));
31
+ const RETURN_TYPE_NAMES = {
32
+ 0: 'Revenue Share',
33
+ 1: 'Fixed Return',
34
+ 2: 'Milestone Based',
35
+ 3: 'Streaming',
36
+ };
37
+ const STATUS_NAMES = {
38
+ 0: 'Proposed',
39
+ 1: 'Funded',
40
+ 2: 'Active',
41
+ 3: 'Completed',
42
+ 4: 'Defaulted',
43
+ 5: 'Cancelled',
44
+ };
45
+ // ABI
46
+ const INVESTMENT_ABI = [
47
+ {
48
+ type: 'function',
49
+ name: 'propose',
50
+ stateMutability: 'nonpayable',
51
+ inputs: [
52
+ { name: '_treasury', type: 'address' },
53
+ { name: '_token', type: 'address' },
54
+ { name: '_amount', type: 'uint256' },
55
+ {
56
+ name: '_terms',
57
+ type: 'tuple',
58
+ components: [
59
+ { name: 'returnType', type: 'uint8' },
60
+ { name: 'revShareBps', type: 'uint256' },
61
+ { name: 'fixedReturnBps', type: 'uint256' },
62
+ { name: 'durationSeconds', type: 'uint256' },
63
+ { name: 'cliffSeconds', type: 'uint256' },
64
+ { name: 'milestoneCount', type: 'uint256' },
65
+ ],
66
+ },
67
+ {
68
+ name: '_protections',
69
+ type: 'tuple',
70
+ components: [
71
+ { name: 'liquidationPreference', type: 'bool' },
72
+ { name: 'maxInvestorsBps', type: 'uint256' },
73
+ { name: 'minReserveBps', type: 'uint256' },
74
+ { name: 'minReputationScore', type: 'uint256' },
75
+ ],
76
+ },
77
+ ],
78
+ outputs: [{ name: '', type: 'uint256' }],
79
+ },
80
+ {
81
+ type: 'function',
82
+ name: 'fund',
83
+ stateMutability: 'nonpayable',
84
+ inputs: [{ name: 'investmentId', type: 'uint256' }],
85
+ outputs: [],
86
+ },
87
+ {
88
+ type: 'function',
89
+ name: 'claimReturn',
90
+ stateMutability: 'nonpayable',
91
+ inputs: [{ name: 'investmentId', type: 'uint256' }],
92
+ outputs: [],
93
+ },
94
+ {
95
+ type: 'function',
96
+ name: 'completeMilestone',
97
+ stateMutability: 'nonpayable',
98
+ inputs: [{ name: 'investmentId', type: 'uint256' }],
99
+ outputs: [],
100
+ },
101
+ {
102
+ type: 'function',
103
+ name: 'triggerDefault',
104
+ stateMutability: 'nonpayable',
105
+ inputs: [{ name: 'investmentId', type: 'uint256' }],
106
+ outputs: [],
107
+ },
108
+ {
109
+ type: 'function',
110
+ name: 'cancel',
111
+ stateMutability: 'nonpayable',
112
+ inputs: [{ name: 'investmentId', type: 'uint256' }],
113
+ outputs: [],
114
+ },
115
+ {
116
+ type: 'function',
117
+ name: 'getInvestment',
118
+ stateMutability: 'view',
119
+ inputs: [{ name: 'investmentId', type: 'uint256' }],
120
+ outputs: [
121
+ { name: 'investor', type: 'address' },
122
+ { name: 'treasury', type: 'address' },
123
+ { name: 'token', type: 'address' },
124
+ { name: 'investedAmount', type: 'uint256' },
125
+ { name: 'returnedAmount', type: 'uint256' },
126
+ { name: 'targetReturn', type: 'uint256' },
127
+ { name: 'status', type: 'uint8' },
128
+ { name: 'createdAt', type: 'uint256' },
129
+ { name: 'fundedAt', type: 'uint256' },
130
+ { name: 'returnType', type: 'uint8' },
131
+ { name: 'durationSeconds', type: 'uint256' },
132
+ { name: 'milestonesCompleted', type: 'uint256' },
133
+ { name: 'milestoneCount', type: 'uint256' },
134
+ ],
135
+ },
136
+ {
137
+ type: 'function',
138
+ name: 'getTreasuryInvestments',
139
+ stateMutability: 'view',
140
+ inputs: [{ name: 'treasury', type: 'address' }],
141
+ outputs: [{ name: '', type: 'uint256[]' }],
142
+ },
143
+ {
144
+ type: 'function',
145
+ name: 'getInvestorPortfolio',
146
+ stateMutability: 'view',
147
+ inputs: [{ name: 'investor', type: 'address' }],
148
+ outputs: [{ name: '', type: 'uint256[]' }],
149
+ },
150
+ {
151
+ type: 'function',
152
+ name: 'getClaimable',
153
+ stateMutability: 'view',
154
+ inputs: [{ name: 'investmentId', type: 'uint256' }],
155
+ outputs: [{ name: '', type: 'uint256' }],
156
+ },
157
+ {
158
+ type: 'function',
159
+ name: 'getStats',
160
+ stateMutability: 'view',
161
+ inputs: [],
162
+ outputs: [
163
+ { name: '_totalInvestments', type: 'uint256' },
164
+ { name: '_totalInvested', type: 'uint256' },
165
+ { name: '_totalReturned', type: 'uint256' },
166
+ { name: '_totalProtocolFees', type: 'uint256' },
167
+ ],
168
+ },
169
+ {
170
+ type: 'event',
171
+ name: 'InvestmentProposed',
172
+ inputs: [
173
+ { indexed: true, name: 'investmentId', type: 'uint256' },
174
+ { indexed: true, name: 'investor', type: 'address' },
175
+ { indexed: true, name: 'treasury', type: 'address' },
176
+ { indexed: false, name: 'amount', type: 'uint256' },
177
+ ],
178
+ },
179
+ ];
180
+ const USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
181
+ const USDC_ABI = [
182
+ {
183
+ type: 'function',
184
+ name: 'approve',
185
+ inputs: [
186
+ { name: 'spender', type: 'address' },
187
+ { name: 'amount', type: 'uint256' },
188
+ ],
189
+ outputs: [{ name: '', type: 'bool' }],
190
+ stateMutability: 'nonpayable',
191
+ },
192
+ ];
193
+ function parseReturnType(type) {
194
+ const lowerType = type.toLowerCase();
195
+ switch (lowerType) {
196
+ case 'revenue-share':
197
+ return ReturnType.REVENUE_SHARE;
198
+ case 'fixed':
199
+ return ReturnType.FIXED_RETURN;
200
+ case 'milestone':
201
+ return ReturnType.MILESTONE_BASED;
202
+ case 'streaming':
203
+ return ReturnType.STREAMING;
204
+ default:
205
+ throw new Error(`Invalid return type: ${type}. Use: revenue-share, fixed, milestone, or streaming`);
206
+ }
207
+ }
208
+ function registerInvestCommand(program) {
209
+ const invest = program
210
+ .command('invest')
211
+ .description('Investment term sheet operations');
212
+ // plob invest propose
213
+ invest
214
+ .command('propose')
215
+ .description('Propose an investment to a treasury')
216
+ .requiredOption('--treasury <address>', 'Treasury address')
217
+ .requiredOption('--amount <number>', 'Investment amount in USDC')
218
+ .requiredOption('--type <type>', 'Return type: revenue-share, fixed, milestone, or streaming')
219
+ .requiredOption('--duration <days>', 'Investment duration in days')
220
+ .option('--share <bps>', 'Revenue share in basis points (e.g., 1000 = 10%)', '0')
221
+ .option('--fixed-return <bps>', 'Fixed return in basis points', '0')
222
+ .option('--milestones <count>', 'Number of milestones', '0')
223
+ .option('--cliff <days>', 'Cliff period in days', '0')
224
+ .option('--token <address>', 'Token address (default: USDC)', USDC_ADDRESS)
225
+ .option('--liquidation-pref', 'Enable liquidation preference', false)
226
+ .option('--max-investors <bps>', 'Max investors percentage (basis points)', '5000')
227
+ .option('--min-reserve <bps>', 'Min reserve percentage (basis points)', '2000')
228
+ .option('--min-reputation <score>', 'Min reputation score', '0')
229
+ .option('--json', 'Output as JSON')
230
+ .action(async (options) => {
231
+ try {
232
+ const { client, account } = await (0, wallet_1.loadWallet)();
233
+ const publicClient = (0, contracts_1.getPublicClient)();
234
+ const returnType = parseReturnType(options.type);
235
+ const amountBigInt = (0, viem_1.parseUnits)(options.amount, 6); // USDC has 6 decimals
236
+ const durationSeconds = BigInt(parseInt(options.duration) * 86400);
237
+ const cliffSeconds = BigInt(parseInt(options.cliff) * 86400);
238
+ const terms = {
239
+ returnType,
240
+ revShareBps: BigInt(parseInt(options.share)),
241
+ fixedReturnBps: BigInt(parseInt(options.fixedReturn)),
242
+ durationSeconds,
243
+ cliffSeconds,
244
+ milestoneCount: BigInt(parseInt(options.milestones)),
245
+ };
246
+ const protections = {
247
+ liquidationPreference: options.liquidationPref,
248
+ maxInvestorsBps: BigInt(parseInt(options.maxInvestors)),
249
+ minReserveBps: BigInt(parseInt(options.minReserve)),
250
+ minReputationScore: BigInt(parseInt(options.minReputation)),
251
+ };
252
+ // Approve USDC first
253
+ (0, display_1.info)('Approving USDC spend...');
254
+ const approveHash = await client.writeContract({
255
+ address: USDC_ADDRESS,
256
+ abi: USDC_ABI,
257
+ functionName: 'approve',
258
+ args: [INVESTMENT_CONTRACT, amountBigInt],
259
+ account,
260
+ chain: chains_1.base,
261
+ });
262
+ await publicClient.waitForTransactionReceipt({ hash: approveHash });
263
+ const hash = await (0, display_1.withSpinner)('Proposing investment', async () => {
264
+ return await client.writeContract({
265
+ address: INVESTMENT_CONTRACT,
266
+ abi: INVESTMENT_ABI,
267
+ functionName: 'propose',
268
+ args: [options.treasury, options.token, amountBigInt, terms, protections],
269
+ account,
270
+ chain: chains_1.base,
271
+ });
272
+ });
273
+ const receipt = await publicClient.waitForTransactionReceipt({ hash });
274
+ // Parse event to get investment ID
275
+ const proposedEvent = receipt.logs
276
+ .map((log) => {
277
+ try {
278
+ return {
279
+ ...log,
280
+ decoded: (0, viem_1.decodeEventLog)({
281
+ abi: INVESTMENT_ABI,
282
+ data: log.data,
283
+ topics: log.topics,
284
+ }),
285
+ };
286
+ }
287
+ catch {
288
+ return null;
289
+ }
290
+ })
291
+ .find((log) => log?.decoded?.eventName === 'InvestmentProposed');
292
+ const investmentId = proposedEvent?.decoded?.args?.investmentId?.toString() || 'unknown';
293
+ if (options.json) {
294
+ (0, display_1.outputJSON)({
295
+ investmentId,
296
+ transactionHash: hash,
297
+ blockNumber: receipt.blockNumber.toString(),
298
+ });
299
+ }
300
+ else {
301
+ (0, display_1.success)(`Investment proposed!`);
302
+ (0, display_1.info)(`Investment ID: ${chalk_1.default.cyan(investmentId)}`);
303
+ (0, display_1.info)(`Transaction: ${chalk_1.default.gray(hash)}`);
304
+ }
305
+ }
306
+ catch (err) {
307
+ (0, display_1.error)(err instanceof Error ? err.message : 'Failed to propose investment');
308
+ process.exit(1);
309
+ }
310
+ });
311
+ // plob invest fund
312
+ invest
313
+ .command('fund')
314
+ .description('Fund a proposed investment')
315
+ .argument('<id>', 'Investment ID')
316
+ .option('--json', 'Output as JSON')
317
+ .action(async (id, options) => {
318
+ try {
319
+ const { client, account } = await (0, wallet_1.loadWallet)();
320
+ const publicClient = (0, contracts_1.getPublicClient)();
321
+ const hash = await (0, display_1.withSpinner)('Funding investment', async () => {
322
+ return await client.writeContract({
323
+ address: INVESTMENT_CONTRACT,
324
+ abi: INVESTMENT_ABI,
325
+ functionName: 'fund',
326
+ args: [BigInt(id)],
327
+ account,
328
+ chain: chains_1.base,
329
+ });
330
+ });
331
+ const receipt = await publicClient.waitForTransactionReceipt({ hash });
332
+ if (options.json) {
333
+ (0, display_1.outputJSON)({
334
+ investmentId: id,
335
+ transactionHash: hash,
336
+ blockNumber: receipt.blockNumber.toString(),
337
+ });
338
+ }
339
+ else {
340
+ (0, display_1.success)('Investment funded!');
341
+ (0, display_1.info)(`Transaction: ${chalk_1.default.gray(hash)}`);
342
+ }
343
+ }
344
+ catch (err) {
345
+ (0, display_1.error)(err instanceof Error ? err.message : 'Failed to fund investment');
346
+ process.exit(1);
347
+ }
348
+ });
349
+ // plob invest claim
350
+ invest
351
+ .command('claim')
352
+ .description('Claim investment returns')
353
+ .argument('<id>', 'Investment ID')
354
+ .option('--json', 'Output as JSON')
355
+ .action(async (id, options) => {
356
+ try {
357
+ const { client, account } = await (0, wallet_1.loadWallet)();
358
+ const publicClient = (0, contracts_1.getPublicClient)();
359
+ // Get claimable amount first
360
+ const claimable = await publicClient.readContract({
361
+ address: INVESTMENT_CONTRACT,
362
+ abi: INVESTMENT_ABI,
363
+ functionName: 'getClaimable',
364
+ args: [BigInt(id)],
365
+ });
366
+ const hash = await (0, display_1.withSpinner)('Claiming returns', async () => {
367
+ return await client.writeContract({
368
+ address: INVESTMENT_CONTRACT,
369
+ abi: INVESTMENT_ABI,
370
+ functionName: 'claimReturn',
371
+ args: [BigInt(id)],
372
+ account,
373
+ chain: chains_1.base,
374
+ });
375
+ });
376
+ const receipt = await publicClient.waitForTransactionReceipt({ hash });
377
+ if (options.json) {
378
+ (0, display_1.outputJSON)({
379
+ investmentId: id,
380
+ amount: claimable.toString(),
381
+ transactionHash: hash,
382
+ blockNumber: receipt.blockNumber.toString(),
383
+ });
384
+ }
385
+ else {
386
+ (0, display_1.success)(`Claimed ${chalk_1.default.green(parseFloat((Number(claimable) / 1e6).toFixed(2)).toString())} USDC`);
387
+ (0, display_1.info)(`Transaction: ${chalk_1.default.gray(hash)}`);
388
+ }
389
+ }
390
+ catch (err) {
391
+ (0, display_1.error)(err instanceof Error ? err.message : 'Failed to claim returns');
392
+ process.exit(1);
393
+ }
394
+ });
395
+ // plob invest milestone
396
+ invest
397
+ .command('milestone')
398
+ .description('Complete a milestone (oracle only)')
399
+ .argument('<id>', 'Investment ID')
400
+ .option('--json', 'Output as JSON')
401
+ .action(async (id, options) => {
402
+ try {
403
+ const { client, account } = await (0, wallet_1.loadWallet)();
404
+ const publicClient = (0, contracts_1.getPublicClient)();
405
+ const hash = await (0, display_1.withSpinner)('Completing milestone', async () => {
406
+ return await client.writeContract({
407
+ address: INVESTMENT_CONTRACT,
408
+ abi: INVESTMENT_ABI,
409
+ functionName: 'completeMilestone',
410
+ args: [BigInt(id)],
411
+ account,
412
+ chain: chains_1.base,
413
+ });
414
+ });
415
+ const receipt = await publicClient.waitForTransactionReceipt({ hash });
416
+ if (options.json) {
417
+ (0, display_1.outputJSON)({
418
+ investmentId: id,
419
+ transactionHash: hash,
420
+ blockNumber: receipt.blockNumber.toString(),
421
+ });
422
+ }
423
+ else {
424
+ (0, display_1.success)('Milestone completed!');
425
+ (0, display_1.info)(`Transaction: ${chalk_1.default.gray(hash)}`);
426
+ }
427
+ }
428
+ catch (err) {
429
+ (0, display_1.error)(err instanceof Error ? err.message : 'Failed to complete milestone');
430
+ process.exit(1);
431
+ }
432
+ });
433
+ // plob invest default
434
+ invest
435
+ .command('default')
436
+ .description('Trigger default on investment')
437
+ .argument('<id>', 'Investment ID')
438
+ .option('--json', 'Output as JSON')
439
+ .action(async (id, options) => {
440
+ try {
441
+ const { client, account } = await (0, wallet_1.loadWallet)();
442
+ const publicClient = (0, contracts_1.getPublicClient)();
443
+ const hash = await (0, display_1.withSpinner)('Triggering default', async () => {
444
+ return await client.writeContract({
445
+ address: INVESTMENT_CONTRACT,
446
+ abi: INVESTMENT_ABI,
447
+ functionName: 'triggerDefault',
448
+ args: [BigInt(id)],
449
+ account,
450
+ chain: chains_1.base,
451
+ });
452
+ });
453
+ const receipt = await publicClient.waitForTransactionReceipt({ hash });
454
+ if (options.json) {
455
+ (0, display_1.outputJSON)({
456
+ investmentId: id,
457
+ transactionHash: hash,
458
+ blockNumber: receipt.blockNumber.toString(),
459
+ });
460
+ }
461
+ else {
462
+ (0, display_1.success)('Default triggered');
463
+ (0, display_1.info)(`Transaction: ${chalk_1.default.gray(hash)}`);
464
+ }
465
+ }
466
+ catch (err) {
467
+ (0, display_1.error)(err instanceof Error ? err.message : 'Failed to trigger default');
468
+ process.exit(1);
469
+ }
470
+ });
471
+ // plob invest cancel
472
+ invest
473
+ .command('cancel')
474
+ .description('Cancel an unfunded investment')
475
+ .argument('<id>', 'Investment ID')
476
+ .option('--json', 'Output as JSON')
477
+ .action(async (id, options) => {
478
+ try {
479
+ const { client, account } = await (0, wallet_1.loadWallet)();
480
+ const publicClient = (0, contracts_1.getPublicClient)();
481
+ const hash = await (0, display_1.withSpinner)('Cancelling investment', async () => {
482
+ return await client.writeContract({
483
+ address: INVESTMENT_CONTRACT,
484
+ abi: INVESTMENT_ABI,
485
+ functionName: 'cancel',
486
+ args: [BigInt(id)],
487
+ account,
488
+ chain: chains_1.base,
489
+ });
490
+ });
491
+ const receipt = await publicClient.waitForTransactionReceipt({ hash });
492
+ if (options.json) {
493
+ (0, display_1.outputJSON)({
494
+ investmentId: id,
495
+ transactionHash: hash,
496
+ blockNumber: receipt.blockNumber.toString(),
497
+ });
498
+ }
499
+ else {
500
+ (0, display_1.success)('Investment cancelled');
501
+ (0, display_1.info)(`Transaction: ${chalk_1.default.gray(hash)}`);
502
+ }
503
+ }
504
+ catch (err) {
505
+ (0, display_1.error)(err instanceof Error ? err.message : 'Failed to cancel investment');
506
+ process.exit(1);
507
+ }
508
+ });
509
+ // plob invest info
510
+ invest
511
+ .command('info')
512
+ .description('View investment details')
513
+ .argument('<id>', 'Investment ID')
514
+ .option('--json', 'Output as JSON')
515
+ .action(async (id, options) => {
516
+ try {
517
+ const publicClient = (0, contracts_1.getPublicClient)();
518
+ const result = await publicClient.readContract({
519
+ address: INVESTMENT_CONTRACT,
520
+ abi: INVESTMENT_ABI,
521
+ functionName: 'getInvestment',
522
+ args: [BigInt(id)],
523
+ });
524
+ const [investor, treasury, token, investedAmount, returnedAmount, targetReturn, status, createdAt, fundedAt, returnType, durationSeconds, milestonesCompleted, milestoneCount,] = result;
525
+ if (options.json) {
526
+ (0, display_1.outputJSON)({
527
+ investmentId: id,
528
+ investor,
529
+ treasury,
530
+ token,
531
+ investedAmount: investedAmount.toString(),
532
+ returnedAmount: returnedAmount.toString(),
533
+ targetReturn: targetReturn.toString(),
534
+ status: STATUS_NAMES[status],
535
+ createdAt: Number(createdAt),
536
+ fundedAt: Number(fundedAt),
537
+ returnType: RETURN_TYPE_NAMES[returnType],
538
+ durationSeconds: Number(durationSeconds),
539
+ milestonesCompleted: Number(milestonesCompleted),
540
+ milestoneCount: Number(milestoneCount),
541
+ });
542
+ }
543
+ else {
544
+ console.log(chalk_1.default.bold.cyan('\nšŸ“Š Investment Details\n'));
545
+ const table = (0, display_1.createTable)(['Property', 'Value']);
546
+ table.push(['ID', chalk_1.default.white(id)], ['Status', chalk_1.default.yellow(STATUS_NAMES[status])], ['Type', chalk_1.default.cyan(RETURN_TYPE_NAMES[returnType])], ['Investor', (0, display_1.formatAddress)(investor)], ['Treasury', (0, display_1.formatAddress)(treasury)], ['Token', (0, display_1.formatAddress)(token)], ['Invested', `${chalk_1.default.green((Number(investedAmount) / 1e6).toFixed(2))} USDC`], ['Returned', `${chalk_1.default.green((Number(returnedAmount) / 1e6).toFixed(2))} USDC`], ['Target Return', `${chalk_1.default.green((Number(targetReturn) / 1e6).toFixed(2))} USDC`], ['Duration', `${Number(durationSeconds) / 86400} days`], ['Milestones', `${Number(milestonesCompleted)} / ${Number(milestoneCount)}`], ['Created', new Date(Number(createdAt) * 1000).toLocaleDateString()], ['Funded', fundedAt > 0n ? new Date(Number(fundedAt) * 1000).toLocaleDateString() : 'Not funded']);
547
+ console.log(table.toString());
548
+ }
549
+ }
550
+ catch (err) {
551
+ (0, display_1.error)(err instanceof Error ? err.message : 'Failed to get investment info');
552
+ process.exit(1);
553
+ }
554
+ });
555
+ // plob invest portfolio
556
+ invest
557
+ .command('portfolio')
558
+ .description('View investor portfolio')
559
+ .argument('[address]', 'Investor address (default: your address)')
560
+ .option('--json', 'Output as JSON')
561
+ .action(async (address, options) => {
562
+ try {
563
+ const publicClient = (0, contracts_1.getPublicClient)();
564
+ const investor = (address || (await (0, wallet_1.getWalletAddress)()));
565
+ const investmentIds = await publicClient.readContract({
566
+ address: INVESTMENT_CONTRACT,
567
+ abi: INVESTMENT_ABI,
568
+ functionName: 'getInvestorPortfolio',
569
+ args: [investor],
570
+ });
571
+ if (options.json) {
572
+ (0, display_1.outputJSON)({
573
+ investor,
574
+ count: investmentIds.length,
575
+ investments: investmentIds.map(id => id.toString()),
576
+ });
577
+ }
578
+ else {
579
+ console.log(chalk_1.default.bold.cyan(`\nšŸ’¼ Portfolio for ${(0, display_1.formatAddress)(investor)}\n`));
580
+ if (investmentIds.length === 0) {
581
+ (0, display_1.info)('No investments found');
582
+ return;
583
+ }
584
+ const table = (0, display_1.createTable)(['ID', 'Status', 'Invested', 'Returned', 'Type']);
585
+ for (const investmentId of investmentIds) {
586
+ const result = await publicClient.readContract({
587
+ address: INVESTMENT_CONTRACT,
588
+ abi: INVESTMENT_ABI,
589
+ functionName: 'getInvestment',
590
+ args: [investmentId],
591
+ });
592
+ const [, , , investedAmount, returnedAmount, , status, , , returnType] = result;
593
+ table.push([
594
+ chalk_1.default.white(investmentId.toString()),
595
+ chalk_1.default.yellow(STATUS_NAMES[status]),
596
+ `${chalk_1.default.green((Number(investedAmount) / 1e6).toFixed(2))} USDC`,
597
+ `${chalk_1.default.green((Number(returnedAmount) / 1e6).toFixed(2))} USDC`,
598
+ chalk_1.default.cyan(RETURN_TYPE_NAMES[returnType]),
599
+ ]);
600
+ }
601
+ console.log(table.toString());
602
+ (0, display_1.info)(`Total investments: ${investmentIds.length}`);
603
+ }
604
+ }
605
+ catch (err) {
606
+ (0, display_1.error)(err instanceof Error ? err.message : 'Failed to get portfolio');
607
+ process.exit(1);
608
+ }
609
+ });
610
+ // plob invest treasury
611
+ invest
612
+ .command('treasury')
613
+ .description('View treasury investments')
614
+ .argument('[address]', 'Treasury address (default: your treasury)')
615
+ .option('--json', 'Output as JSON')
616
+ .action(async (address, options) => {
617
+ try {
618
+ const publicClient = (0, contracts_1.getPublicClient)();
619
+ let treasury;
620
+ if (address) {
621
+ treasury = address;
622
+ }
623
+ else {
624
+ // Try to get user's treasury from TreasuryFactory
625
+ const owner = await (0, wallet_1.getWalletAddress)();
626
+ treasury = await publicClient.readContract({
627
+ address: '0x171a685f28546a0ebb13059184db1f808b915066',
628
+ abi: [
629
+ {
630
+ type: 'function',
631
+ name: 'treasuries',
632
+ inputs: [{ name: '', type: 'address' }],
633
+ outputs: [{ name: '', type: 'address' }],
634
+ stateMutability: 'view',
635
+ },
636
+ ],
637
+ functionName: 'treasuries',
638
+ args: [owner],
639
+ });
640
+ }
641
+ const investmentIds = await publicClient.readContract({
642
+ address: INVESTMENT_CONTRACT,
643
+ abi: INVESTMENT_ABI,
644
+ functionName: 'getTreasuryInvestments',
645
+ args: [treasury],
646
+ });
647
+ if (options.json) {
648
+ (0, display_1.outputJSON)({
649
+ treasury,
650
+ count: investmentIds.length,
651
+ investments: investmentIds.map(id => id.toString()),
652
+ });
653
+ }
654
+ else {
655
+ console.log(chalk_1.default.bold.cyan(`\nšŸ¦ Treasury Investments: ${(0, display_1.formatAddress)(treasury)}\n`));
656
+ if (investmentIds.length === 0) {
657
+ (0, display_1.info)('No investments found');
658
+ return;
659
+ }
660
+ const table = (0, display_1.createTable)(['ID', 'Status', 'Amount', 'Investor', 'Type']);
661
+ for (const investmentId of investmentIds) {
662
+ const result = await publicClient.readContract({
663
+ address: INVESTMENT_CONTRACT,
664
+ abi: INVESTMENT_ABI,
665
+ functionName: 'getInvestment',
666
+ args: [investmentId],
667
+ });
668
+ const [investor, , , investedAmount, , , status, , , returnType] = result;
669
+ table.push([
670
+ chalk_1.default.white(investmentId.toString()),
671
+ chalk_1.default.yellow(STATUS_NAMES[status]),
672
+ `${chalk_1.default.green((Number(investedAmount) / 1e6).toFixed(2))} USDC`,
673
+ (0, display_1.formatAddress)(investor),
674
+ chalk_1.default.cyan(RETURN_TYPE_NAMES[returnType]),
675
+ ]);
676
+ }
677
+ console.log(table.toString());
678
+ (0, display_1.info)(`Total investments: ${investmentIds.length}`);
679
+ }
680
+ }
681
+ catch (err) {
682
+ (0, display_1.error)(err instanceof Error ? err.message : 'Failed to get treasury investments');
683
+ process.exit(1);
684
+ }
685
+ });
686
+ // plob invest stats
687
+ invest
688
+ .command('stats')
689
+ .description('View protocol-wide investment statistics')
690
+ .option('--json', 'Output as JSON')
691
+ .action(async (options) => {
692
+ try {
693
+ const publicClient = (0, contracts_1.getPublicClient)();
694
+ const [totalInvestments, totalInvested, totalReturned, totalProtocolFees] = await publicClient.readContract({
695
+ address: INVESTMENT_CONTRACT,
696
+ abi: INVESTMENT_ABI,
697
+ functionName: 'getStats',
698
+ args: [],
699
+ });
700
+ if (options.json) {
701
+ (0, display_1.outputJSON)({
702
+ totalInvestments: Number(totalInvestments),
703
+ totalInvested: totalInvested.toString(),
704
+ totalReturned: totalReturned.toString(),
705
+ totalProtocolFees: totalProtocolFees.toString(),
706
+ });
707
+ }
708
+ else {
709
+ console.log(chalk_1.default.bold.cyan('\nšŸ“Š Protocol Investment Statistics\n'));
710
+ const table = (0, display_1.createTable)(['Metric', 'Value']);
711
+ table.push(['Total Investments', chalk_1.default.white(totalInvestments.toString())], ['Total Invested', `${chalk_1.default.green((Number(totalInvested) / 1e6).toFixed(2))} USDC`], ['Total Returned', `${chalk_1.default.green((Number(totalReturned) / 1e6).toFixed(2))} USDC`], ['Protocol Fees', `${chalk_1.default.yellow((Number(totalProtocolFees) / 1e6).toFixed(2))} USDC`], ['Avg Return Rate', `${chalk_1.default.cyan(((Number(totalReturned) / Number(totalInvested)) * 100).toFixed(2))}%`]);
712
+ console.log(table.toString());
713
+ }
714
+ }
715
+ catch (err) {
716
+ (0, display_1.error)(err instanceof Error ? err.message : 'Failed to get stats');
717
+ process.exit(1);
718
+ }
719
+ });
720
+ }
721
+ //# sourceMappingURL=invest.js.map