@sherwoodagent/cli 0.15.3 → 0.17.2

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/dist/index.js CHANGED
@@ -1,4 +1,35 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ PROPOSAL_STATE,
4
+ PROPOSAL_STATES,
5
+ VOTE_TYPE,
6
+ cancelProposal,
7
+ emergencyCancel,
8
+ emergencySettle,
9
+ executeProposal,
10
+ getCapitalSnapshot,
11
+ getExecuteCalls,
12
+ getGovernorAddress,
13
+ getGovernorParams,
14
+ getProposal,
15
+ getProposalState,
16
+ getRegisteredVaults,
17
+ getSettlementCalls,
18
+ getVoteWeight,
19
+ hasVoted,
20
+ parseDuration,
21
+ proposalCount,
22
+ propose,
23
+ setCooldownPeriod,
24
+ setExecutionWindow,
25
+ setMaxPerformanceFeeBps,
26
+ setMaxStrategyDuration,
27
+ setProtocolFeeBps,
28
+ setVetoThresholdBps,
29
+ setVotingPeriod,
30
+ settleProposal,
31
+ vote
32
+ } from "./chunk-4GTOZMVG.js";
2
33
  import {
3
34
  fetchMetadata,
4
35
  uploadMetadata
@@ -10,7 +41,7 @@ import {
10
41
  queryApprovals,
11
42
  queryJoinRequests,
12
43
  revokeAttestation
13
- } from "./chunk-AK7BD7KH.js";
44
+ } from "./chunk-RAFAIAIE.js";
14
45
  import {
15
46
  approveDepositor,
16
47
  deposit,
@@ -26,23 +57,24 @@ import {
26
57
  resolveVaultSyndicate,
27
58
  setTextRecord,
28
59
  setVaultAddress
29
- } from "./chunk-LACUMLU4.js";
60
+ } from "./chunk-E7KKGN3V.js";
30
61
  import {
62
+ AERODROME,
31
63
  AGENT_REGISTRY,
64
+ BASE_STRATEGY_ABI,
32
65
  EAS_SCHEMAS,
33
66
  ERC20_ABI,
34
67
  MOONWELL,
35
68
  SHERWOOD,
36
- STRATEGY_REGISTRY_ABI,
69
+ STRATEGY_TEMPLATES,
37
70
  SYNDICATE_FACTORY_ABI,
38
- SYNDICATE_GOVERNOR_ABI,
39
71
  SYNDICATE_VAULT_ABI,
40
72
  TOKENS,
41
73
  UNISWAP,
42
74
  UNISWAP_QUOTER_V2_ABI,
43
75
  VENICE,
44
76
  VENICE_STAKING_ABI
45
- } from "./chunk-GC5J54OC.js";
77
+ } from "./chunk-E3UCNLU3.js";
46
78
  import {
47
79
  getAccount,
48
80
  getPublicClient,
@@ -76,7 +108,7 @@ import {
76
108
  import { config as loadDotenv } from "dotenv";
77
109
  import { createRequire } from "module";
78
110
  import { Command, Option } from "commander";
79
- import { parseUnits as parseUnits8, isAddress as isAddress5 } from "viem";
111
+ import { parseUnits as parseUnits7, isAddress as isAddress5 } from "viem";
80
112
  import chalk7 from "chalk";
81
113
  import ora7 from "ora";
82
114
  import { input, confirm, select } from "@inquirer/prompts";
@@ -137,342 +169,717 @@ var UniswapProvider = class {
137
169
  }
138
170
  };
139
171
 
140
- // src/commands/strategy-run.ts
141
- import { parseUnits as parseUnits2, formatUnits, isAddress } from "viem";
172
+ // src/commands/strategy-template.ts
173
+ import { parseUnits, isAddress, erc20Abi } from "viem";
142
174
  import chalk from "chalk";
143
175
  import ora from "ora";
176
+ import { writeFileSync, mkdirSync } from "fs";
177
+ import { resolve } from "path";
144
178
 
145
- // src/strategies/levered-swap.ts
146
- import {
147
- encodeFunctionData,
148
- parseUnits,
149
- parseEther
150
- } from "viem";
151
- var ERC20_ABI2 = [
152
- {
153
- name: "approve",
154
- type: "function",
155
- inputs: [
156
- { name: "spender", type: "address" },
157
- { name: "amount", type: "uint256" }
158
- ],
159
- outputs: [{ name: "", type: "bool" }]
160
- }
161
- ];
162
- var MTOKEN_ABI = [
163
- {
164
- name: "mint",
165
- type: "function",
166
- inputs: [{ name: "mintAmount", type: "uint256" }],
167
- outputs: [{ name: "", type: "uint256" }]
168
- },
169
- {
170
- name: "borrow",
171
- type: "function",
172
- inputs: [{ name: "borrowAmount", type: "uint256" }],
173
- outputs: [{ name: "", type: "uint256" }]
174
- },
175
- {
176
- name: "repayBorrow",
177
- type: "function",
178
- inputs: [{ name: "repayAmount", type: "uint256" }],
179
- outputs: [{ name: "", type: "uint256" }]
180
- },
181
- {
182
- name: "redeemUnderlying",
183
- type: "function",
184
- inputs: [{ name: "redeemAmount", type: "uint256" }],
185
- outputs: [{ name: "", type: "uint256" }]
186
- }
187
- ];
188
- var COMPTROLLER_ABI = [
189
- {
190
- name: "enterMarkets",
191
- type: "function",
192
- inputs: [{ name: "mTokens", type: "address[]" }],
193
- outputs: [{ name: "", type: "uint256[]" }]
179
+ // src/lib/clone.ts
180
+ import { concat } from "viem";
181
+ async function cloneTemplate(template) {
182
+ const creationCode = concat([
183
+ "0x3d602d80600a3d3981f3363d3d373d3d3d363d73",
184
+ template,
185
+ "0x5af43d82803e903d91602b57fd5bf3"
186
+ ]);
187
+ const wallet = getWalletClient();
188
+ const account = getAccount();
189
+ const chain = getChain();
190
+ const hash = await wallet.sendTransaction({
191
+ account,
192
+ chain,
193
+ data: creationCode,
194
+ value: 0n
195
+ });
196
+ const receipt = await getPublicClient().waitForTransactionReceipt({ hash });
197
+ if (!receipt.contractAddress) {
198
+ throw new Error(`Clone deployment failed \u2014 no contract address in receipt (tx: ${hash})`);
194
199
  }
195
- ];
196
- var SWAP_ROUTER_ABI = [
197
- {
198
- name: "exactInputSingle",
199
- type: "function",
200
- inputs: [
201
- {
202
- name: "params",
203
- type: "tuple",
204
- components: [
205
- { name: "tokenIn", type: "address" },
206
- { name: "tokenOut", type: "address" },
207
- { name: "fee", type: "uint24" },
208
- { name: "recipient", type: "address" },
209
- { name: "amountIn", type: "uint256" },
210
- { name: "amountOutMinimum", type: "uint256" },
211
- { name: "sqrtPriceLimitX96", type: "uint160" }
212
- ]
213
- }
200
+ return { clone: receipt.contractAddress, hash };
201
+ }
202
+
203
+ // src/lib/batch.ts
204
+ function formatBatch(calls) {
205
+ return calls.map((call, i) => {
206
+ const selector = call.data.slice(0, 10);
207
+ return ` ${i + 1}. ${call.target} :: ${selector}... (${call.value > 0n ? call.value + " wei" : "no value"})`;
208
+ }).join("\n");
209
+ }
210
+
211
+ // src/strategies/moonwell-supply-template.ts
212
+ import { encodeAbiParameters, encodeFunctionData } from "viem";
213
+ function buildInitData(underlying, mToken, supplyAmount, minRedeemAmount) {
214
+ return encodeAbiParameters(
215
+ [
216
+ { type: "address" },
217
+ { type: "address" },
218
+ { type: "uint256" },
219
+ { type: "uint256" }
214
220
  ],
215
- outputs: [{ name: "amountOut", type: "uint256" }]
216
- }
217
- ];
218
- function buildEntryBatch(config, vaultAddress, amountOutMinimum, borrowDecimals) {
219
- const ZERO = "0x0000000000000000000000000000000000000000";
220
- if (MOONWELL().mWETH === ZERO || MOONWELL().mUSDC === ZERO || MOONWELL().COMPTROLLER === ZERO) {
221
- throw new Error("Moonwell is not deployed on this network \u2014 levered swap requires Moonwell lending markets");
222
- }
223
- const collateral = parseEther(config.collateralAmount);
224
- const borrow = parseUnits(config.borrowAmount, borrowDecimals);
225
- const calls = [
226
- // 1. Approve mWETH to pull WETH from executor
221
+ [underlying, mToken, supplyAmount, minRedeemAmount]
222
+ );
223
+ }
224
+ function buildExecuteCalls(clone, underlying, supplyAmount) {
225
+ return [
227
226
  {
228
- target: TOKENS().WETH,
227
+ target: underlying,
229
228
  data: encodeFunctionData({
230
- abi: ERC20_ABI2,
229
+ abi: ERC20_ABI,
231
230
  functionName: "approve",
232
- args: [MOONWELL().mWETH, collateral]
231
+ args: [clone, supplyAmount]
233
232
  }),
234
233
  value: 0n
235
234
  },
236
- // 2. Deposit WETH as collateral (mint mWETH tokens)
237
235
  {
238
- target: MOONWELL().mWETH,
236
+ target: clone,
239
237
  data: encodeFunctionData({
240
- abi: MTOKEN_ABI,
241
- functionName: "mint",
242
- args: [collateral]
238
+ abi: BASE_STRATEGY_ABI,
239
+ functionName: "execute"
243
240
  }),
244
241
  value: 0n
245
- },
246
- // 3. Enter WETH market (enable as collateral for borrowing)
242
+ }
243
+ ];
244
+ }
245
+ function buildSettleCalls(clone) {
246
+ return [
247
247
  {
248
- target: MOONWELL().COMPTROLLER,
248
+ target: clone,
249
249
  data: encodeFunctionData({
250
- abi: COMPTROLLER_ABI,
251
- functionName: "enterMarkets",
252
- args: [[MOONWELL().mWETH]]
250
+ abi: BASE_STRATEGY_ABI,
251
+ functionName: "settle"
253
252
  }),
254
253
  value: 0n
255
- },
256
- // 4. Borrow USDC against WETH collateral
254
+ }
255
+ ];
256
+ }
257
+
258
+ // src/strategies/venice-inference-template.ts
259
+ import { encodeAbiParameters as encodeAbiParameters2, encodeFunctionData as encodeFunctionData2 } from "viem";
260
+ var INIT_PARAMS_TYPES = [
261
+ {
262
+ type: "tuple",
263
+ components: [
264
+ { name: "asset", type: "address" },
265
+ { name: "weth", type: "address" },
266
+ { name: "vvv", type: "address" },
267
+ { name: "sVVV", type: "address" },
268
+ { name: "aeroRouter", type: "address" },
269
+ { name: "aeroFactory", type: "address" },
270
+ { name: "agent", type: "address" },
271
+ { name: "assetAmount", type: "uint256" },
272
+ { name: "minVVV", type: "uint256" },
273
+ { name: "deadlineOffset", type: "uint256" },
274
+ { name: "singleHop", type: "bool" }
275
+ ]
276
+ }
277
+ ];
278
+ function buildInitData2(params) {
279
+ return encodeAbiParameters2(INIT_PARAMS_TYPES, [params]);
280
+ }
281
+ function buildExecuteCalls2(clone, asset, assetAmount) {
282
+ return [
257
283
  {
258
- target: MOONWELL().mUSDC,
259
- data: encodeFunctionData({
260
- abi: MTOKEN_ABI,
261
- functionName: "borrow",
262
- args: [borrow]
284
+ target: asset,
285
+ data: encodeFunctionData2({
286
+ abi: ERC20_ABI,
287
+ functionName: "approve",
288
+ args: [clone, assetAmount]
263
289
  }),
264
290
  value: 0n
265
291
  },
266
- // 5. Approve SwapRouter to pull borrowed USDC
267
292
  {
268
- target: TOKENS().USDC,
269
- data: encodeFunctionData({
270
- abi: ERC20_ABI2,
271
- functionName: "approve",
272
- args: [UNISWAP().SWAP_ROUTER, borrow]
293
+ target: clone,
294
+ data: encodeFunctionData2({
295
+ abi: BASE_STRATEGY_ABI,
296
+ functionName: "execute"
273
297
  }),
274
298
  value: 0n
275
- },
276
- // 6. Swap USDC → target token
299
+ }
300
+ ];
301
+ }
302
+ function buildSettleCalls2(clone) {
303
+ return [
277
304
  {
278
- target: UNISWAP().SWAP_ROUTER,
279
- data: encodeFunctionData({
280
- abi: SWAP_ROUTER_ABI,
281
- functionName: "exactInputSingle",
282
- args: [
283
- {
284
- tokenIn: TOKENS().USDC,
285
- tokenOut: config.targetToken,
286
- fee: config.fee,
287
- recipient: vaultAddress,
288
- // Tokens stay in vault (delegatecall)
289
- amountIn: borrow,
290
- amountOutMinimum,
291
- sqrtPriceLimitX96: 0n
292
- }
293
- ]
305
+ target: clone,
306
+ data: encodeFunctionData2({
307
+ abi: BASE_STRATEGY_ABI,
308
+ functionName: "settle"
294
309
  }),
295
310
  value: 0n
296
311
  }
297
312
  ];
298
- return calls;
299
313
  }
300
314
 
301
- // src/lib/quote.ts
302
- import { encodeFunctionData as encodeFunctionData2, decodeFunctionResult, concat, pad, numberToHex } from "viem";
303
- async function getQuote(params) {
304
- const client = getPublicClient();
305
- const calldata = encodeFunctionData2({
306
- abi: UNISWAP_QUOTER_V2_ABI,
307
- functionName: "quoteExactInputSingle",
308
- args: [
309
- {
310
- tokenIn: params.tokenIn,
311
- tokenOut: params.tokenOut,
312
- amountIn: params.amountIn,
313
- fee: params.fee,
314
- sqrtPriceLimitX96: 0n
315
- }
315
+ // src/strategies/aerodrome-lp-template.ts
316
+ import { encodeAbiParameters as encodeAbiParameters3, encodeFunctionData as encodeFunctionData3 } from "viem";
317
+ var INIT_PARAMS_TYPES2 = [
318
+ {
319
+ type: "tuple",
320
+ components: [
321
+ { name: "tokenA", type: "address" },
322
+ { name: "tokenB", type: "address" },
323
+ { name: "stable", type: "bool" },
324
+ { name: "factory", type: "address" },
325
+ { name: "router", type: "address" },
326
+ { name: "gauge", type: "address" },
327
+ { name: "lpToken", type: "address" },
328
+ { name: "amountADesired", type: "uint256" },
329
+ { name: "amountBDesired", type: "uint256" },
330
+ { name: "amountAMin", type: "uint256" },
331
+ { name: "amountBMin", type: "uint256" },
332
+ { name: "minAmountAOut", type: "uint256" },
333
+ { name: "minAmountBOut", type: "uint256" }
316
334
  ]
317
- });
318
- const { data } = await client.call({
319
- to: UNISWAP().QUOTER_V2,
320
- data: calldata
321
- });
322
- if (!data) {
323
- throw new Error("Quoter returned no data \u2014 pool may not exist for this pair/fee");
324
335
  }
325
- const [amountOut, sqrtPriceX96After, , gasEstimate] = decodeFunctionResult({
326
- abi: UNISWAP_QUOTER_V2_ABI,
327
- functionName: "quoteExactInputSingle",
328
- data
329
- });
330
- return { amountOut, sqrtPriceX96After, gasEstimate };
336
+ ];
337
+ function buildInitData3(params) {
338
+ return encodeAbiParameters3(INIT_PARAMS_TYPES2, [params]);
331
339
  }
332
- function applySlippage(amountOut, slippageBps) {
333
- return amountOut * BigInt(1e4 - slippageBps) / 10000n;
340
+ function buildExecuteCalls3(clone, tokenA, amountA, tokenB, amountB) {
341
+ return [
342
+ {
343
+ target: tokenA,
344
+ data: encodeFunctionData3({
345
+ abi: ERC20_ABI,
346
+ functionName: "approve",
347
+ args: [clone, amountA]
348
+ }),
349
+ value: 0n
350
+ },
351
+ {
352
+ target: tokenB,
353
+ data: encodeFunctionData3({
354
+ abi: ERC20_ABI,
355
+ functionName: "approve",
356
+ args: [clone, amountB]
357
+ }),
358
+ value: 0n
359
+ },
360
+ {
361
+ target: clone,
362
+ data: encodeFunctionData3({
363
+ abi: BASE_STRATEGY_ABI,
364
+ functionName: "execute"
365
+ }),
366
+ value: 0n
367
+ }
368
+ ];
334
369
  }
335
- function encodeSwapPath(tokens, fees) {
336
- if (tokens.length < 2 || fees.length !== tokens.length - 1) {
337
- throw new Error("Invalid path: need at least 2 tokens and (tokens-1) fees");
338
- }
339
- const parts = [];
340
- for (let i = 0; i < tokens.length; i++) {
341
- parts.push(tokens[i].toLowerCase());
342
- if (i < fees.length) {
343
- parts.push(pad(numberToHex(fees[i]), { size: 3 }));
370
+ function buildSettleCalls3(clone) {
371
+ return [
372
+ {
373
+ target: clone,
374
+ data: encodeFunctionData3({
375
+ abi: BASE_STRATEGY_ABI,
376
+ functionName: "settle"
377
+ }),
378
+ value: 0n
344
379
  }
345
- }
346
- return concat(parts);
380
+ ];
347
381
  }
348
- async function getMultiHopQuote(params) {
349
- const client = getPublicClient();
350
- const calldata = encodeFunctionData2({
351
- abi: UNISWAP_QUOTER_V2_ABI,
352
- functionName: "quoteExactInput",
353
- args: [params.path, params.amountIn]
354
- });
355
- const { data } = await client.call({
356
- to: UNISWAP().QUOTER_V2,
357
- data: calldata
358
- });
359
- if (!data) {
360
- throw new Error("Quoter returned no data \u2014 pool may not exist for this path");
382
+
383
+ // src/strategies/wsteth-moonwell-template.ts
384
+ import { encodeAbiParameters as encodeAbiParameters4, encodeFunctionData as encodeFunctionData4 } from "viem";
385
+ var INIT_PARAMS_TYPES3 = [
386
+ {
387
+ type: "tuple",
388
+ components: [
389
+ { name: "weth", type: "address" },
390
+ { name: "wsteth", type: "address" },
391
+ { name: "mwsteth", type: "address" },
392
+ { name: "aeroRouter", type: "address" },
393
+ { name: "aeroFactory", type: "address" },
394
+ { name: "supplyAmount", type: "uint256" },
395
+ { name: "minWstethOut", type: "uint256" },
396
+ { name: "minWethOut", type: "uint256" },
397
+ { name: "deadlineOffset", type: "uint256" }
398
+ ]
361
399
  }
362
- const [amountOut, , , gasEstimate] = decodeFunctionResult({
363
- abi: UNISWAP_QUOTER_V2_ABI,
364
- functionName: "quoteExactInput",
365
- data
366
- });
367
- return { amountOut, sqrtPriceX96After: 0n, gasEstimate };
400
+ ];
401
+ function buildInitData4(params) {
402
+ return encodeAbiParameters4(INIT_PARAMS_TYPES3, [params]);
368
403
  }
369
-
370
- // src/lib/batch.ts
371
- function formatBatch(calls) {
372
- return calls.map((call, i) => {
373
- const selector = call.data.slice(0, 10);
374
- return ` ${i + 1}. ${call.target} :: ${selector}... (${call.value > 0n ? call.value + " wei" : "no value"})`;
375
- }).join("\n");
404
+ function buildExecuteCalls4(clone, weth, supplyAmount) {
405
+ return [
406
+ {
407
+ target: weth,
408
+ data: encodeFunctionData4({
409
+ abi: ERC20_ABI,
410
+ functionName: "approve",
411
+ args: [clone, supplyAmount]
412
+ }),
413
+ value: 0n
414
+ },
415
+ {
416
+ target: clone,
417
+ data: encodeFunctionData4({
418
+ abi: BASE_STRATEGY_ABI,
419
+ functionName: "execute"
420
+ }),
421
+ value: 0n
422
+ }
423
+ ];
424
+ }
425
+ function buildSettleCalls4(clone) {
426
+ return [
427
+ {
428
+ target: clone,
429
+ data: encodeFunctionData4({
430
+ abi: BASE_STRATEGY_ABI,
431
+ functionName: "settle"
432
+ }),
433
+ value: 0n
434
+ }
435
+ ];
376
436
  }
377
437
 
378
- // src/commands/strategy-run.ts
379
- var VALID_FEES = [500, 3e3, 1e4];
380
- async function runLeveredSwap(opts) {
381
- const vaultAddress = opts.vault;
382
- if (!isAddress(opts.token)) {
383
- console.error(chalk.red(`Invalid token address: ${opts.token}`));
438
+ // src/commands/strategy-template.ts
439
+ var ZERO = "0x0000000000000000000000000000000000000000";
440
+ var TEMPLATES = [
441
+ {
442
+ name: "Moonwell Supply",
443
+ key: "moonwell-supply",
444
+ description: "Supply tokens to Moonwell lending market, earn yield",
445
+ addressKey: "MOONWELL_SUPPLY"
446
+ },
447
+ {
448
+ name: "Aerodrome LP",
449
+ key: "aerodrome-lp",
450
+ description: "Provide liquidity on Aerodrome DEX + optional gauge staking",
451
+ addressKey: "AERODROME_LP"
452
+ },
453
+ {
454
+ name: "Venice Inference",
455
+ key: "venice-inference",
456
+ description: "Stake VVV for sVVV \u2014 Venice private AI inference",
457
+ addressKey: "VENICE_INFERENCE"
458
+ },
459
+ {
460
+ name: "wstETH Moonwell Yield",
461
+ key: "wsteth-moonwell",
462
+ description: "WETH \u2192 wstETH \u2192 Moonwell \u2014 stack Lido + lending yield",
463
+ addressKey: "WSTETH_MOONWELL"
464
+ }
465
+ ];
466
+ function resolveTemplate(key) {
467
+ const def = TEMPLATES.find((t) => t.key === key);
468
+ if (!def) {
469
+ console.error(chalk.red(`Unknown template: ${key}`));
470
+ console.error(chalk.dim(`Available: ${TEMPLATES.map((t) => t.key).join(", ")}`));
384
471
  process.exit(1);
385
472
  }
386
- const targetToken = opts.token;
387
- const feeTier = Number(opts.fee);
388
- if (!VALID_FEES.includes(feeTier)) {
389
- console.error(chalk.red(`Invalid fee tier: ${opts.fee}. Valid: ${VALID_FEES.join(", ")}`));
473
+ const address = STRATEGY_TEMPLATES()[def.addressKey];
474
+ if (address === ZERO) {
475
+ console.error(chalk.red(`Template "${def.name}" not deployed on this network.`));
390
476
  process.exit(1);
391
477
  }
392
- const slippageBps = Number(opts.slippage);
393
- const client = getPublicClient();
394
- let targetDecimals;
395
- let borrowDecimals;
396
- try {
397
- [targetDecimals, borrowDecimals] = await Promise.all([
398
- client.readContract({
399
- address: targetToken,
400
- abi: ERC20_ABI,
401
- functionName: "decimals"
402
- }),
403
- client.readContract({
404
- address: TOKENS().USDC,
405
- abi: ERC20_ABI,
406
- functionName: "decimals"
407
- })
478
+ return { def, address };
479
+ }
480
+ async function buildInitDataForTemplate(templateKey, opts, vault) {
481
+ if (templateKey === "moonwell-supply") {
482
+ if (!opts.amount) {
483
+ console.error(chalk.red("--amount is required for moonwell-supply template"));
484
+ process.exit(1);
485
+ }
486
+ const token = opts.token || "USDC";
487
+ const underlying = resolveToken(token);
488
+ const mToken = resolveMToken(token);
489
+ const decimals = token.toUpperCase() === "USDC" ? 6 : 18;
490
+ const supplyAmount = parseUnits(opts.amount, decimals);
491
+ const minRedeem = parseUnits(opts.minRedeem || opts.amount, decimals);
492
+ return {
493
+ initData: buildInitData(underlying, mToken, supplyAmount, minRedeem),
494
+ asset: underlying,
495
+ assetAmount: supplyAmount
496
+ };
497
+ }
498
+ if (templateKey === "venice-inference") {
499
+ if (!opts.amount) {
500
+ console.error(chalk.red("--amount is required for venice-inference template"));
501
+ process.exit(1);
502
+ }
503
+ const assetSymbol = opts.asset || "USDC";
504
+ const asset = resolveToken(assetSymbol);
505
+ const vvv = VENICE().VVV;
506
+ const isDirect = asset.toLowerCase() === vvv.toLowerCase();
507
+ const decimals = assetSymbol.toUpperCase() === "USDC" ? 6 : 18;
508
+ const assetAmount = parseUnits(opts.amount, decimals);
509
+ const agent = opts.agent || getAccount().address;
510
+ const params = {
511
+ asset,
512
+ weth: isDirect ? ZERO : TOKENS().WETH,
513
+ vvv,
514
+ sVVV: VENICE().STAKING,
515
+ aeroRouter: isDirect ? ZERO : AERODROME().ROUTER,
516
+ aeroFactory: isDirect ? ZERO : AERODROME().FACTORY,
517
+ agent,
518
+ assetAmount,
519
+ minVVV: isDirect ? 0n : parseUnits(opts.minVvv || "0", 18),
520
+ deadlineOffset: 300n,
521
+ singleHop: !!opts.singleHop
522
+ };
523
+ return {
524
+ initData: buildInitData2(params),
525
+ asset,
526
+ assetAmount
527
+ };
528
+ }
529
+ if (templateKey === "aerodrome-lp") {
530
+ for (const flag of ["tokenA", "tokenB", "amountA", "amountB", "lpToken"]) {
531
+ if (!opts[flag]) {
532
+ console.error(chalk.red(`--${flag.replace(/([A-Z])/g, "-$1").toLowerCase()} is required for aerodrome-lp template`));
533
+ process.exit(1);
534
+ }
535
+ }
536
+ const tokenA = opts.tokenA;
537
+ const tokenB = opts.tokenB;
538
+ const publicClient = getPublicClient();
539
+ const [decimalsA, decimalsB] = await Promise.all([
540
+ publicClient.readContract({ address: tokenA, abi: erc20Abi, functionName: "decimals" }),
541
+ publicClient.readContract({ address: tokenB, abi: erc20Abi, functionName: "decimals" })
408
542
  ]);
409
- } catch {
410
- console.error(chalk.red(`Could not read token decimals \u2014 are the addresses valid ERC20s?`));
411
- process.exit(1);
543
+ const amountA = parseUnits(opts.amountA, decimalsA);
544
+ const amountB = parseUnits(opts.amountB, decimalsB);
545
+ const minAOut = parseUnits(opts.minAOut || "0", decimalsA);
546
+ const minBOut = parseUnits(opts.minBOut || "0", decimalsB);
547
+ const params = {
548
+ tokenA,
549
+ tokenB,
550
+ stable: !!opts.stable,
551
+ factory: AERODROME().FACTORY,
552
+ router: AERODROME().ROUTER,
553
+ gauge: opts.gauge || ZERO,
554
+ lpToken: opts.lpToken,
555
+ amountADesired: amountA,
556
+ amountBDesired: amountB,
557
+ amountAMin: amountA,
558
+ // use desired as min for now
559
+ amountBMin: amountB,
560
+ minAmountAOut: minAOut,
561
+ minAmountBOut: minBOut
562
+ };
563
+ return {
564
+ initData: buildInitData3(params),
565
+ asset: tokenA,
566
+ assetAmount: amountA,
567
+ extraApprovals: [{ token: tokenB, amount: amountB }]
568
+ };
412
569
  }
413
- console.log();
414
- console.log(chalk.bold("Levered Swap Strategy"));
415
- console.log(chalk.dim("\u2500".repeat(40)));
416
- console.log(` Collateral: ${opts.collateral} WETH (agent-provided)`);
417
- console.log(` Borrow: ${opts.borrow} USDC (from Moonwell)`);
418
- console.log(` Buy: ${targetToken} (${targetDecimals} decimals)`);
419
- console.log(` Fee tier: ${(feeTier / 1e4 * 100).toFixed(2)}%`);
420
- console.log(` Slippage: ${(slippageBps / 100).toFixed(2)}%`);
421
- console.log(` Vault: ${vaultAddress}`);
422
- console.log();
423
- const spinner = ora("Fetching Uniswap quote...").start();
424
- let amountOut;
425
- let minOut;
426
- try {
427
- const borrowAmount = parseUnits2(opts.borrow, borrowDecimals);
428
- const quote = await getQuote({
429
- tokenIn: TOKENS().USDC,
430
- tokenOut: targetToken,
431
- amountIn: borrowAmount,
432
- fee: feeTier
433
- });
434
- amountOut = quote.amountOut;
435
- minOut = applySlippage(amountOut, slippageBps);
436
- spinner.succeed(
437
- `Quote: ${formatUnits(amountOut, targetDecimals)} tokens (min: ${formatUnits(minOut, targetDecimals)}, gas est: ${quote.gasEstimate})`
438
- );
439
- } catch (err) {
440
- spinner.fail("Failed to fetch quote");
441
- console.error(chalk.red(err instanceof Error ? err.message : String(err)));
442
- process.exit(1);
570
+ if (templateKey === "wsteth-moonwell") {
571
+ if (!opts.amount) {
572
+ console.error(chalk.red("--amount is required for wsteth-moonwell template"));
573
+ process.exit(1);
574
+ }
575
+ const supplyAmount = parseUnits(opts.amount, 18);
576
+ const slippageBps = BigInt(opts.slippage || "500");
577
+ const minWstethOut = supplyAmount - supplyAmount * slippageBps / 10000n;
578
+ const minWethOut = supplyAmount - supplyAmount * slippageBps / 10000n;
579
+ const params = {
580
+ weth: TOKENS().WETH,
581
+ wsteth: TOKENS().wstETH,
582
+ mwsteth: MOONWELL().mWstETH,
583
+ aeroRouter: AERODROME().ROUTER,
584
+ aeroFactory: AERODROME().FACTORY,
585
+ supplyAmount,
586
+ minWstethOut,
587
+ minWethOut,
588
+ deadlineOffset: 300n
589
+ };
590
+ return {
591
+ initData: buildInitData4(params),
592
+ asset: TOKENS().WETH,
593
+ assetAmount: supplyAmount
594
+ };
443
595
  }
444
- const config = {
445
- collateralAmount: opts.collateral,
446
- borrowAmount: opts.borrow,
447
- targetToken,
448
- fee: feeTier,
449
- slippageBps,
450
- profitTargetBps: 2e3,
451
- // 20% default
452
- stopLossBps: 1e3
453
- // 10% default
596
+ throw new Error(`No init builder for template: ${templateKey}`);
597
+ }
598
+ function buildCallsForTemplate(templateKey, clone, asset, assetAmount, extraApprovals) {
599
+ if (templateKey === "moonwell-supply") {
600
+ return {
601
+ executeCalls: buildExecuteCalls(clone, asset, assetAmount),
602
+ settleCalls: buildSettleCalls(clone)
603
+ };
604
+ }
605
+ if (templateKey === "venice-inference") {
606
+ return {
607
+ executeCalls: buildExecuteCalls2(clone, asset, assetAmount),
608
+ settleCalls: buildSettleCalls2(clone)
609
+ };
610
+ }
611
+ if (templateKey === "aerodrome-lp") {
612
+ const tokenB = extraApprovals?.[0]?.token ?? ZERO;
613
+ const amountB = extraApprovals?.[0]?.amount ?? 0n;
614
+ return {
615
+ executeCalls: buildExecuteCalls3(clone, asset, assetAmount, tokenB, amountB),
616
+ settleCalls: buildSettleCalls3(clone)
617
+ };
618
+ }
619
+ if (templateKey === "wsteth-moonwell") {
620
+ return {
621
+ executeCalls: buildExecuteCalls4(clone, asset, assetAmount),
622
+ settleCalls: buildSettleCalls4(clone)
623
+ };
624
+ }
625
+ throw new Error(`No call builder for template: ${templateKey}`);
626
+ }
627
+ function resolveToken(symbolOrAddress) {
628
+ if (isAddress(symbolOrAddress)) return symbolOrAddress;
629
+ const upper = symbolOrAddress.toUpperCase();
630
+ const tokens = TOKENS();
631
+ const tokenMap = {
632
+ USDC: tokens.USDC,
633
+ WETH: tokens.WETH,
634
+ DAI: tokens.DAI,
635
+ AERO: tokens.AERO,
636
+ VVV: VENICE().VVV
454
637
  };
455
- const calls = buildEntryBatch(config, vaultAddress, minOut, borrowDecimals);
456
- console.log();
457
- console.log(chalk.bold("Batch calls (6):"));
458
- console.log(formatBatch(calls));
459
- console.log();
460
- if (!opts.execute) {
461
- console.log();
462
- console.log(chalk.yellow("Dry run complete. Add --execute to submit on-chain."));
463
- console.log(chalk.dim(" Prerequisite: send WETH to vault before executing."));
464
- return;
638
+ const addr = tokenMap[upper];
639
+ if (!addr || addr === ZERO) {
640
+ console.error(chalk.red(`Unknown token: ${symbolOrAddress}`));
641
+ process.exit(1);
465
642
  }
466
- const execSpinner = ora("Executing batch via vault...").start();
467
- try {
468
- const txHash = await executeBatch(calls);
469
- execSpinner.succeed(`Batch executed: ${txHash}`);
470
- console.log(chalk.dim(` ${getExplorerUrl(txHash)}`));
471
- } catch (err) {
472
- execSpinner.fail("Execution failed");
473
- console.error(chalk.red(err instanceof Error ? err.message : String(err)));
643
+ return addr;
644
+ }
645
+ function resolveMToken(tokenSymbol) {
646
+ const upper = tokenSymbol.toUpperCase();
647
+ const moonwell = MOONWELL();
648
+ const mTokenMap = {
649
+ USDC: moonwell.mUSDC,
650
+ WETH: moonwell.mWETH
651
+ };
652
+ const addr = mTokenMap[upper];
653
+ if (!addr || addr === ZERO) {
654
+ console.error(chalk.red(`No Moonwell market for: ${tokenSymbol}`));
474
655
  process.exit(1);
475
656
  }
657
+ return addr;
658
+ }
659
+ function serializeCalls(calls) {
660
+ return JSON.stringify(
661
+ calls.map((c) => ({
662
+ target: c.target,
663
+ data: c.data,
664
+ value: c.value.toString()
665
+ })),
666
+ null,
667
+ 2
668
+ );
669
+ }
670
+ function registerStrategyTemplateCommands(strategy2) {
671
+ strategy2.command("list").description("List available strategy templates").action(() => {
672
+ const templates = STRATEGY_TEMPLATES();
673
+ console.log();
674
+ console.log(chalk.bold("Strategy Templates"));
675
+ console.log(chalk.dim("\u2500".repeat(60)));
676
+ for (const t of TEMPLATES) {
677
+ const addr = templates[t.addressKey];
678
+ const deployed = addr !== ZERO;
679
+ console.log();
680
+ console.log(` ${chalk.bold(t.name)} (${chalk.cyan(t.key)})`);
681
+ console.log(` ${t.description}`);
682
+ console.log(` Template: ${deployed ? chalk.green(addr) : chalk.red("not deployed")}`);
683
+ }
684
+ console.log();
685
+ console.log(chalk.dim("Clone a template: sherwood strategy clone <template> --vault <addr> ..."));
686
+ console.log(chalk.dim("Full proposal: sherwood strategy propose <template> --vault <addr> ..."));
687
+ console.log();
688
+ });
689
+ strategy2.command("clone").description("Clone a strategy template and initialize it").argument("<template>", "Template: moonwell-supply, aerodrome-lp, venice-inference, wsteth-moonwell").requiredOption("--vault <address>", "Vault address").option("--amount <n>", "Asset amount to deploy").option("--min-redeem <n>", "Min asset on settlement (Moonwell)").option("--token <symbol>", "Asset token symbol (default: USDC)").option("--asset <symbol>", "Asset token (USDC, VVV, or address)").option("--agent <address>", "Agent wallet (Venice, default: your wallet)").option("--min-vvv <n>", "Min VVV from swap (Venice)").option("--single-hop", "Single-hop Aerodrome swap (Venice)").option("--token-a <address>", "Token A (Aerodrome)").option("--token-b <address>", "Token B (Aerodrome)").option("--amount-a <n>", "Token A amount (Aerodrome)").option("--amount-b <n>", "Token B amount (Aerodrome)").option("--stable", "Stable pool (Aerodrome)").option("--gauge <address>", "Gauge address (Aerodrome)").option("--lp-token <address>", "LP token address (Aerodrome)").option("--min-a-out <n>", "Min token A on settle (Aerodrome)").option("--min-b-out <n>", "Min token B on settle (Aerodrome)").option("--slippage <bps>", "Slippage tolerance in bps (wstETH, default: 500 = 5%)").action(async (templateKey, opts) => {
690
+ const vault = opts.vault;
691
+ if (!isAddress(vault)) {
692
+ console.error(chalk.red("Invalid vault address"));
693
+ process.exit(1);
694
+ }
695
+ const { def, address: templateAddr } = resolveTemplate(templateKey);
696
+ const cloneSpinner = ora(`Cloning ${def.name} template...`).start();
697
+ let clone;
698
+ let cloneHash;
699
+ try {
700
+ const result = await cloneTemplate(templateAddr);
701
+ clone = result.clone;
702
+ cloneHash = result.hash;
703
+ cloneSpinner.succeed(`Cloned: ${chalk.green(clone)}`);
704
+ console.log(chalk.dim(` Tx: ${getExplorerUrl(cloneHash)}`));
705
+ } catch (err) {
706
+ cloneSpinner.fail("Clone failed");
707
+ console.error(chalk.red(err instanceof Error ? err.message : String(err)));
708
+ process.exit(1);
709
+ }
710
+ const initSpinner = ora("Initializing strategy...").start();
711
+ try {
712
+ const { initData } = await buildInitDataForTemplate(templateKey, opts, vault);
713
+ const account = getAccount();
714
+ const wallet = getWalletClient();
715
+ const initHash = await wallet.writeContract({
716
+ account,
717
+ chain: getChain(),
718
+ address: clone,
719
+ abi: BASE_STRATEGY_ABI,
720
+ functionName: "initialize",
721
+ args: [vault, account.address, initData]
722
+ });
723
+ await getPublicClient().waitForTransactionReceipt({ hash: initHash });
724
+ initSpinner.succeed("Initialized");
725
+ } catch (err) {
726
+ initSpinner.fail("Initialize failed");
727
+ console.error(chalk.red(err instanceof Error ? err.message : String(err)));
728
+ process.exit(1);
729
+ }
730
+ console.log();
731
+ console.log(chalk.bold("Strategy clone ready:"), chalk.green(clone));
732
+ console.log(chalk.dim("Use this address in your proposal batch calls."));
733
+ console.log();
734
+ });
735
+ strategy2.command("propose").description("Clone + init + build calls + submit governance proposal (all-in-one)").argument("<template>", "Template: moonwell-supply, aerodrome-lp, venice-inference, wsteth-moonwell").requiredOption("--vault <address>", "Vault address").option("--write-calls <dir>", "Write execute/settle JSON to directory (skip proposal submission)").option("--name <name>", "Proposal name").option("--description <text>", "Proposal description").option("--performance-fee <bps>", "Agent fee in bps").option("--duration <duration>", "Strategy duration (7d, 24h, etc.)").option("--amount <n>", "Asset amount to deploy").option("--min-redeem <n>", "Min asset on settlement (Moonwell)").option("--token <symbol>", "Asset token symbol (default: USDC)").option("--asset <symbol>", "Asset token (USDC, VVV, or address)").option("--agent <address>", "Agent wallet (Venice, default: your wallet)").option("--min-vvv <n>", "Min VVV from swap (Venice)").option("--single-hop", "Single-hop Aerodrome swap (Venice)").option("--token-a <address>", "Token A (Aerodrome)").option("--token-b <address>", "Token B (Aerodrome)").option("--amount-a <n>", "Token A amount (Aerodrome)").option("--amount-b <n>", "Token B amount (Aerodrome)").option("--stable", "Stable pool (Aerodrome)").option("--gauge <address>", "Gauge address (Aerodrome)").option("--lp-token <address>", "LP token address (Aerodrome)").option("--min-a-out <n>", "Min token A on settle (Aerodrome)").option("--min-b-out <n>", "Min token B on settle (Aerodrome)").option("--slippage <bps>", "Slippage tolerance in bps (wstETH, default: 500 = 5%)").action(async (templateKey, opts) => {
736
+ const vault = opts.vault;
737
+ if (!isAddress(vault)) {
738
+ console.error(chalk.red("Invalid vault address"));
739
+ process.exit(1);
740
+ }
741
+ const { def, address: templateAddr } = resolveTemplate(templateKey);
742
+ const cloneSpinner = ora(`Cloning ${def.name} template...`).start();
743
+ let clone;
744
+ try {
745
+ const result = await cloneTemplate(templateAddr);
746
+ clone = result.clone;
747
+ cloneSpinner.succeed(`Cloned: ${chalk.green(clone)}`);
748
+ console.log(chalk.dim(` Tx: ${getExplorerUrl(result.hash)}`));
749
+ } catch (err) {
750
+ cloneSpinner.fail("Clone failed");
751
+ console.error(chalk.red(err instanceof Error ? err.message : String(err)));
752
+ process.exit(1);
753
+ }
754
+ const initSpinner = ora("Initializing strategy...").start();
755
+ let asset;
756
+ let assetAmount;
757
+ let extraApprovals;
758
+ try {
759
+ const built = await buildInitDataForTemplate(templateKey, opts, vault);
760
+ asset = built.asset;
761
+ assetAmount = built.assetAmount;
762
+ extraApprovals = built.extraApprovals;
763
+ const account2 = getAccount();
764
+ const wallet = getWalletClient();
765
+ const initHash = await wallet.writeContract({
766
+ account: account2,
767
+ chain: getChain(),
768
+ address: clone,
769
+ abi: BASE_STRATEGY_ABI,
770
+ functionName: "initialize",
771
+ args: [vault, account2.address, built.initData]
772
+ });
773
+ await getPublicClient().waitForTransactionReceipt({ hash: initHash });
774
+ initSpinner.succeed("Initialized");
775
+ } catch (err) {
776
+ initSpinner.fail("Initialize failed");
777
+ console.error(chalk.red(err instanceof Error ? err.message : String(err)));
778
+ process.exit(1);
779
+ }
780
+ const { executeCalls, settleCalls } = buildCallsForTemplate(
781
+ templateKey,
782
+ clone,
783
+ asset,
784
+ assetAmount,
785
+ extraApprovals
786
+ );
787
+ console.log();
788
+ console.log(chalk.bold(`Execute calls (${executeCalls.length}):`));
789
+ console.log(formatBatch(executeCalls));
790
+ console.log(chalk.bold(`Settle calls (${settleCalls.length}):`));
791
+ console.log(formatBatch(settleCalls));
792
+ if (opts.writeCalls) {
793
+ const dir = resolve(opts.writeCalls);
794
+ mkdirSync(dir, { recursive: true });
795
+ const execPath = resolve(dir, "execute.json");
796
+ const settlePath = resolve(dir, "settle.json");
797
+ writeFileSync(execPath, serializeCalls(executeCalls));
798
+ writeFileSync(settlePath, serializeCalls(settleCalls));
799
+ console.log();
800
+ console.log(chalk.green(`Execute calls: ${execPath}`));
801
+ console.log(chalk.green(`Settle calls: ${settlePath}`));
802
+ console.log(chalk.green(`Clone address: ${clone}`));
803
+ console.log();
804
+ console.log(chalk.dim("Submit with:"));
805
+ console.log(chalk.dim(` sherwood proposal create \\`));
806
+ console.log(chalk.dim(` --vault ${vault} \\`));
807
+ console.log(chalk.dim(` --name "..." --description "..." \\`));
808
+ console.log(chalk.dim(` --performance-fee 0 --duration 7d \\`));
809
+ console.log(chalk.dim(` --execute-calls ${execPath} \\`));
810
+ console.log(chalk.dim(` --settle-calls ${settlePath}`));
811
+ if (templateKey === "venice-inference") {
812
+ console.log();
813
+ console.log(chalk.yellow("Reminder: before settlement, agent must approve repayment:"));
814
+ console.log(chalk.yellow(` asset.approve(${clone}, <repaymentAmount>)`));
815
+ console.log(chalk.yellow(" Agent can update repayment via strategy.updateParams(newRepayment, 0, 0)"));
816
+ }
817
+ console.log();
818
+ return;
819
+ }
820
+ if (!opts.name || !opts.performanceFee || !opts.duration) {
821
+ console.error(chalk.red("Missing --name, --performance-fee, or --duration. Use --write-calls to skip proposal submission."));
822
+ process.exit(1);
823
+ }
824
+ const { propose: propose2 } = await import("./governor-J7WTADUX.js");
825
+ const { pinJSON } = await import("./ipfs-6XVOOHSR.js");
826
+ const { parseDuration: parseDuration2 } = await import("./governor-J7WTADUX.js");
827
+ const performanceFeeBps = BigInt(opts.performanceFee);
828
+ if (performanceFeeBps < 0n || performanceFeeBps > 10000n) {
829
+ console.error(chalk.red("--performance-fee must be 0-10000 (basis points)"));
830
+ process.exit(1);
831
+ }
832
+ const strategyDuration = parseDuration2(opts.duration);
833
+ const account = getAccount();
834
+ const metaSpinner = ora("Pinning metadata to IPFS...").start();
835
+ let metadataURI;
836
+ try {
837
+ const metadata = {
838
+ name: opts.name,
839
+ description: opts.description || "",
840
+ proposer: account.address,
841
+ vault,
842
+ strategyClone: clone,
843
+ template: def.key,
844
+ performanceFeeBps: Number(performanceFeeBps),
845
+ strategyDuration: Number(strategyDuration),
846
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
847
+ };
848
+ metadataURI = await pinJSON(metadata, opts.name);
849
+ metaSpinner.succeed(`Metadata pinned: ${metadataURI}`);
850
+ } catch (err) {
851
+ metaSpinner.fail("IPFS pin failed");
852
+ console.error(chalk.red(err instanceof Error ? err.message : String(err)));
853
+ process.exit(1);
854
+ }
855
+ const proposeSpinner = ora("Submitting proposal...").start();
856
+ try {
857
+ const { hash, proposalId } = await propose2(
858
+ vault,
859
+ metadataURI,
860
+ performanceFeeBps,
861
+ strategyDuration,
862
+ executeCalls,
863
+ settleCalls
864
+ );
865
+ proposeSpinner.succeed(`Proposal #${proposalId} created`);
866
+ console.log(chalk.dim(` Tx: ${getExplorerUrl(hash)}`));
867
+ console.log(chalk.dim(` Clone: ${clone}`));
868
+ } catch (err) {
869
+ proposeSpinner.fail("Proposal failed");
870
+ console.error(chalk.red(err instanceof Error ? err.message : String(err)));
871
+ process.exit(1);
872
+ }
873
+ if (templateKey === "venice-inference") {
874
+ console.log();
875
+ console.log(chalk.yellow("Next steps:"));
876
+ console.log(chalk.yellow(" 1. After execution: sherwood venice provision"));
877
+ console.log(chalk.yellow(" 2. Use inference: sherwood venice infer --model <id> --prompt '...'"));
878
+ console.log(chalk.yellow(" 3. Before settlement: approve repayment (principal + profit):"));
879
+ console.log(chalk.yellow(` asset.approve(${clone}, <repaymentAmount>)`));
880
+ }
881
+ console.log();
882
+ });
476
883
  }
477
884
 
478
885
  // src/lib/factory.ts
@@ -652,74 +1059,83 @@ async function getActiveSyndicates2(creator) {
652
1059
  return data.syndicates;
653
1060
  }
654
1061
 
655
- // src/lib/registry.ts
656
- function getRegistryAddress() {
657
- return "0x0000000000000000000000000000000000000000";
658
- }
659
- async function registerStrategy(implementation, strategyTypeId, name, metadataURI) {
660
- const wallet = getWalletClient();
661
- return wallet.writeContract({
662
- account: getAccount(),
663
- chain: getChain(),
664
- address: getRegistryAddress(),
665
- abi: STRATEGY_REGISTRY_ABI,
666
- functionName: "registerStrategy",
667
- args: [implementation, strategyTypeId, name, metadataURI]
668
- });
669
- }
670
- async function getStrategy(id) {
1062
+ // src/commands/venice.ts
1063
+ import { parseUnits as parseUnits4, formatUnits as formatUnits2, isAddress as isAddress2 } from "viem";
1064
+ import chalk2 from "chalk";
1065
+ import ora2 from "ora";
1066
+
1067
+ // src/lib/quote.ts
1068
+ import { encodeFunctionData as encodeFunctionData5, decodeFunctionResult, concat as concat2, pad, numberToHex } from "viem";
1069
+ async function getQuote(params) {
671
1070
  const client = getPublicClient();
672
- const result = await client.readContract({
673
- address: getRegistryAddress(),
674
- abi: STRATEGY_REGISTRY_ABI,
675
- functionName: "getStrategy",
676
- args: [id]
1071
+ const calldata = encodeFunctionData5({
1072
+ abi: UNISWAP_QUOTER_V2_ABI,
1073
+ functionName: "quoteExactInputSingle",
1074
+ args: [
1075
+ {
1076
+ tokenIn: params.tokenIn,
1077
+ tokenOut: params.tokenOut,
1078
+ amountIn: params.amountIn,
1079
+ fee: params.fee,
1080
+ sqrtPriceLimitX96: 0n
1081
+ }
1082
+ ]
677
1083
  });
678
- return {
679
- id,
680
- implementation: result.implementation,
681
- creator: result.creator,
682
- strategyTypeId: result.strategyTypeId,
683
- active: result.active,
684
- name: result.name,
685
- metadataURI: result.metadataURI
686
- };
1084
+ const { data } = await client.call({
1085
+ to: UNISWAP().QUOTER_V2,
1086
+ data: calldata
1087
+ });
1088
+ if (!data) {
1089
+ throw new Error("Quoter returned no data \u2014 pool may not exist for this pair/fee");
1090
+ }
1091
+ const [amountOut, sqrtPriceX96After, , gasEstimate] = decodeFunctionResult({
1092
+ abi: UNISWAP_QUOTER_V2_ABI,
1093
+ functionName: "quoteExactInputSingle",
1094
+ data
1095
+ });
1096
+ return { amountOut, sqrtPriceX96After, gasEstimate };
687
1097
  }
688
- async function listStrategies(typeId) {
689
- const client = getPublicClient();
690
- const registryAddress = getRegistryAddress();
691
- let ids;
692
- if (typeId !== void 0) {
693
- ids = await client.readContract({
694
- address: registryAddress,
695
- abi: STRATEGY_REGISTRY_ABI,
696
- functionName: "getStrategiesByType",
697
- args: [typeId]
698
- });
699
- } else {
700
- const count = await client.readContract({
701
- address: registryAddress,
702
- abi: STRATEGY_REGISTRY_ABI,
703
- functionName: "strategyCount"
704
- });
705
- ids = Array.from({ length: Number(count) }, (_, i) => BigInt(i + 1));
1098
+ function applySlippage(amountOut, slippageBps) {
1099
+ return amountOut * BigInt(1e4 - slippageBps) / 10000n;
1100
+ }
1101
+ function encodeSwapPath(tokens, fees) {
1102
+ if (tokens.length < 2 || fees.length !== tokens.length - 1) {
1103
+ throw new Error("Invalid path: need at least 2 tokens and (tokens-1) fees");
1104
+ }
1105
+ const parts = [];
1106
+ for (let i = 0; i < tokens.length; i++) {
1107
+ parts.push(tokens[i].toLowerCase());
1108
+ if (i < fees.length) {
1109
+ parts.push(pad(numberToHex(fees[i]), { size: 3 }));
1110
+ }
706
1111
  }
707
- const strategies = [];
708
- for (const id of ids) {
709
- const s = await getStrategy(id);
710
- strategies.push(s);
1112
+ return concat2(parts);
1113
+ }
1114
+ async function getMultiHopQuote(params) {
1115
+ const client = getPublicClient();
1116
+ const calldata = encodeFunctionData5({
1117
+ abi: UNISWAP_QUOTER_V2_ABI,
1118
+ functionName: "quoteExactInput",
1119
+ args: [params.path, params.amountIn]
1120
+ });
1121
+ const { data } = await client.call({
1122
+ to: UNISWAP().QUOTER_V2,
1123
+ data: calldata
1124
+ });
1125
+ if (!data) {
1126
+ throw new Error("Quoter returned no data \u2014 pool may not exist for this path");
711
1127
  }
712
- return strategies;
1128
+ const [amountOut, , , gasEstimate] = decodeFunctionResult({
1129
+ abi: UNISWAP_QUOTER_V2_ABI,
1130
+ functionName: "quoteExactInput",
1131
+ data
1132
+ });
1133
+ return { amountOut, sqrtPriceX96After: 0n, gasEstimate };
713
1134
  }
714
1135
 
715
- // src/commands/venice.ts
716
- import { parseUnits as parseUnits5, formatUnits as formatUnits3, isAddress as isAddress2 } from "viem";
717
- import chalk2 from "chalk";
718
- import ora2 from "ora";
719
-
720
1136
  // src/strategies/venice-fund.ts
721
- import { encodeFunctionData as encodeFunctionData3, parseUnits as parseUnits4 } from "viem";
722
- var ERC20_ABI3 = [
1137
+ import { encodeFunctionData as encodeFunctionData6, parseUnits as parseUnits3 } from "viem";
1138
+ var ERC20_ABI2 = [
723
1139
  {
724
1140
  name: "approve",
725
1141
  type: "function",
@@ -783,17 +1199,17 @@ var STAKING_ABI = [
783
1199
  }
784
1200
  ];
785
1201
  function buildFundBatch(config, vaultAddress, agents, assetAddress, assetDecimals, minVVV, swapPath) {
786
- const ZERO = "0x0000000000000000000000000000000000000000";
787
- if (VENICE().VVV === ZERO || VENICE().STAKING === ZERO) {
1202
+ const ZERO2 = "0x0000000000000000000000000000000000000000";
1203
+ if (VENICE().VVV === ZERO2 || VENICE().STAKING === ZERO2) {
788
1204
  throw new Error("Venice (VVV/sVVV) is not deployed on this network \u2014 venice fund requires Venice staking contracts");
789
1205
  }
790
- const assetAmount = parseUnits4(config.amount, assetDecimals);
1206
+ const assetAmount = parseUnits3(config.amount, assetDecimals);
791
1207
  const isWeth = assetAddress.toLowerCase() === TOKENS().WETH.toLowerCase();
792
1208
  const calls = [];
793
1209
  calls.push({
794
1210
  target: assetAddress,
795
- data: encodeFunctionData3({
796
- abi: ERC20_ABI3,
1211
+ data: encodeFunctionData6({
1212
+ abi: ERC20_ABI2,
797
1213
  functionName: "approve",
798
1214
  args: [UNISWAP().SWAP_ROUTER, assetAmount]
799
1215
  }),
@@ -802,7 +1218,7 @@ function buildFundBatch(config, vaultAddress, agents, assetAddress, assetDecimal
802
1218
  if (isWeth) {
803
1219
  calls.push({
804
1220
  target: UNISWAP().SWAP_ROUTER,
805
- data: encodeFunctionData3({
1221
+ data: encodeFunctionData6({
806
1222
  abi: SWAP_ROUTER_EXACT_INPUT_SINGLE_ABI,
807
1223
  functionName: "exactInputSingle",
808
1224
  args: [
@@ -822,7 +1238,7 @@ function buildFundBatch(config, vaultAddress, agents, assetAddress, assetDecimal
822
1238
  } else {
823
1239
  calls.push({
824
1240
  target: UNISWAP().SWAP_ROUTER,
825
- data: encodeFunctionData3({
1241
+ data: encodeFunctionData6({
826
1242
  abi: SWAP_ROUTER_EXACT_INPUT_ABI,
827
1243
  functionName: "exactInput",
828
1244
  args: [
@@ -839,8 +1255,8 @@ function buildFundBatch(config, vaultAddress, agents, assetAddress, assetDecimal
839
1255
  }
840
1256
  calls.push({
841
1257
  target: VENICE().VVV,
842
- data: encodeFunctionData3({
843
- abi: ERC20_ABI3,
1258
+ data: encodeFunctionData6({
1259
+ abi: ERC20_ABI2,
844
1260
  functionName: "approve",
845
1261
  args: [VENICE().STAKING, minVVV]
846
1262
  }),
@@ -850,7 +1266,7 @@ function buildFundBatch(config, vaultAddress, agents, assetAddress, assetDecimal
850
1266
  for (const agent of agents) {
851
1267
  calls.push({
852
1268
  target: VENICE().STAKING,
853
- data: encodeFunctionData3({
1269
+ data: encodeFunctionData6({
854
1270
  abi: STAKING_ABI,
855
1271
  functionName: "stake",
856
1272
  args: [agent, perAgent]
@@ -865,16 +1281,22 @@ function buildFundBatch(config, vaultAddress, agents, assetAddress, assetDecimal
865
1281
  var VENICE_API_BASE = "https://api.venice.ai/api/v1";
866
1282
  async function provisionApiKey() {
867
1283
  const account = getAccount();
868
- const tokenRes = await fetch(`${VENICE_API_BASE}/api_keys/generate_web3_key`);
1284
+ const tokenRes = await fetch(`${VENICE_API_BASE}/api_keys/generate_web3_key`, {
1285
+ signal: AbortSignal.timeout(15e3)
1286
+ });
869
1287
  if (!tokenRes.ok) {
870
1288
  throw new Error(`Failed to get validation token: ${tokenRes.status} ${tokenRes.statusText}`);
871
1289
  }
872
1290
  const tokenData = await tokenRes.json();
873
- const token = tokenData.data.token;
1291
+ const token = tokenData?.data?.token;
1292
+ if (!token) {
1293
+ throw new Error("Venice API returned no validation token");
1294
+ }
874
1295
  const signature = await account.signMessage({ message: token });
875
1296
  const keyRes = await fetch(`${VENICE_API_BASE}/api_keys/generate_web3_key`, {
876
1297
  method: "POST",
877
1298
  headers: { "Content-Type": "application/json" },
1299
+ signal: AbortSignal.timeout(15e3),
878
1300
  body: JSON.stringify({
879
1301
  address: account.address,
880
1302
  signature,
@@ -888,28 +1310,92 @@ async function provisionApiKey() {
888
1310
  throw new Error(`Failed to generate API key: ${keyRes.status} ${body}`);
889
1311
  }
890
1312
  const keyData = await keyRes.json();
891
- const apiKey = keyData.data.apiKey;
1313
+ const apiKey = keyData?.data?.apiKey;
1314
+ if (!apiKey) {
1315
+ throw new Error("Venice API returned no API key");
1316
+ }
892
1317
  setVeniceApiKey(apiKey);
893
1318
  return apiKey;
894
1319
  }
895
- async function checkApiKeyValid() {
1320
+ async function checkApiKeyValid() {
1321
+ const apiKey = getVeniceApiKey();
1322
+ if (!apiKey) return false;
1323
+ try {
1324
+ const res = await fetch(`${VENICE_API_BASE}/models`, {
1325
+ headers: { Authorization: `Bearer ${apiKey}` },
1326
+ signal: AbortSignal.timeout(15e3)
1327
+ });
1328
+ return res.ok;
1329
+ } catch {
1330
+ return false;
1331
+ }
1332
+ }
1333
+ async function chatCompletion(opts) {
1334
+ const apiKey = getVeniceApiKey();
1335
+ if (!apiKey) {
1336
+ throw new Error("No Venice API key configured. Run 'sherwood venice provision' first.");
1337
+ }
1338
+ const body = {
1339
+ model: opts.model,
1340
+ messages: opts.messages
1341
+ };
1342
+ if (opts.temperature !== void 0) body.temperature = opts.temperature;
1343
+ if (opts.maxTokens !== void 0) body.max_tokens = opts.maxTokens;
1344
+ const veniceParams = {};
1345
+ if (opts.enableWebSearch) veniceParams.enable_web_search = "on";
1346
+ if (opts.disableThinking) veniceParams.disable_thinking = true;
1347
+ if (Object.keys(veniceParams).length > 0) body.venice_parameters = veniceParams;
1348
+ const res = await fetch(`${VENICE_API_BASE}/chat/completions`, {
1349
+ method: "POST",
1350
+ headers: {
1351
+ "Content-Type": "application/json",
1352
+ Authorization: `Bearer ${apiKey}`
1353
+ },
1354
+ signal: AbortSignal.timeout(12e4),
1355
+ // inference can be slow
1356
+ body: JSON.stringify(body)
1357
+ });
1358
+ if (!res.ok) {
1359
+ const errBody = await res.text();
1360
+ throw new Error(`Venice inference failed: ${res.status} ${errBody}`);
1361
+ }
1362
+ const data = await res.json();
1363
+ const choice = data.choices?.[0];
1364
+ if (!choice) {
1365
+ throw new Error("Venice returned no choices");
1366
+ }
1367
+ return {
1368
+ content: choice.message?.content ?? "",
1369
+ model: data.model ?? opts.model,
1370
+ usage: {
1371
+ promptTokens: data.usage?.prompt_tokens ?? 0,
1372
+ completionTokens: data.usage?.completion_tokens ?? 0,
1373
+ totalTokens: data.usage?.total_tokens ?? 0
1374
+ }
1375
+ };
1376
+ }
1377
+ async function listModels() {
896
1378
  const apiKey = getVeniceApiKey();
897
- if (!apiKey) return false;
898
- try {
899
- const res = await fetch(`${VENICE_API_BASE}/models`, {
900
- headers: { Authorization: `Bearer ${apiKey}` }
901
- });
902
- return res.ok;
903
- } catch {
904
- return false;
1379
+ if (!apiKey) {
1380
+ throw new Error("No Venice API key configured. Run 'sherwood venice provision' first.");
1381
+ }
1382
+ const res = await fetch(`${VENICE_API_BASE}/models`, {
1383
+ headers: { Authorization: `Bearer ${apiKey}` },
1384
+ signal: AbortSignal.timeout(15e3)
1385
+ });
1386
+ if (!res.ok) {
1387
+ throw new Error(`Failed to list Venice models: ${res.status} ${res.statusText}`);
905
1388
  }
1389
+ const data = await res.json();
1390
+ return (data.data ?? []).map((m) => m.id);
906
1391
  }
907
1392
 
908
1393
  // src/commands/venice.ts
909
- var VALID_FEES2 = [500, 3e3, 1e4];
1394
+ import { readFileSync, writeFileSync as writeFileSync2 } from "fs";
1395
+ var VALID_FEES = [500, 3e3, 1e4];
910
1396
  function registerVeniceCommands(program2) {
911
1397
  const venice = program2.command("venice").description("Venice private inference \u2014 stake VVV, provision API keys");
912
- venice.command("fund").description("Swap vault profits \u2192 VVV \u2192 stake \u2192 distribute sVVV to agents").requiredOption("--vault <address>", "Vault address").requiredOption("--amount <amount>", "Deposit token amount to convert (e.g. 500)").option("--fee1 <tier>", "Fee tier for asset \u2192 WETH hop (500, 3000, 10000)", "3000").option("--fee2 <tier>", "Fee tier for WETH \u2192 VVV hop", "10000").option("--slippage <bps>", "Slippage tolerance in bps", "100").option("--execute", "Execute on-chain (default: simulate only)", false).action(async (opts) => {
1398
+ venice.command("fund").description("Swap vault profits \u2192 VVV \u2192 stake \u2192 distribute sVVV to agents").requiredOption("--vault <address>", "Vault address").requiredOption("--amount <amount>", "Deposit token amount to convert (e.g. 500)").option("--fee1 <tier>", "Fee tier for asset \u2192 WETH hop (500, 3000, 10000)", "3000").option("--fee2 <tier>", "Fee tier for WETH \u2192 VVV hop", "10000").option("--slippage <bps>", "Slippage tolerance in bps", "100").option("--execute", "Execute on-chain (default: simulate only)", false).option("--write-calls <path>", "Write batch calls to JSON file for proposal create (skips execution)").action(async (opts) => {
913
1399
  const vaultAddress = opts.vault;
914
1400
  if (!isAddress2(vaultAddress)) {
915
1401
  console.error(chalk2.red(`Invalid vault address: ${opts.vault}`));
@@ -917,8 +1403,8 @@ function registerVeniceCommands(program2) {
917
1403
  }
918
1404
  const fee1 = Number(opts.fee1);
919
1405
  const fee2 = Number(opts.fee2);
920
- if (!VALID_FEES2.includes(fee1) || !VALID_FEES2.includes(fee2)) {
921
- console.error(chalk2.red(`Invalid fee tier. Valid: ${VALID_FEES2.join(", ")}`));
1406
+ if (!VALID_FEES.includes(fee1) || !VALID_FEES.includes(fee2)) {
1407
+ console.error(chalk2.red(`Invalid fee tier. Valid: ${VALID_FEES.join(", ")}`));
922
1408
  process.exit(1);
923
1409
  }
924
1410
  const slippageBps = Number(opts.slippage);
@@ -951,7 +1437,7 @@ function registerVeniceCommands(program2) {
951
1437
  console.error(chalk2.red("No agents registered in vault. Register agents first."));
952
1438
  process.exit(1);
953
1439
  }
954
- const requestedAmount = parseUnits5(opts.amount, assetDecimals);
1440
+ const requestedAmount = parseUnits4(opts.amount, assetDecimals);
955
1441
  const profit = assetBalance > totalDeposited ? assetBalance - totalDeposited : 0n;
956
1442
  const isWeth = assetAddress.toLowerCase() === TOKENS().WETH.toLowerCase();
957
1443
  console.log();
@@ -959,16 +1445,16 @@ function registerVeniceCommands(program2) {
959
1445
  console.log(chalk2.dim("\u2500".repeat(40)));
960
1446
  console.log(` Asset: ${assetSymbol} (${assetDecimals} decimals)`);
961
1447
  console.log(` Amount: ${opts.amount} ${assetSymbol}`);
962
- console.log(` Vault balance: ${formatUnits3(assetBalance, assetDecimals)} ${assetSymbol}`);
963
- console.log(` Deposited: ${formatUnits3(totalDeposited, assetDecimals)} ${assetSymbol}`);
964
- console.log(` Profit: ${formatUnits3(profit, assetDecimals)} ${assetSymbol}`);
1448
+ console.log(` Vault balance: ${formatUnits2(assetBalance, assetDecimals)} ${assetSymbol}`);
1449
+ console.log(` Deposited: ${formatUnits2(totalDeposited, assetDecimals)} ${assetSymbol}`);
1450
+ console.log(` Profit: ${formatUnits2(profit, assetDecimals)} ${assetSymbol}`);
965
1451
  console.log(` Agents: ${agents.length} (sVVV will be split equally)`);
966
1452
  console.log(` Routing: ${isWeth ? `WETH \u2192 VVV (fee ${fee2})` : `${assetSymbol} \u2192 WETH (fee ${fee1}) \u2192 VVV (fee ${fee2})`}`);
967
1453
  console.log(` Slippage: ${(slippageBps / 100).toFixed(2)}%`);
968
1454
  console.log(` Vault: ${vaultAddress}`);
969
1455
  console.log();
970
1456
  if (requestedAmount > profit) {
971
- console.warn(chalk2.yellow(` Warning: amount (${opts.amount}) exceeds available profit (${formatUnits3(profit, assetDecimals)})`));
1457
+ console.warn(chalk2.yellow(` Warning: amount (${opts.amount}) exceeds available profit (${formatUnits2(profit, assetDecimals)})`));
972
1458
  console.warn(chalk2.yellow(" This will use deposited capital, not just profits."));
973
1459
  console.log();
974
1460
  }
@@ -998,7 +1484,7 @@ function registerVeniceCommands(program2) {
998
1484
  }
999
1485
  minOut = applySlippage(amountOut, slippageBps);
1000
1486
  quoteSpinner.succeed(
1001
- `Quote: ${formatUnits3(amountOut, 18)} VVV (min: ${formatUnits3(minOut, 18)}, per agent: ${formatUnits3(minOut / BigInt(agents.length), 18)})`
1487
+ `Quote: ${formatUnits2(amountOut, 18)} VVV (min: ${formatUnits2(minOut, 18)}, per agent: ${formatUnits2(minOut / BigInt(agents.length), 18)})`
1002
1488
  );
1003
1489
  } catch (err) {
1004
1490
  quoteSpinner.fail("Failed to fetch quote");
@@ -1016,9 +1502,24 @@ function registerVeniceCommands(program2) {
1016
1502
  console.log(chalk2.bold(`Batch calls (${calls.length}):`));
1017
1503
  console.log(formatBatch(calls));
1018
1504
  console.log();
1505
+ if (opts.writeCalls) {
1506
+ const callsJson = calls.map((c) => ({
1507
+ target: c.target,
1508
+ data: c.data,
1509
+ value: c.value.toString()
1510
+ }));
1511
+ writeFileSync2(opts.writeCalls, JSON.stringify(callsJson, null, 2));
1512
+ const settlePath = `${opts.writeCalls}.settle.json`;
1513
+ writeFileSync2(settlePath, "[]");
1514
+ console.log(chalk2.green(`Execute calls written to: ${opts.writeCalls}`));
1515
+ console.log(chalk2.green(`Settlement calls written to: ${settlePath}`));
1516
+ console.log();
1517
+ console.log(chalk2.dim("Use with: sherwood proposal create --execute-calls <path> --settle-calls <path>"));
1518
+ return;
1519
+ }
1019
1520
  if (!opts.execute) {
1020
1521
  console.log();
1021
- console.log(chalk2.yellow("Dry run complete. Add --execute to submit on-chain."));
1522
+ console.log(chalk2.yellow("Dry run complete. Add --execute to submit on-chain, or --write-calls <path> to export for proposals."));
1022
1523
  return;
1023
1524
  }
1024
1525
  const execSpinner = ora2("Executing batch via vault...").start();
@@ -1049,7 +1550,7 @@ function registerVeniceCommands(program2) {
1049
1550
  console.log(chalk2.yellow(" Run 'sherwood venice fund' first to distribute sVVV to agents."));
1050
1551
  process.exit(1);
1051
1552
  }
1052
- checkSpinner.succeed(`sVVV balance: ${formatUnits3(sVvvBalance, 18)}`);
1553
+ checkSpinner.succeed(`sVVV balance: ${formatUnits2(sVvvBalance, 18)}`);
1053
1554
  } catch (err) {
1054
1555
  checkSpinner.fail("Failed to check sVVV balance");
1055
1556
  console.error(chalk2.red(err instanceof Error ? err.message : String(err)));
@@ -1126,17 +1627,17 @@ function registerVeniceCommands(program2) {
1126
1627
  console.log(chalk2.bold("Venice Inference Status"));
1127
1628
  console.log(chalk2.dim("\u2500".repeat(50)));
1128
1629
  console.log(chalk2.bold("\n Vault"));
1129
- console.log(` Profit available: ${formatUnits3(profit, assetDecimals)} ${assetSymbol}`);
1130
- console.log(` VVV (unstaked): ${formatUnits3(vaultVvvBalance, 18)}`);
1630
+ console.log(` Profit available: ${formatUnits2(profit, assetDecimals)} ${assetSymbol}`);
1631
+ console.log(` VVV (unstaked): ${formatUnits2(vaultVvvBalance, 18)}`);
1131
1632
  console.log(chalk2.bold("\n Agent sVVV Balances"));
1132
1633
  for (const { agent, balance } of agentBalances) {
1133
1634
  const isMe = agent.toLowerCase() === account.address.toLowerCase();
1134
1635
  const label = isMe ? chalk2.green(`${agent} (you)`) : agent;
1135
- console.log(` ${label}: ${formatUnits3(balance, 18)} sVVV`);
1636
+ console.log(` ${label}: ${formatUnits2(balance, 18)} sVVV`);
1136
1637
  }
1137
1638
  console.log(chalk2.bold("\n Your Wallet"));
1138
- console.log(` sVVV: ${formatUnits3(mySvvv, 18)}`);
1139
- console.log(` Pending rewards: ${formatUnits3(myPending, 18)} VVV`);
1639
+ console.log(` sVVV: ${formatUnits2(mySvvv, 18)}`);
1640
+ console.log(` Pending rewards: ${formatUnits2(myPending, 18)} VVV`);
1140
1641
  console.log(chalk2.bold("\n Venice API"));
1141
1642
  console.log(` Key: ${apiKey ? `${apiKey.slice(0, 8)}...${apiKey.slice(-4)}` : chalk2.dim("not provisioned")}`);
1142
1643
  console.log(` Status: ${apiKeyValid ? chalk2.green("valid") : chalk2.red("invalid/missing")}`);
@@ -1147,16 +1648,79 @@ function registerVeniceCommands(program2) {
1147
1648
  process.exit(1);
1148
1649
  }
1149
1650
  });
1651
+ venice.command("models").description("List available Venice inference models").action(async () => {
1652
+ const spinner = ora2("Fetching Venice models...").start();
1653
+ try {
1654
+ const models = await listModels();
1655
+ spinner.succeed(`${models.length} models available`);
1656
+ console.log();
1657
+ for (const model of models) {
1658
+ console.log(` ${model}`);
1659
+ }
1660
+ console.log();
1661
+ } catch (err) {
1662
+ spinner.fail("Failed to list models");
1663
+ console.error(chalk2.red(err instanceof Error ? err.message : String(err)));
1664
+ process.exit(1);
1665
+ }
1666
+ });
1667
+ venice.command("infer").description("Run private inference via Venice chat completions").requiredOption("--prompt <text>", "User prompt").requiredOption("--model <id>", "Venice model ID (use 'venice models' to list)").option("--system <text>", "System prompt").option("--data <path>", "Path to data file \u2014 contents prepended to prompt as context").option("--web-search", "Enable Venice web search", false).option("--no-thinking", "Disable chain-of-thought reasoning").option("--temperature <n>", "Sampling temperature (0-2)").option("--max-tokens <n>", "Maximum completion tokens").option("--json", "Output raw JSON response", false).action(async (opts) => {
1668
+ const messages = [];
1669
+ if (opts.system) {
1670
+ messages.push({ role: "system", content: opts.system });
1671
+ }
1672
+ let userContent = opts.prompt;
1673
+ if (opts.data) {
1674
+ try {
1675
+ const data = readFileSync(opts.data, "utf-8");
1676
+ userContent = `Context data:
1677
+ \`\`\`
1678
+ ${data}
1679
+ \`\`\`
1680
+
1681
+ ${opts.prompt}`;
1682
+ } catch (err) {
1683
+ console.error(chalk2.red(`Failed to read data file: ${opts.data}`));
1684
+ console.error(chalk2.red(err instanceof Error ? err.message : String(err)));
1685
+ process.exit(1);
1686
+ }
1687
+ }
1688
+ messages.push({ role: "user", content: userContent });
1689
+ const spinner = ora2(`Running inference (${opts.model})...`).start();
1690
+ try {
1691
+ const result = await chatCompletion({
1692
+ model: opts.model,
1693
+ messages,
1694
+ temperature: opts.temperature !== void 0 ? Number(opts.temperature) : void 0,
1695
+ maxTokens: opts.maxTokens !== void 0 ? Number(opts.maxTokens) : void 0,
1696
+ enableWebSearch: opts.webSearch,
1697
+ disableThinking: opts.thinking === false
1698
+ });
1699
+ spinner.succeed("Inference complete");
1700
+ if (opts.json) {
1701
+ console.log(JSON.stringify(result, null, 2));
1702
+ } else {
1703
+ console.log();
1704
+ console.log(result.content);
1705
+ console.log();
1706
+ console.log(chalk2.dim(`Model: ${result.model} | Tokens: ${result.usage.promptTokens} in, ${result.usage.completionTokens} out, ${result.usage.totalTokens} total`));
1707
+ }
1708
+ } catch (err) {
1709
+ spinner.fail("Inference failed");
1710
+ console.error(chalk2.red(err instanceof Error ? err.message : String(err)));
1711
+ process.exit(1);
1712
+ }
1713
+ });
1150
1714
  }
1151
1715
 
1152
1716
  // src/commands/allowance.ts
1153
- import { parseUnits as parseUnits7, formatUnits as formatUnits4, isAddress as isAddress3 } from "viem";
1717
+ import { parseUnits as parseUnits6, formatUnits as formatUnits3, isAddress as isAddress3 } from "viem";
1154
1718
  import chalk3 from "chalk";
1155
1719
  import ora3 from "ora";
1156
1720
 
1157
1721
  // src/strategies/allowance-disburse.ts
1158
- import { encodeFunctionData as encodeFunctionData4, parseUnits as parseUnits6 } from "viem";
1159
- var ERC20_ABI4 = [
1722
+ import { encodeFunctionData as encodeFunctionData7, parseUnits as parseUnits5 } from "viem";
1723
+ var ERC20_ABI3 = [
1160
1724
  {
1161
1725
  name: "approve",
1162
1726
  type: "function",
@@ -1218,15 +1782,15 @@ var SWAP_ROUTER_EXACT_INPUT_ABI2 = [
1218
1782
  }
1219
1783
  ];
1220
1784
  function buildDisburseBatch(config, vaultAddress, agents, assetAddress, assetDecimals, minUsdc, swapPath) {
1221
- const assetAmount = parseUnits6(config.amount, assetDecimals);
1785
+ const assetAmount = parseUnits5(config.amount, assetDecimals);
1222
1786
  const isUsdc = assetAddress.toLowerCase() === TOKENS().USDC.toLowerCase();
1223
1787
  const isWeth = assetAddress.toLowerCase() === TOKENS().WETH.toLowerCase();
1224
1788
  const calls = [];
1225
1789
  if (!isUsdc) {
1226
1790
  calls.push({
1227
1791
  target: assetAddress,
1228
- data: encodeFunctionData4({
1229
- abi: ERC20_ABI4,
1792
+ data: encodeFunctionData7({
1793
+ abi: ERC20_ABI3,
1230
1794
  functionName: "approve",
1231
1795
  args: [UNISWAP().SWAP_ROUTER, assetAmount]
1232
1796
  }),
@@ -1235,7 +1799,7 @@ function buildDisburseBatch(config, vaultAddress, agents, assetAddress, assetDec
1235
1799
  if (isWeth) {
1236
1800
  calls.push({
1237
1801
  target: UNISWAP().SWAP_ROUTER,
1238
- data: encodeFunctionData4({
1802
+ data: encodeFunctionData7({
1239
1803
  abi: SWAP_ROUTER_EXACT_INPUT_SINGLE_ABI2,
1240
1804
  functionName: "exactInputSingle",
1241
1805
  args: [
@@ -1255,7 +1819,7 @@ function buildDisburseBatch(config, vaultAddress, agents, assetAddress, assetDec
1255
1819
  } else {
1256
1820
  calls.push({
1257
1821
  target: UNISWAP().SWAP_ROUTER,
1258
- data: encodeFunctionData4({
1822
+ data: encodeFunctionData7({
1259
1823
  abi: SWAP_ROUTER_EXACT_INPUT_ABI2,
1260
1824
  functionName: "exactInput",
1261
1825
  args: [
@@ -1275,8 +1839,8 @@ function buildDisburseBatch(config, vaultAddress, agents, assetAddress, assetDec
1275
1839
  for (const agent of agents) {
1276
1840
  calls.push({
1277
1841
  target: TOKENS().USDC,
1278
- data: encodeFunctionData4({
1279
- abi: ERC20_ABI4,
1842
+ data: encodeFunctionData7({
1843
+ abi: ERC20_ABI3,
1280
1844
  functionName: "transfer",
1281
1845
  args: [agent, perAgent]
1282
1846
  }),
@@ -1287,7 +1851,7 @@ function buildDisburseBatch(config, vaultAddress, agents, assetAddress, assetDec
1287
1851
  }
1288
1852
 
1289
1853
  // src/commands/allowance.ts
1290
- var VALID_FEES3 = [500, 3e3, 1e4];
1854
+ var VALID_FEES2 = [500, 3e3, 1e4];
1291
1855
  function registerAllowanceCommands(program2) {
1292
1856
  const allowance = program2.command("allowance").description("Disburse vault profits to agent wallets");
1293
1857
  allowance.command("disburse").description("Swap vault profits \u2192 USDC \u2192 distribute to all agent operator wallets").requiredOption("--vault <address>", "Vault address").requiredOption("--amount <amount>", "Deposit token amount to convert & distribute (e.g. 500)").option("--fee <tier>", "Fee tier for asset \u2192 USDC swap (500, 3000, 10000)", "3000").option("--slippage <bps>", "Slippage tolerance in bps", "100").option("--execute", "Execute on-chain (default: simulate only)", false).action(async (opts) => {
@@ -1297,8 +1861,8 @@ function registerAllowanceCommands(program2) {
1297
1861
  process.exit(1);
1298
1862
  }
1299
1863
  const fee = Number(opts.fee);
1300
- if (!VALID_FEES3.includes(fee)) {
1301
- console.error(chalk3.red(`Invalid fee tier. Valid: ${VALID_FEES3.join(", ")}`));
1864
+ if (!VALID_FEES2.includes(fee)) {
1865
+ console.error(chalk3.red(`Invalid fee tier. Valid: ${VALID_FEES2.join(", ")}`));
1302
1866
  process.exit(1);
1303
1867
  }
1304
1868
  const slippageBps = Number(opts.slippage);
@@ -1331,7 +1895,7 @@ function registerAllowanceCommands(program2) {
1331
1895
  console.error(chalk3.red("No agents registered in vault. Register agents first."));
1332
1896
  process.exit(1);
1333
1897
  }
1334
- const requestedAmount = parseUnits7(opts.amount, assetDecimals);
1898
+ const requestedAmount = parseUnits6(opts.amount, assetDecimals);
1335
1899
  const profit = assetBalance > totalDeposited ? assetBalance - totalDeposited : 0n;
1336
1900
  const isUsdc = assetAddress.toLowerCase() === TOKENS().USDC.toLowerCase();
1337
1901
  const isWeth = assetAddress.toLowerCase() === TOKENS().WETH.toLowerCase();
@@ -1340,9 +1904,9 @@ function registerAllowanceCommands(program2) {
1340
1904
  console.log(chalk3.dim("\u2500".repeat(40)));
1341
1905
  console.log(` Asset: ${assetSymbol} (${assetDecimals} decimals)`);
1342
1906
  console.log(` Amount: ${opts.amount} ${assetSymbol}`);
1343
- console.log(` Vault balance: ${formatUnits4(assetBalance, assetDecimals)} ${assetSymbol}`);
1344
- console.log(` Deposited: ${formatUnits4(totalDeposited, assetDecimals)} ${assetSymbol}`);
1345
- console.log(` Profit: ${formatUnits4(profit, assetDecimals)} ${assetSymbol}`);
1907
+ console.log(` Vault balance: ${formatUnits3(assetBalance, assetDecimals)} ${assetSymbol}`);
1908
+ console.log(` Deposited: ${formatUnits3(totalDeposited, assetDecimals)} ${assetSymbol}`);
1909
+ console.log(` Profit: ${formatUnits3(profit, assetDecimals)} ${assetSymbol}`);
1346
1910
  console.log(` Agents: ${agents.length} (USDC will be split equally)`);
1347
1911
  if (!isUsdc) {
1348
1912
  console.log(` Routing: ${isWeth ? `WETH \u2192 USDC (fee ${fee})` : `${assetSymbol} \u2192 WETH \u2192 USDC (fee ${fee})`}`);
@@ -1351,7 +1915,7 @@ function registerAllowanceCommands(program2) {
1351
1915
  console.log(` Vault: ${vaultAddress}`);
1352
1916
  console.log();
1353
1917
  if (requestedAmount > profit) {
1354
- console.warn(chalk3.yellow(` Warning: amount (${opts.amount}) exceeds available profit (${formatUnits4(profit, assetDecimals)})`));
1918
+ console.warn(chalk3.yellow(` Warning: amount (${opts.amount}) exceeds available profit (${formatUnits3(profit, assetDecimals)})`));
1355
1919
  console.warn(chalk3.yellow(" This will use deposited capital, not just profits."));
1356
1920
  console.log();
1357
1921
  }
@@ -1385,7 +1949,7 @@ function registerAllowanceCommands(program2) {
1385
1949
  }
1386
1950
  minUsdc = applySlippage(amountOut, slippageBps);
1387
1951
  quoteSpinner.succeed(
1388
- `Quote: ${formatUnits4(amountOut, 6)} USDC (min: ${formatUnits4(minUsdc, 6)}, per agent: ${formatUnits4(minUsdc / BigInt(agents.length), 6)})`
1952
+ `Quote: ${formatUnits3(amountOut, 6)} USDC (min: ${formatUnits3(minUsdc, 6)}, per agent: ${formatUnits3(minUsdc / BigInt(agents.length), 6)})`
1389
1953
  );
1390
1954
  } catch (err) {
1391
1955
  quoteSpinner.fail("Failed to fetch quote");
@@ -1395,7 +1959,7 @@ function registerAllowanceCommands(program2) {
1395
1959
  }
1396
1960
  const perAgent = minUsdc / BigInt(agents.length);
1397
1961
  if (isUsdc) {
1398
- console.log(chalk3.dim(` Per agent: ${formatUnits4(perAgent, 6)} USDC`));
1962
+ console.log(chalk3.dim(` Per agent: ${formatUnits3(perAgent, 6)} USDC`));
1399
1963
  console.log();
1400
1964
  }
1401
1965
  const config = {
@@ -1462,14 +2026,14 @@ function registerAllowanceCommands(program2) {
1462
2026
  console.log(chalk3.dim("\u2500".repeat(50)));
1463
2027
  console.log(chalk3.bold("\n Vault"));
1464
2028
  console.log(` Asset: ${assetSymbol}`);
1465
- console.log(` Balance: ${formatUnits4(assetBalance, assetDecimals)} ${assetSymbol}`);
1466
- console.log(` Deposited: ${formatUnits4(totalDeposited, assetDecimals)} ${assetSymbol}`);
1467
- console.log(` Profit: ${formatUnits4(profit, assetDecimals)} ${assetSymbol}`);
2029
+ console.log(` Balance: ${formatUnits3(assetBalance, assetDecimals)} ${assetSymbol}`);
2030
+ console.log(` Deposited: ${formatUnits3(totalDeposited, assetDecimals)} ${assetSymbol}`);
2031
+ console.log(` Profit: ${formatUnits3(profit, assetDecimals)} ${assetSymbol}`);
1468
2032
  console.log(chalk3.bold("\n Agent USDC Balances"));
1469
2033
  for (const { agent, balance } of agentBalances) {
1470
2034
  const isMe = agent.toLowerCase() === account.address.toLowerCase();
1471
2035
  const label = isMe ? chalk3.green(`${agent} (you)`) : agent;
1472
- console.log(` ${label}: ${formatUnits4(balance, 6)} USDC`);
2036
+ console.log(` ${label}: ${formatUnits3(balance, 6)} USDC`);
1473
2037
  }
1474
2038
  console.log();
1475
2039
  } catch (err) {
@@ -1641,348 +2205,7 @@ function registerIdentityCommands(program2) {
1641
2205
  import { isAddress as isAddress4 } from "viem";
1642
2206
  import chalk5 from "chalk";
1643
2207
  import ora5 from "ora";
1644
- import { readFileSync } from "fs";
1645
-
1646
- // src/lib/governor.ts
1647
- var PROPOSAL_STATES = [
1648
- "Draft",
1649
- "Pending",
1650
- "Approved",
1651
- "Rejected",
1652
- "Expired",
1653
- "Executed",
1654
- "Settled",
1655
- "Cancelled"
1656
- ];
1657
- var PROPOSAL_STATE = {
1658
- Draft: 0,
1659
- Pending: 1,
1660
- Approved: 2,
1661
- Rejected: 3,
1662
- Expired: 4,
1663
- Executed: 5,
1664
- Settled: 6,
1665
- Cancelled: 7
1666
- };
1667
- var VOTE_TYPE = {
1668
- For: 0,
1669
- Against: 1,
1670
- Abstain: 2
1671
- };
1672
- function parseDuration(input2) {
1673
- const match = input2.match(/^(\d+)(d|h|m|s)?$/);
1674
- if (!match) throw new Error(`Invalid duration: ${input2}`);
1675
- const value = BigInt(match[1]);
1676
- switch (match[2]) {
1677
- case "d":
1678
- return value * 86400n;
1679
- case "h":
1680
- return value * 3600n;
1681
- case "m":
1682
- return value * 60n;
1683
- case "s":
1684
- case void 0:
1685
- return value;
1686
- default:
1687
- throw new Error(`Unknown duration unit: ${match[2]}`);
1688
- }
1689
- }
1690
- function getGovernorAddress() {
1691
- return SHERWOOD().GOVERNOR;
1692
- }
1693
- async function getGovernorParams() {
1694
- const client = getPublicClient();
1695
- const result = await client.readContract({
1696
- address: getGovernorAddress(),
1697
- abi: SYNDICATE_GOVERNOR_ABI,
1698
- functionName: "getGovernorParams"
1699
- });
1700
- return result;
1701
- }
1702
- async function getProposal(id) {
1703
- const client = getPublicClient();
1704
- const result = await client.readContract({
1705
- address: getGovernorAddress(),
1706
- abi: SYNDICATE_GOVERNOR_ABI,
1707
- functionName: "getProposal",
1708
- args: [id]
1709
- });
1710
- return result;
1711
- }
1712
- async function getProposalState(id) {
1713
- const client = getPublicClient();
1714
- return client.readContract({
1715
- address: getGovernorAddress(),
1716
- abi: SYNDICATE_GOVERNOR_ABI,
1717
- functionName: "getProposalState",
1718
- args: [id]
1719
- });
1720
- }
1721
- async function proposalCount() {
1722
- const client = getPublicClient();
1723
- return client.readContract({
1724
- address: getGovernorAddress(),
1725
- abi: SYNDICATE_GOVERNOR_ABI,
1726
- functionName: "proposalCount"
1727
- });
1728
- }
1729
- async function getVoteWeight(proposalId, voter) {
1730
- const client = getPublicClient();
1731
- return client.readContract({
1732
- address: getGovernorAddress(),
1733
- abi: SYNDICATE_GOVERNOR_ABI,
1734
- functionName: "getVoteWeight",
1735
- args: [proposalId, voter]
1736
- });
1737
- }
1738
- async function hasVoted(proposalId, voter) {
1739
- const client = getPublicClient();
1740
- return client.readContract({
1741
- address: getGovernorAddress(),
1742
- abi: SYNDICATE_GOVERNOR_ABI,
1743
- functionName: "hasVoted",
1744
- args: [proposalId, voter]
1745
- });
1746
- }
1747
- async function getExecuteCalls(proposalId) {
1748
- const client = getPublicClient();
1749
- const result = await client.readContract({
1750
- address: getGovernorAddress(),
1751
- abi: SYNDICATE_GOVERNOR_ABI,
1752
- functionName: "getExecuteCalls",
1753
- args: [proposalId]
1754
- });
1755
- return result.map((c) => ({ target: c.target, data: c.data, value: c.value }));
1756
- }
1757
- async function getSettlementCalls(proposalId) {
1758
- const client = getPublicClient();
1759
- const result = await client.readContract({
1760
- address: getGovernorAddress(),
1761
- abi: SYNDICATE_GOVERNOR_ABI,
1762
- functionName: "getSettlementCalls",
1763
- args: [proposalId]
1764
- });
1765
- return result.map((c) => ({ target: c.target, data: c.data, value: c.value }));
1766
- }
1767
- async function getRegisteredVaults() {
1768
- const client = getPublicClient();
1769
- return client.readContract({
1770
- address: getGovernorAddress(),
1771
- abi: SYNDICATE_GOVERNOR_ABI,
1772
- functionName: "getRegisteredVaults"
1773
- });
1774
- }
1775
- async function getCapitalSnapshot(proposalId) {
1776
- const client = getPublicClient();
1777
- return client.readContract({
1778
- address: getGovernorAddress(),
1779
- abi: SYNDICATE_GOVERNOR_ABI,
1780
- functionName: "getCapitalSnapshot",
1781
- args: [proposalId]
1782
- });
1783
- }
1784
- async function propose(vault, metadataURI, performanceFeeBps, strategyDuration, executeCalls, settlementCalls, coProposers = []) {
1785
- const wallet = getWalletClient();
1786
- const client = getPublicClient();
1787
- const hash = await wallet.writeContract({
1788
- account: getAccount(),
1789
- chain: getChain(),
1790
- address: getGovernorAddress(),
1791
- abi: SYNDICATE_GOVERNOR_ABI,
1792
- functionName: "propose",
1793
- args: [vault, metadataURI, performanceFeeBps, strategyDuration, executeCalls, settlementCalls, coProposers]
1794
- });
1795
- const receipt = await client.waitForTransactionReceipt({ hash });
1796
- let proposalId;
1797
- try {
1798
- proposalId = await proposalCount();
1799
- } catch {
1800
- proposalId = 0n;
1801
- }
1802
- return { hash: receipt.transactionHash, proposalId };
1803
- }
1804
- async function vote(proposalId, support) {
1805
- const wallet = getWalletClient();
1806
- const client = getPublicClient();
1807
- const hash = await wallet.writeContract({
1808
- account: getAccount(),
1809
- chain: getChain(),
1810
- address: getGovernorAddress(),
1811
- abi: SYNDICATE_GOVERNOR_ABI,
1812
- functionName: "vote",
1813
- args: [proposalId, support]
1814
- });
1815
- const receipt = await client.waitForTransactionReceipt({ hash });
1816
- return receipt.transactionHash;
1817
- }
1818
- async function executeProposal(proposalId) {
1819
- const wallet = getWalletClient();
1820
- const client = getPublicClient();
1821
- const hash = await wallet.writeContract({
1822
- account: getAccount(),
1823
- chain: getChain(),
1824
- address: getGovernorAddress(),
1825
- abi: SYNDICATE_GOVERNOR_ABI,
1826
- functionName: "executeProposal",
1827
- args: [proposalId]
1828
- });
1829
- const receipt = await client.waitForTransactionReceipt({ hash });
1830
- return receipt.transactionHash;
1831
- }
1832
- async function settleProposal(proposalId) {
1833
- const wallet = getWalletClient();
1834
- const client = getPublicClient();
1835
- const hash = await wallet.writeContract({
1836
- account: getAccount(),
1837
- chain: getChain(),
1838
- address: getGovernorAddress(),
1839
- abi: SYNDICATE_GOVERNOR_ABI,
1840
- functionName: "settleProposal",
1841
- args: [proposalId]
1842
- });
1843
- const receipt = await client.waitForTransactionReceipt({ hash });
1844
- return receipt.transactionHash;
1845
- }
1846
- async function emergencySettle(proposalId, calls) {
1847
- const wallet = getWalletClient();
1848
- const client = getPublicClient();
1849
- const hash = await wallet.writeContract({
1850
- account: getAccount(),
1851
- chain: getChain(),
1852
- address: getGovernorAddress(),
1853
- abi: SYNDICATE_GOVERNOR_ABI,
1854
- functionName: "emergencySettle",
1855
- args: [proposalId, calls]
1856
- });
1857
- const receipt = await client.waitForTransactionReceipt({ hash });
1858
- return receipt.transactionHash;
1859
- }
1860
- async function cancelProposal(proposalId) {
1861
- const wallet = getWalletClient();
1862
- const client = getPublicClient();
1863
- const hash = await wallet.writeContract({
1864
- account: getAccount(),
1865
- chain: getChain(),
1866
- address: getGovernorAddress(),
1867
- abi: SYNDICATE_GOVERNOR_ABI,
1868
- functionName: "cancelProposal",
1869
- args: [proposalId]
1870
- });
1871
- const receipt = await client.waitForTransactionReceipt({ hash });
1872
- return receipt.transactionHash;
1873
- }
1874
- async function emergencyCancel(proposalId) {
1875
- const wallet = getWalletClient();
1876
- const client = getPublicClient();
1877
- const hash = await wallet.writeContract({
1878
- account: getAccount(),
1879
- chain: getChain(),
1880
- address: getGovernorAddress(),
1881
- abi: SYNDICATE_GOVERNOR_ABI,
1882
- functionName: "emergencyCancel",
1883
- args: [proposalId]
1884
- });
1885
- const receipt = await client.waitForTransactionReceipt({ hash });
1886
- return receipt.transactionHash;
1887
- }
1888
- async function setVotingPeriod(seconds) {
1889
- const wallet = getWalletClient();
1890
- const client = getPublicClient();
1891
- const hash = await wallet.writeContract({
1892
- account: getAccount(),
1893
- chain: getChain(),
1894
- address: getGovernorAddress(),
1895
- abi: SYNDICATE_GOVERNOR_ABI,
1896
- functionName: "setVotingPeriod",
1897
- args: [seconds]
1898
- });
1899
- const receipt = await client.waitForTransactionReceipt({ hash });
1900
- return receipt.transactionHash;
1901
- }
1902
- async function setExecutionWindow(seconds) {
1903
- const wallet = getWalletClient();
1904
- const client = getPublicClient();
1905
- const hash = await wallet.writeContract({
1906
- account: getAccount(),
1907
- chain: getChain(),
1908
- address: getGovernorAddress(),
1909
- abi: SYNDICATE_GOVERNOR_ABI,
1910
- functionName: "setExecutionWindow",
1911
- args: [seconds]
1912
- });
1913
- const receipt = await client.waitForTransactionReceipt({ hash });
1914
- return receipt.transactionHash;
1915
- }
1916
- async function setVetoThresholdBps(bps) {
1917
- const wallet = getWalletClient();
1918
- const client = getPublicClient();
1919
- const hash = await wallet.writeContract({
1920
- account: getAccount(),
1921
- chain: getChain(),
1922
- address: getGovernorAddress(),
1923
- abi: SYNDICATE_GOVERNOR_ABI,
1924
- functionName: "setVetoThresholdBps",
1925
- args: [bps]
1926
- });
1927
- const receipt = await client.waitForTransactionReceipt({ hash });
1928
- return receipt.transactionHash;
1929
- }
1930
- async function setMaxPerformanceFeeBps(bps) {
1931
- const wallet = getWalletClient();
1932
- const client = getPublicClient();
1933
- const hash = await wallet.writeContract({
1934
- account: getAccount(),
1935
- chain: getChain(),
1936
- address: getGovernorAddress(),
1937
- abi: SYNDICATE_GOVERNOR_ABI,
1938
- functionName: "setMaxPerformanceFeeBps",
1939
- args: [bps]
1940
- });
1941
- const receipt = await client.waitForTransactionReceipt({ hash });
1942
- return receipt.transactionHash;
1943
- }
1944
- async function setMaxStrategyDuration(seconds) {
1945
- const wallet = getWalletClient();
1946
- const client = getPublicClient();
1947
- const hash = await wallet.writeContract({
1948
- account: getAccount(),
1949
- chain: getChain(),
1950
- address: getGovernorAddress(),
1951
- abi: SYNDICATE_GOVERNOR_ABI,
1952
- functionName: "setMaxStrategyDuration",
1953
- args: [seconds]
1954
- });
1955
- const receipt = await client.waitForTransactionReceipt({ hash });
1956
- return receipt.transactionHash;
1957
- }
1958
- async function setCooldownPeriod(seconds) {
1959
- const wallet = getWalletClient();
1960
- const client = getPublicClient();
1961
- const hash = await wallet.writeContract({
1962
- account: getAccount(),
1963
- chain: getChain(),
1964
- address: getGovernorAddress(),
1965
- abi: SYNDICATE_GOVERNOR_ABI,
1966
- functionName: "setCooldownPeriod",
1967
- args: [seconds]
1968
- });
1969
- const receipt = await client.waitForTransactionReceipt({ hash });
1970
- return receipt.transactionHash;
1971
- }
1972
- async function setProtocolFeeBps(bps) {
1973
- const wallet = getWalletClient();
1974
- const client = getPublicClient();
1975
- const hash = await wallet.writeContract({
1976
- account: getAccount(),
1977
- chain: getChain(),
1978
- address: getGovernorAddress(),
1979
- abi: SYNDICATE_GOVERNOR_ABI,
1980
- functionName: "setProtocolFeeBps",
1981
- args: [bps]
1982
- });
1983
- const receipt = await client.waitForTransactionReceipt({ hash });
1984
- return receipt.transactionHash;
1985
- }
2208
+ import { readFileSync as readFileSync2 } from "fs";
1986
2209
 
1987
2210
  // src/lib/format.ts
1988
2211
  function formatDurationShort(seconds) {
@@ -2026,7 +2249,7 @@ function formatTimestamp(ts) {
2026
2249
  return new Date(Number(ts) * 1e3).toLocaleString();
2027
2250
  }
2028
2251
  function parseCallsFile(path) {
2029
- const raw = readFileSync(path, "utf-8");
2252
+ const raw = readFileSync2(path, "utf-8");
2030
2253
  const parsed = JSON.parse(raw);
2031
2254
  return parsed.map((c) => ({
2032
2255
  target: c.target,
@@ -2512,7 +2735,7 @@ try {
2512
2735
  var require2 = createRequire(import.meta.url);
2513
2736
  var { version: CLI_VERSION } = require2("../package.json");
2514
2737
  async function loadXmtp() {
2515
- return import("./xmtp-BSTLCZWG.js");
2738
+ return import("./xmtp-2AHDKL2Q.js");
2516
2739
  }
2517
2740
  async function loadCron() {
2518
2741
  return import("./cron-SKYKVZ6K.js");
@@ -3189,7 +3412,7 @@ var vaultCmd = program.command("vault");
3189
3412
  vaultCmd.command("deposit").description("Deposit into a vault").option("--vault <address>", "Vault address (default: from config)").requiredOption("--amount <amount>", "Amount to deposit (in asset units)").action(async (opts) => {
3190
3413
  resolveVault(opts);
3191
3414
  const decimals = await getAssetDecimals();
3192
- const amount = parseUnits8(opts.amount, decimals);
3415
+ const amount = parseUnits7(opts.amount, decimals);
3193
3416
  const spinner = ora7(`Depositing ${opts.amount}...`).start();
3194
3417
  try {
3195
3418
  const hash = await deposit(amount);
@@ -3241,81 +3464,8 @@ vaultCmd.command("balance").description("Show LP share balance and asset value")
3241
3464
  process.exit(1);
3242
3465
  }
3243
3466
  });
3244
- var strategy = program.command("strategy");
3245
- strategy.command("list").description("List registered strategies").option("--type <id>", "Filter by strategy type").action(async (opts) => {
3246
- const spinner = ora7("Loading strategies...").start();
3247
- try {
3248
- const strategies = await listStrategies(
3249
- opts.type ? BigInt(opts.type) : void 0
3250
- );
3251
- spinner.stop();
3252
- if (strategies.length === 0) {
3253
- console.log(chalk7.dim("No strategies registered."));
3254
- return;
3255
- }
3256
- console.log();
3257
- console.log(chalk7.bold(`Strategies (${strategies.length})`));
3258
- console.log(chalk7.dim("\u2500".repeat(70)));
3259
- for (const s of strategies) {
3260
- const status = s.active ? chalk7.green("active") : chalk7.red("inactive");
3261
- console.log(` #${s.id} ${chalk7.bold(s.name)} [type: ${s.strategyTypeId}] ${status}`);
3262
- console.log(` Creator: ${s.creator}`);
3263
- console.log(` Implementation: ${s.implementation}`);
3264
- if (s.metadataURI) {
3265
- console.log(` Metadata: ${chalk7.dim(s.metadataURI)}`);
3266
- }
3267
- console.log();
3268
- }
3269
- } catch (err) {
3270
- spinner.fail("Failed to load strategies");
3271
- console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
3272
- process.exit(1);
3273
- }
3274
- });
3275
- strategy.command("info").description("Show strategy details").argument("<id>", "Strategy ID").action(async (idStr) => {
3276
- const spinner = ora7("Loading strategy...").start();
3277
- try {
3278
- const s = await getStrategy(BigInt(idStr));
3279
- spinner.stop();
3280
- console.log();
3281
- console.log(chalk7.bold(`Strategy #${s.id}`));
3282
- console.log(chalk7.dim("\u2500".repeat(40)));
3283
- console.log(` Name: ${s.name}`);
3284
- console.log(` Type: ${s.strategyTypeId}`);
3285
- console.log(` Active: ${s.active ? chalk7.green("yes") : chalk7.red("no")}`);
3286
- console.log(` Creator: ${s.creator}`);
3287
- console.log(` Implementation: ${s.implementation}`);
3288
- if (s.metadataURI) {
3289
- console.log(` Metadata: ${chalk7.dim(s.metadataURI)}`);
3290
- }
3291
- console.log();
3292
- } catch (err) {
3293
- spinner.fail("Failed to load strategy");
3294
- console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
3295
- process.exit(1);
3296
- }
3297
- });
3298
- strategy.command("register").description("Register a new strategy on-chain").requiredOption("--implementation <address>", "Strategy contract address").requiredOption("--type <id>", "Strategy type ID").requiredOption("--name <name>", "Strategy name").option("--metadata <uri>", "Metadata URI (IPFS/Arweave)", "").action(async (opts) => {
3299
- const spinner = ora7("Registering strategy...").start();
3300
- try {
3301
- const hash = await registerStrategy(
3302
- opts.implementation,
3303
- BigInt(opts.type),
3304
- opts.name,
3305
- opts.metadata
3306
- );
3307
- spinner.succeed(`Strategy registered: ${hash}`);
3308
- console.log(chalk7.dim(` ${getExplorerUrl(hash)}`));
3309
- } catch (err) {
3310
- spinner.fail("Registration failed");
3311
- console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
3312
- process.exit(1);
3313
- }
3314
- });
3315
- strategy.command("run").description("Execute the levered swap strategy").option("--vault <address>", "Vault address (default: from config)").requiredOption("--collateral <amount>", "WETH collateral amount (e.g. 1.0)").requiredOption("--borrow <amount>", "USDC to borrow against collateral").requiredOption("--token <address>", "Target token address to buy").option("--fee <tier>", "Uniswap fee tier in bps (500, 3000, 10000)", "500").option("--slippage <bps>", "Slippage tolerance in bps", "100").option("--execute", "Actually execute on-chain (default: simulate only)", false).action(async (opts) => {
3316
- resolveVault(opts);
3317
- await runLeveredSwap(opts);
3318
- });
3467
+ var strategy = program.command("strategy").description("Strategy templates \u2014 list, clone, propose");
3468
+ registerStrategyTemplateCommands(strategy);
3319
3469
  program.command("providers").description("List available DeFi providers").action(async () => {
3320
3470
  const { MessariProvider, NansenProvider } = await import("./research-IUHVRHR3.js");
3321
3471
  const providers = [new MoonwellProvider(), new UniswapProvider(), new MessariProvider(), new NansenProvider()];
@@ -3328,7 +3478,7 @@ ${info.name} (${info.type})`);
3328
3478
  }
3329
3479
  });
3330
3480
  try {
3331
- const { registerChatCommands } = await import("./chat-A2ORZVU3.js");
3481
+ const { registerChatCommands } = await import("./chat-XTOTPTGV.js");
3332
3482
  registerChatCommands(program);
3333
3483
  } catch {
3334
3484
  program.command("chat <name> [action] [actionArgs...]").description("Syndicate chat (XMTP) \u2014 requires @xmtp/cli").action(() => {
@@ -3338,14 +3488,14 @@ try {
3338
3488
  process.exit(1);
3339
3489
  });
3340
3490
  }
3341
- var { registerSessionCommands } = await import("./session-3YRZI426.js");
3491
+ var { registerSessionCommands } = await import("./session-RDBY24HJ.js");
3342
3492
  registerSessionCommands(program);
3343
3493
  registerVeniceCommands(program);
3344
3494
  registerAllowanceCommands(program);
3345
3495
  registerIdentityCommands(program);
3346
3496
  registerProposalCommands(program);
3347
3497
  registerGovernorCommands(program);
3348
- var { registerResearchCommands } = await import("./research-63D7YID3.js");
3498
+ var { registerResearchCommands } = await import("./research-BYQZ2MLK.js");
3349
3499
  registerResearchCommands(program);
3350
3500
  var configCmd = program.command("config");
3351
3501
  configCmd.command("set").description("Save settings to ~/.sherwood/config.json (persists across sessions)").option("--private-key <key>", "Wallet private key (0x-prefixed)").option("--vault <address>", "Default SyndicateVault address").option("--rpc <url>", "Custom RPC URL for the active --chain network").option("--notify-to <id>", "Destination for cron summaries (Telegram chat ID, phone, etc.)").action((opts) => {