@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.
- package/README.md +82 -40
- package/dist/src/commands/dashboard.d.ts +3 -0
- package/dist/src/commands/dashboard.d.ts.map +1 -0
- package/dist/src/commands/dashboard.js +67 -0
- package/dist/src/commands/dashboard.js.map +1 -0
- package/dist/src/commands/export.d.ts +3 -0
- package/dist/src/commands/export.d.ts.map +1 -0
- package/dist/src/commands/export.js +97 -0
- package/dist/src/commands/export.js.map +1 -0
- package/dist/src/commands/health.d.ts +3 -0
- package/dist/src/commands/health.d.ts.map +1 -0
- package/dist/src/commands/health.js +165 -0
- package/dist/src/commands/health.js.map +1 -0
- package/dist/src/commands/invest.d.ts +3 -0
- package/dist/src/commands/invest.d.ts.map +1 -0
- package/dist/src/commands/invest.js +721 -0
- package/dist/src/commands/invest.js.map +1 -0
- package/dist/src/commands/quickstart.d.ts +3 -0
- package/dist/src/commands/quickstart.d.ts.map +1 -0
- package/dist/src/commands/quickstart.js +138 -0
- package/dist/src/commands/quickstart.js.map +1 -0
- package/dist/src/commands/search.d.ts +3 -0
- package/dist/src/commands/search.d.ts.map +1 -0
- package/dist/src/commands/search.js +126 -0
- package/dist/src/commands/search.js.map +1 -0
- package/dist/src/commands/treasury.d.ts.map +1 -1
- package/dist/src/commands/treasury.js +317 -0
- package/dist/src/commands/treasury.js.map +1 -1
- package/dist/src/commands/trust-graph.d.ts +3 -0
- package/dist/src/commands/trust-graph.d.ts.map +1 -0
- package/dist/src/commands/trust-graph.js +282 -0
- package/dist/src/commands/trust-graph.js.map +1 -0
- package/dist/src/commands/whoami.d.ts +3 -0
- package/dist/src/commands/whoami.d.ts.map +1 -0
- package/dist/src/commands/whoami.js +160 -0
- package/dist/src/commands/whoami.js.map +1 -0
- package/dist/src/index.js +18 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/lib/contracts.d.ts +177 -23
- package/dist/src/lib/contracts.d.ts.map +1 -1
- package/dist/src/lib/contracts.js +151 -0
- package/dist/src/lib/contracts.js.map +1 -1
- package/package.json +4 -2
- package/src/commands/dashboard.ts +69 -0
- package/src/commands/export.ts +132 -0
- package/src/commands/health.ts +212 -0
- package/src/commands/invest.ts +810 -0
- package/src/commands/quickstart.ts +150 -0
- package/src/commands/search.ts +151 -0
- package/src/commands/treasury.ts +385 -0
- package/src/commands/trust-graph.ts +267 -0
- package/src/commands/whoami.ts +172 -0
- package/src/index.ts +18 -2
- 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
|