@sherwoodagent/cli 0.15.3 → 0.17.1

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-A4F6KTXA.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-X3L57WGW.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-EMPYAZNP.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-UQKNCRDW.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,622 @@ 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 };
331
- }
332
- function applySlippage(amountOut, slippageBps) {
333
- return amountOut * BigInt(1e4 - slippageBps) / 10000n;
336
+ ];
337
+ function buildInitData3(params) {
338
+ return encodeAbiParameters3(INIT_PARAMS_TYPES2, [params]);
334
339
  }
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 }));
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
344
367
  }
345
- }
346
- return concat(parts);
347
- }
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");
361
- }
362
- const [amountOut, , , gasEstimate] = decodeFunctionResult({
363
- abi: UNISWAP_QUOTER_V2_ABI,
364
- functionName: "quoteExactInput",
365
- data
366
- });
367
- return { amountOut, sqrtPriceX96After: 0n, gasEstimate };
368
+ ];
368
369
  }
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");
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
379
+ }
380
+ ];
376
381
  }
377
382
 
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}`));
383
+ // src/commands/strategy-template.ts
384
+ var ZERO = "0x0000000000000000000000000000000000000000";
385
+ var TEMPLATES = [
386
+ {
387
+ name: "Moonwell Supply",
388
+ key: "moonwell-supply",
389
+ description: "Supply tokens to Moonwell lending market, earn yield",
390
+ addressKey: "MOONWELL_SUPPLY"
391
+ },
392
+ {
393
+ name: "Aerodrome LP",
394
+ key: "aerodrome-lp",
395
+ description: "Provide liquidity on Aerodrome DEX + optional gauge staking",
396
+ addressKey: "AERODROME_LP"
397
+ },
398
+ {
399
+ name: "Venice Inference",
400
+ key: "venice-inference",
401
+ description: "Stake VVV for sVVV \u2014 Venice private AI inference",
402
+ addressKey: "VENICE_INFERENCE"
403
+ }
404
+ ];
405
+ function resolveTemplate(key) {
406
+ const def = TEMPLATES.find((t) => t.key === key);
407
+ if (!def) {
408
+ console.error(chalk.red(`Unknown template: ${key}`));
409
+ console.error(chalk.dim(`Available: ${TEMPLATES.map((t) => t.key).join(", ")}`));
384
410
  process.exit(1);
385
411
  }
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(", ")}`));
412
+ const address = STRATEGY_TEMPLATES()[def.addressKey];
413
+ if (address === ZERO) {
414
+ console.error(chalk.red(`Template "${def.name}" not deployed on this network.`));
390
415
  process.exit(1);
391
416
  }
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
- })
417
+ return { def, address };
418
+ }
419
+ async function buildInitDataForTemplate(templateKey, opts, vault) {
420
+ if (templateKey === "moonwell-supply") {
421
+ if (!opts.amount) {
422
+ console.error(chalk.red("--amount is required for moonwell-supply template"));
423
+ process.exit(1);
424
+ }
425
+ const token = opts.token || "USDC";
426
+ const underlying = resolveToken(token);
427
+ const mToken = resolveMToken(token);
428
+ const decimals = token.toUpperCase() === "USDC" ? 6 : 18;
429
+ const supplyAmount = parseUnits(opts.amount, decimals);
430
+ const minRedeem = parseUnits(opts.minRedeem || opts.amount, decimals);
431
+ return {
432
+ initData: buildInitData(underlying, mToken, supplyAmount, minRedeem),
433
+ asset: underlying,
434
+ assetAmount: supplyAmount
435
+ };
436
+ }
437
+ if (templateKey === "venice-inference") {
438
+ if (!opts.amount) {
439
+ console.error(chalk.red("--amount is required for venice-inference template"));
440
+ process.exit(1);
441
+ }
442
+ const assetSymbol = opts.asset || "USDC";
443
+ const asset = resolveToken(assetSymbol);
444
+ const vvv = VENICE().VVV;
445
+ const isDirect = asset.toLowerCase() === vvv.toLowerCase();
446
+ const decimals = assetSymbol.toUpperCase() === "USDC" ? 6 : 18;
447
+ const assetAmount = parseUnits(opts.amount, decimals);
448
+ const agent = opts.agent || getAccount().address;
449
+ const params = {
450
+ asset,
451
+ weth: isDirect ? ZERO : TOKENS().WETH,
452
+ vvv,
453
+ sVVV: VENICE().STAKING,
454
+ aeroRouter: isDirect ? ZERO : AERODROME().ROUTER,
455
+ aeroFactory: isDirect ? ZERO : AERODROME().FACTORY,
456
+ agent,
457
+ assetAmount,
458
+ minVVV: isDirect ? 0n : parseUnits(opts.minVvv || "0", 18),
459
+ deadlineOffset: 300n,
460
+ singleHop: !!opts.singleHop
461
+ };
462
+ return {
463
+ initData: buildInitData2(params),
464
+ asset,
465
+ assetAmount
466
+ };
467
+ }
468
+ if (templateKey === "aerodrome-lp") {
469
+ for (const flag of ["tokenA", "tokenB", "amountA", "amountB", "lpToken"]) {
470
+ if (!opts[flag]) {
471
+ console.error(chalk.red(`--${flag.replace(/([A-Z])/g, "-$1").toLowerCase()} is required for aerodrome-lp template`));
472
+ process.exit(1);
473
+ }
474
+ }
475
+ const tokenA = opts.tokenA;
476
+ const tokenB = opts.tokenB;
477
+ const publicClient = getPublicClient();
478
+ const [decimalsA, decimalsB] = await Promise.all([
479
+ publicClient.readContract({ address: tokenA, abi: erc20Abi, functionName: "decimals" }),
480
+ publicClient.readContract({ address: tokenB, abi: erc20Abi, functionName: "decimals" })
408
481
  ]);
409
- } catch {
410
- console.error(chalk.red(`Could not read token decimals \u2014 are the addresses valid ERC20s?`));
411
- process.exit(1);
482
+ const amountA = parseUnits(opts.amountA, decimalsA);
483
+ const amountB = parseUnits(opts.amountB, decimalsB);
484
+ const minAOut = parseUnits(opts.minAOut || "0", decimalsA);
485
+ const minBOut = parseUnits(opts.minBOut || "0", decimalsB);
486
+ const params = {
487
+ tokenA,
488
+ tokenB,
489
+ stable: !!opts.stable,
490
+ factory: AERODROME().FACTORY,
491
+ router: AERODROME().ROUTER,
492
+ gauge: opts.gauge || ZERO,
493
+ lpToken: opts.lpToken,
494
+ amountADesired: amountA,
495
+ amountBDesired: amountB,
496
+ amountAMin: amountA,
497
+ // use desired as min for now
498
+ amountBMin: amountB,
499
+ minAmountAOut: minAOut,
500
+ minAmountBOut: minBOut
501
+ };
502
+ return {
503
+ initData: buildInitData3(params),
504
+ asset: tokenA,
505
+ assetAmount: amountA,
506
+ extraApprovals: [{ token: tokenB, amount: amountB }]
507
+ };
412
508
  }
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);
509
+ throw new Error(`No init builder for template: ${templateKey}`);
510
+ }
511
+ function buildCallsForTemplate(templateKey, clone, asset, assetAmount, extraApprovals) {
512
+ if (templateKey === "moonwell-supply") {
513
+ return {
514
+ executeCalls: buildExecuteCalls(clone, asset, assetAmount),
515
+ settleCalls: buildSettleCalls(clone)
516
+ };
443
517
  }
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
518
+ if (templateKey === "venice-inference") {
519
+ return {
520
+ executeCalls: buildExecuteCalls2(clone, asset, assetAmount),
521
+ settleCalls: buildSettleCalls2(clone)
522
+ };
523
+ }
524
+ if (templateKey === "aerodrome-lp") {
525
+ const tokenB = extraApprovals?.[0]?.token ?? ZERO;
526
+ const amountB = extraApprovals?.[0]?.amount ?? 0n;
527
+ return {
528
+ executeCalls: buildExecuteCalls3(clone, asset, assetAmount, tokenB, amountB),
529
+ settleCalls: buildSettleCalls3(clone)
530
+ };
531
+ }
532
+ throw new Error(`No call builder for template: ${templateKey}`);
533
+ }
534
+ function resolveToken(symbolOrAddress) {
535
+ if (isAddress(symbolOrAddress)) return symbolOrAddress;
536
+ const upper = symbolOrAddress.toUpperCase();
537
+ const tokens = TOKENS();
538
+ const tokenMap = {
539
+ USDC: tokens.USDC,
540
+ WETH: tokens.WETH,
541
+ DAI: tokens.DAI,
542
+ AERO: tokens.AERO,
543
+ VVV: VENICE().VVV
454
544
  };
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;
545
+ const addr = tokenMap[upper];
546
+ if (!addr || addr === ZERO) {
547
+ console.error(chalk.red(`Unknown token: ${symbolOrAddress}`));
548
+ process.exit(1);
465
549
  }
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)));
550
+ return addr;
551
+ }
552
+ function resolveMToken(tokenSymbol) {
553
+ const upper = tokenSymbol.toUpperCase();
554
+ const moonwell = MOONWELL();
555
+ const mTokenMap = {
556
+ USDC: moonwell.mUSDC,
557
+ WETH: moonwell.mWETH
558
+ };
559
+ const addr = mTokenMap[upper];
560
+ if (!addr || addr === ZERO) {
561
+ console.error(chalk.red(`No Moonwell market for: ${tokenSymbol}`));
474
562
  process.exit(1);
475
563
  }
564
+ return addr;
565
+ }
566
+ function serializeCalls(calls) {
567
+ return JSON.stringify(
568
+ calls.map((c) => ({
569
+ target: c.target,
570
+ data: c.data,
571
+ value: c.value.toString()
572
+ })),
573
+ null,
574
+ 2
575
+ );
576
+ }
577
+ function registerStrategyTemplateCommands(strategy2) {
578
+ strategy2.command("list").description("List available strategy templates").action(() => {
579
+ const templates = STRATEGY_TEMPLATES();
580
+ console.log();
581
+ console.log(chalk.bold("Strategy Templates"));
582
+ console.log(chalk.dim("\u2500".repeat(60)));
583
+ for (const t of TEMPLATES) {
584
+ const addr = templates[t.addressKey];
585
+ const deployed = addr !== ZERO;
586
+ console.log();
587
+ console.log(` ${chalk.bold(t.name)} (${chalk.cyan(t.key)})`);
588
+ console.log(` ${t.description}`);
589
+ console.log(` Template: ${deployed ? chalk.green(addr) : chalk.red("not deployed")}`);
590
+ }
591
+ console.log();
592
+ console.log(chalk.dim("Clone a template: sherwood strategy clone <template> --vault <addr> ..."));
593
+ console.log(chalk.dim("Full proposal: sherwood strategy propose <template> --vault <addr> ..."));
594
+ console.log();
595
+ });
596
+ strategy2.command("clone").description("Clone a strategy template and initialize it").argument("<template>", "Template: moonwell-supply, aerodrome-lp, venice-inference").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)").action(async (templateKey, opts) => {
597
+ const vault = opts.vault;
598
+ if (!isAddress(vault)) {
599
+ console.error(chalk.red("Invalid vault address"));
600
+ process.exit(1);
601
+ }
602
+ const { def, address: templateAddr } = resolveTemplate(templateKey);
603
+ const cloneSpinner = ora(`Cloning ${def.name} template...`).start();
604
+ let clone;
605
+ let cloneHash;
606
+ try {
607
+ const result = await cloneTemplate(templateAddr);
608
+ clone = result.clone;
609
+ cloneHash = result.hash;
610
+ cloneSpinner.succeed(`Cloned: ${chalk.green(clone)}`);
611
+ console.log(chalk.dim(` Tx: ${getExplorerUrl(cloneHash)}`));
612
+ } catch (err) {
613
+ cloneSpinner.fail("Clone failed");
614
+ console.error(chalk.red(err instanceof Error ? err.message : String(err)));
615
+ process.exit(1);
616
+ }
617
+ const initSpinner = ora("Initializing strategy...").start();
618
+ try {
619
+ const { initData } = await buildInitDataForTemplate(templateKey, opts, vault);
620
+ const account = getAccount();
621
+ const wallet = getWalletClient();
622
+ const initHash = await wallet.writeContract({
623
+ account,
624
+ chain: getChain(),
625
+ address: clone,
626
+ abi: BASE_STRATEGY_ABI,
627
+ functionName: "initialize",
628
+ args: [vault, account.address, initData]
629
+ });
630
+ await getPublicClient().waitForTransactionReceipt({ hash: initHash });
631
+ initSpinner.succeed("Initialized");
632
+ } catch (err) {
633
+ initSpinner.fail("Initialize failed");
634
+ console.error(chalk.red(err instanceof Error ? err.message : String(err)));
635
+ process.exit(1);
636
+ }
637
+ console.log();
638
+ console.log(chalk.bold("Strategy clone ready:"), chalk.green(clone));
639
+ console.log(chalk.dim("Use this address in your proposal batch calls."));
640
+ console.log();
641
+ });
642
+ strategy2.command("propose").description("Clone + init + build calls + submit governance proposal (all-in-one)").argument("<template>", "Template: moonwell-supply, aerodrome-lp, venice-inference").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)").action(async (templateKey, opts) => {
643
+ const vault = opts.vault;
644
+ if (!isAddress(vault)) {
645
+ console.error(chalk.red("Invalid vault address"));
646
+ process.exit(1);
647
+ }
648
+ const { def, address: templateAddr } = resolveTemplate(templateKey);
649
+ const cloneSpinner = ora(`Cloning ${def.name} template...`).start();
650
+ let clone;
651
+ try {
652
+ const result = await cloneTemplate(templateAddr);
653
+ clone = result.clone;
654
+ cloneSpinner.succeed(`Cloned: ${chalk.green(clone)}`);
655
+ console.log(chalk.dim(` Tx: ${getExplorerUrl(result.hash)}`));
656
+ } catch (err) {
657
+ cloneSpinner.fail("Clone failed");
658
+ console.error(chalk.red(err instanceof Error ? err.message : String(err)));
659
+ process.exit(1);
660
+ }
661
+ const initSpinner = ora("Initializing strategy...").start();
662
+ let asset;
663
+ let assetAmount;
664
+ let extraApprovals;
665
+ try {
666
+ const built = await buildInitDataForTemplate(templateKey, opts, vault);
667
+ asset = built.asset;
668
+ assetAmount = built.assetAmount;
669
+ extraApprovals = built.extraApprovals;
670
+ const account2 = getAccount();
671
+ const wallet = getWalletClient();
672
+ const initHash = await wallet.writeContract({
673
+ account: account2,
674
+ chain: getChain(),
675
+ address: clone,
676
+ abi: BASE_STRATEGY_ABI,
677
+ functionName: "initialize",
678
+ args: [vault, account2.address, built.initData]
679
+ });
680
+ await getPublicClient().waitForTransactionReceipt({ hash: initHash });
681
+ initSpinner.succeed("Initialized");
682
+ } catch (err) {
683
+ initSpinner.fail("Initialize failed");
684
+ console.error(chalk.red(err instanceof Error ? err.message : String(err)));
685
+ process.exit(1);
686
+ }
687
+ const { executeCalls, settleCalls } = buildCallsForTemplate(
688
+ templateKey,
689
+ clone,
690
+ asset,
691
+ assetAmount,
692
+ extraApprovals
693
+ );
694
+ console.log();
695
+ console.log(chalk.bold(`Execute calls (${executeCalls.length}):`));
696
+ console.log(formatBatch(executeCalls));
697
+ console.log(chalk.bold(`Settle calls (${settleCalls.length}):`));
698
+ console.log(formatBatch(settleCalls));
699
+ if (opts.writeCalls) {
700
+ const dir = resolve(opts.writeCalls);
701
+ mkdirSync(dir, { recursive: true });
702
+ const execPath = resolve(dir, "execute.json");
703
+ const settlePath = resolve(dir, "settle.json");
704
+ writeFileSync(execPath, serializeCalls(executeCalls));
705
+ writeFileSync(settlePath, serializeCalls(settleCalls));
706
+ console.log();
707
+ console.log(chalk.green(`Execute calls: ${execPath}`));
708
+ console.log(chalk.green(`Settle calls: ${settlePath}`));
709
+ console.log(chalk.green(`Clone address: ${clone}`));
710
+ console.log();
711
+ console.log(chalk.dim("Submit with:"));
712
+ console.log(chalk.dim(` sherwood proposal create \\`));
713
+ console.log(chalk.dim(` --vault ${vault} \\`));
714
+ console.log(chalk.dim(` --name "..." --description "..." \\`));
715
+ console.log(chalk.dim(` --performance-fee 0 --duration 7d \\`));
716
+ console.log(chalk.dim(` --execute-calls ${execPath} \\`));
717
+ console.log(chalk.dim(` --settle-calls ${settlePath}`));
718
+ if (templateKey === "venice-inference") {
719
+ console.log();
720
+ console.log(chalk.yellow("Reminder: agent must pre-approve sVVV clawback:"));
721
+ console.log(chalk.yellow(` sVVV.approve(${clone}, <amount>)`));
722
+ }
723
+ console.log();
724
+ return;
725
+ }
726
+ if (!opts.name || !opts.performanceFee || !opts.duration) {
727
+ console.error(chalk.red("Missing --name, --performance-fee, or --duration. Use --write-calls to skip proposal submission."));
728
+ process.exit(1);
729
+ }
730
+ const { propose: propose2 } = await import("./governor-P6YV4YR7.js");
731
+ const { pinJSON } = await import("./ipfs-6XVOOHSR.js");
732
+ const { parseDuration: parseDuration2 } = await import("./governor-P6YV4YR7.js");
733
+ const performanceFeeBps = BigInt(opts.performanceFee);
734
+ if (performanceFeeBps < 0n || performanceFeeBps > 10000n) {
735
+ console.error(chalk.red("--performance-fee must be 0-10000 (basis points)"));
736
+ process.exit(1);
737
+ }
738
+ const strategyDuration = parseDuration2(opts.duration);
739
+ const account = getAccount();
740
+ const metaSpinner = ora("Pinning metadata to IPFS...").start();
741
+ let metadataURI;
742
+ try {
743
+ const metadata = {
744
+ name: opts.name,
745
+ description: opts.description || "",
746
+ proposer: account.address,
747
+ vault,
748
+ strategyClone: clone,
749
+ template: def.key,
750
+ performanceFeeBps: Number(performanceFeeBps),
751
+ strategyDuration: Number(strategyDuration),
752
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
753
+ };
754
+ metadataURI = await pinJSON(metadata, opts.name);
755
+ metaSpinner.succeed(`Metadata pinned: ${metadataURI}`);
756
+ } catch (err) {
757
+ metaSpinner.fail("IPFS pin failed");
758
+ console.error(chalk.red(err instanceof Error ? err.message : String(err)));
759
+ process.exit(1);
760
+ }
761
+ const proposeSpinner = ora("Submitting proposal...").start();
762
+ try {
763
+ const { hash, proposalId } = await propose2(
764
+ vault,
765
+ metadataURI,
766
+ performanceFeeBps,
767
+ strategyDuration,
768
+ executeCalls,
769
+ settleCalls
770
+ );
771
+ proposeSpinner.succeed(`Proposal #${proposalId} created`);
772
+ console.log(chalk.dim(` Tx: ${getExplorerUrl(hash)}`));
773
+ console.log(chalk.dim(` Clone: ${clone}`));
774
+ } catch (err) {
775
+ proposeSpinner.fail("Proposal failed");
776
+ console.error(chalk.red(err instanceof Error ? err.message : String(err)));
777
+ process.exit(1);
778
+ }
779
+ if (templateKey === "venice-inference") {
780
+ console.log();
781
+ console.log(chalk.yellow("Next steps:"));
782
+ console.log(chalk.yellow(` 1. Pre-approve sVVV clawback: sVVV.approve(${clone}, <amount>)`));
783
+ console.log(chalk.yellow(" 2. After execution: sherwood venice provision"));
784
+ console.log(chalk.yellow(" 3. Use inference: sherwood venice infer --model <id> --prompt '...'"));
785
+ }
786
+ console.log();
787
+ });
476
788
  }
477
789
 
478
790
  // src/lib/factory.ts
@@ -652,74 +964,83 @@ async function getActiveSyndicates2(creator) {
652
964
  return data.syndicates;
653
965
  }
654
966
 
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) {
967
+ // src/commands/venice.ts
968
+ import { parseUnits as parseUnits4, formatUnits as formatUnits2, isAddress as isAddress2 } from "viem";
969
+ import chalk2 from "chalk";
970
+ import ora2 from "ora";
971
+
972
+ // src/lib/quote.ts
973
+ import { encodeFunctionData as encodeFunctionData4, decodeFunctionResult, concat as concat2, pad, numberToHex } from "viem";
974
+ async function getQuote(params) {
671
975
  const client = getPublicClient();
672
- const result = await client.readContract({
673
- address: getRegistryAddress(),
674
- abi: STRATEGY_REGISTRY_ABI,
675
- functionName: "getStrategy",
676
- args: [id]
976
+ const calldata = encodeFunctionData4({
977
+ abi: UNISWAP_QUOTER_V2_ABI,
978
+ functionName: "quoteExactInputSingle",
979
+ args: [
980
+ {
981
+ tokenIn: params.tokenIn,
982
+ tokenOut: params.tokenOut,
983
+ amountIn: params.amountIn,
984
+ fee: params.fee,
985
+ sqrtPriceLimitX96: 0n
986
+ }
987
+ ]
677
988
  });
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
- };
989
+ const { data } = await client.call({
990
+ to: UNISWAP().QUOTER_V2,
991
+ data: calldata
992
+ });
993
+ if (!data) {
994
+ throw new Error("Quoter returned no data \u2014 pool may not exist for this pair/fee");
995
+ }
996
+ const [amountOut, sqrtPriceX96After, , gasEstimate] = decodeFunctionResult({
997
+ abi: UNISWAP_QUOTER_V2_ABI,
998
+ functionName: "quoteExactInputSingle",
999
+ data
1000
+ });
1001
+ return { amountOut, sqrtPriceX96After, gasEstimate };
687
1002
  }
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));
1003
+ function applySlippage(amountOut, slippageBps) {
1004
+ return amountOut * BigInt(1e4 - slippageBps) / 10000n;
1005
+ }
1006
+ function encodeSwapPath(tokens, fees) {
1007
+ if (tokens.length < 2 || fees.length !== tokens.length - 1) {
1008
+ throw new Error("Invalid path: need at least 2 tokens and (tokens-1) fees");
1009
+ }
1010
+ const parts = [];
1011
+ for (let i = 0; i < tokens.length; i++) {
1012
+ parts.push(tokens[i].toLowerCase());
1013
+ if (i < fees.length) {
1014
+ parts.push(pad(numberToHex(fees[i]), { size: 3 }));
1015
+ }
706
1016
  }
707
- const strategies = [];
708
- for (const id of ids) {
709
- const s = await getStrategy(id);
710
- strategies.push(s);
1017
+ return concat2(parts);
1018
+ }
1019
+ async function getMultiHopQuote(params) {
1020
+ const client = getPublicClient();
1021
+ const calldata = encodeFunctionData4({
1022
+ abi: UNISWAP_QUOTER_V2_ABI,
1023
+ functionName: "quoteExactInput",
1024
+ args: [params.path, params.amountIn]
1025
+ });
1026
+ const { data } = await client.call({
1027
+ to: UNISWAP().QUOTER_V2,
1028
+ data: calldata
1029
+ });
1030
+ if (!data) {
1031
+ throw new Error("Quoter returned no data \u2014 pool may not exist for this path");
711
1032
  }
712
- return strategies;
1033
+ const [amountOut, , , gasEstimate] = decodeFunctionResult({
1034
+ abi: UNISWAP_QUOTER_V2_ABI,
1035
+ functionName: "quoteExactInput",
1036
+ data
1037
+ });
1038
+ return { amountOut, sqrtPriceX96After: 0n, gasEstimate };
713
1039
  }
714
1040
 
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
1041
  // src/strategies/venice-fund.ts
721
- import { encodeFunctionData as encodeFunctionData3, parseUnits as parseUnits4 } from "viem";
722
- var ERC20_ABI3 = [
1042
+ import { encodeFunctionData as encodeFunctionData5, parseUnits as parseUnits3 } from "viem";
1043
+ var ERC20_ABI2 = [
723
1044
  {
724
1045
  name: "approve",
725
1046
  type: "function",
@@ -783,17 +1104,17 @@ var STAKING_ABI = [
783
1104
  }
784
1105
  ];
785
1106
  function buildFundBatch(config, vaultAddress, agents, assetAddress, assetDecimals, minVVV, swapPath) {
786
- const ZERO = "0x0000000000000000000000000000000000000000";
787
- if (VENICE().VVV === ZERO || VENICE().STAKING === ZERO) {
1107
+ const ZERO2 = "0x0000000000000000000000000000000000000000";
1108
+ if (VENICE().VVV === ZERO2 || VENICE().STAKING === ZERO2) {
788
1109
  throw new Error("Venice (VVV/sVVV) is not deployed on this network \u2014 venice fund requires Venice staking contracts");
789
1110
  }
790
- const assetAmount = parseUnits4(config.amount, assetDecimals);
1111
+ const assetAmount = parseUnits3(config.amount, assetDecimals);
791
1112
  const isWeth = assetAddress.toLowerCase() === TOKENS().WETH.toLowerCase();
792
1113
  const calls = [];
793
1114
  calls.push({
794
1115
  target: assetAddress,
795
- data: encodeFunctionData3({
796
- abi: ERC20_ABI3,
1116
+ data: encodeFunctionData5({
1117
+ abi: ERC20_ABI2,
797
1118
  functionName: "approve",
798
1119
  args: [UNISWAP().SWAP_ROUTER, assetAmount]
799
1120
  }),
@@ -802,7 +1123,7 @@ function buildFundBatch(config, vaultAddress, agents, assetAddress, assetDecimal
802
1123
  if (isWeth) {
803
1124
  calls.push({
804
1125
  target: UNISWAP().SWAP_ROUTER,
805
- data: encodeFunctionData3({
1126
+ data: encodeFunctionData5({
806
1127
  abi: SWAP_ROUTER_EXACT_INPUT_SINGLE_ABI,
807
1128
  functionName: "exactInputSingle",
808
1129
  args: [
@@ -822,7 +1143,7 @@ function buildFundBatch(config, vaultAddress, agents, assetAddress, assetDecimal
822
1143
  } else {
823
1144
  calls.push({
824
1145
  target: UNISWAP().SWAP_ROUTER,
825
- data: encodeFunctionData3({
1146
+ data: encodeFunctionData5({
826
1147
  abi: SWAP_ROUTER_EXACT_INPUT_ABI,
827
1148
  functionName: "exactInput",
828
1149
  args: [
@@ -839,8 +1160,8 @@ function buildFundBatch(config, vaultAddress, agents, assetAddress, assetDecimal
839
1160
  }
840
1161
  calls.push({
841
1162
  target: VENICE().VVV,
842
- data: encodeFunctionData3({
843
- abi: ERC20_ABI3,
1163
+ data: encodeFunctionData5({
1164
+ abi: ERC20_ABI2,
844
1165
  functionName: "approve",
845
1166
  args: [VENICE().STAKING, minVVV]
846
1167
  }),
@@ -850,7 +1171,7 @@ function buildFundBatch(config, vaultAddress, agents, assetAddress, assetDecimal
850
1171
  for (const agent of agents) {
851
1172
  calls.push({
852
1173
  target: VENICE().STAKING,
853
- data: encodeFunctionData3({
1174
+ data: encodeFunctionData5({
854
1175
  abi: STAKING_ABI,
855
1176
  functionName: "stake",
856
1177
  args: [agent, perAgent]
@@ -865,16 +1186,22 @@ function buildFundBatch(config, vaultAddress, agents, assetAddress, assetDecimal
865
1186
  var VENICE_API_BASE = "https://api.venice.ai/api/v1";
866
1187
  async function provisionApiKey() {
867
1188
  const account = getAccount();
868
- const tokenRes = await fetch(`${VENICE_API_BASE}/api_keys/generate_web3_key`);
1189
+ const tokenRes = await fetch(`${VENICE_API_BASE}/api_keys/generate_web3_key`, {
1190
+ signal: AbortSignal.timeout(15e3)
1191
+ });
869
1192
  if (!tokenRes.ok) {
870
1193
  throw new Error(`Failed to get validation token: ${tokenRes.status} ${tokenRes.statusText}`);
871
1194
  }
872
1195
  const tokenData = await tokenRes.json();
873
- const token = tokenData.data.token;
1196
+ const token = tokenData?.data?.token;
1197
+ if (!token) {
1198
+ throw new Error("Venice API returned no validation token");
1199
+ }
874
1200
  const signature = await account.signMessage({ message: token });
875
1201
  const keyRes = await fetch(`${VENICE_API_BASE}/api_keys/generate_web3_key`, {
876
1202
  method: "POST",
877
1203
  headers: { "Content-Type": "application/json" },
1204
+ signal: AbortSignal.timeout(15e3),
878
1205
  body: JSON.stringify({
879
1206
  address: account.address,
880
1207
  signature,
@@ -888,28 +1215,92 @@ async function provisionApiKey() {
888
1215
  throw new Error(`Failed to generate API key: ${keyRes.status} ${body}`);
889
1216
  }
890
1217
  const keyData = await keyRes.json();
891
- const apiKey = keyData.data.apiKey;
1218
+ const apiKey = keyData?.data?.apiKey;
1219
+ if (!apiKey) {
1220
+ throw new Error("Venice API returned no API key");
1221
+ }
892
1222
  setVeniceApiKey(apiKey);
893
1223
  return apiKey;
894
1224
  }
895
- async function checkApiKeyValid() {
1225
+ async function checkApiKeyValid() {
1226
+ const apiKey = getVeniceApiKey();
1227
+ if (!apiKey) return false;
1228
+ try {
1229
+ const res = await fetch(`${VENICE_API_BASE}/models`, {
1230
+ headers: { Authorization: `Bearer ${apiKey}` },
1231
+ signal: AbortSignal.timeout(15e3)
1232
+ });
1233
+ return res.ok;
1234
+ } catch {
1235
+ return false;
1236
+ }
1237
+ }
1238
+ async function chatCompletion(opts) {
1239
+ const apiKey = getVeniceApiKey();
1240
+ if (!apiKey) {
1241
+ throw new Error("No Venice API key configured. Run 'sherwood venice provision' first.");
1242
+ }
1243
+ const body = {
1244
+ model: opts.model,
1245
+ messages: opts.messages
1246
+ };
1247
+ if (opts.temperature !== void 0) body.temperature = opts.temperature;
1248
+ if (opts.maxTokens !== void 0) body.max_tokens = opts.maxTokens;
1249
+ const veniceParams = {};
1250
+ if (opts.enableWebSearch) veniceParams.enable_web_search = "on";
1251
+ if (opts.disableThinking) veniceParams.disable_thinking = true;
1252
+ if (Object.keys(veniceParams).length > 0) body.venice_parameters = veniceParams;
1253
+ const res = await fetch(`${VENICE_API_BASE}/chat/completions`, {
1254
+ method: "POST",
1255
+ headers: {
1256
+ "Content-Type": "application/json",
1257
+ Authorization: `Bearer ${apiKey}`
1258
+ },
1259
+ signal: AbortSignal.timeout(12e4),
1260
+ // inference can be slow
1261
+ body: JSON.stringify(body)
1262
+ });
1263
+ if (!res.ok) {
1264
+ const errBody = await res.text();
1265
+ throw new Error(`Venice inference failed: ${res.status} ${errBody}`);
1266
+ }
1267
+ const data = await res.json();
1268
+ const choice = data.choices?.[0];
1269
+ if (!choice) {
1270
+ throw new Error("Venice returned no choices");
1271
+ }
1272
+ return {
1273
+ content: choice.message?.content ?? "",
1274
+ model: data.model ?? opts.model,
1275
+ usage: {
1276
+ promptTokens: data.usage?.prompt_tokens ?? 0,
1277
+ completionTokens: data.usage?.completion_tokens ?? 0,
1278
+ totalTokens: data.usage?.total_tokens ?? 0
1279
+ }
1280
+ };
1281
+ }
1282
+ async function listModels() {
896
1283
  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;
1284
+ if (!apiKey) {
1285
+ throw new Error("No Venice API key configured. Run 'sherwood venice provision' first.");
1286
+ }
1287
+ const res = await fetch(`${VENICE_API_BASE}/models`, {
1288
+ headers: { Authorization: `Bearer ${apiKey}` },
1289
+ signal: AbortSignal.timeout(15e3)
1290
+ });
1291
+ if (!res.ok) {
1292
+ throw new Error(`Failed to list Venice models: ${res.status} ${res.statusText}`);
905
1293
  }
1294
+ const data = await res.json();
1295
+ return (data.data ?? []).map((m) => m.id);
906
1296
  }
907
1297
 
908
1298
  // src/commands/venice.ts
909
- var VALID_FEES2 = [500, 3e3, 1e4];
1299
+ import { readFileSync, writeFileSync as writeFileSync2 } from "fs";
1300
+ var VALID_FEES = [500, 3e3, 1e4];
910
1301
  function registerVeniceCommands(program2) {
911
1302
  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) => {
1303
+ 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
1304
  const vaultAddress = opts.vault;
914
1305
  if (!isAddress2(vaultAddress)) {
915
1306
  console.error(chalk2.red(`Invalid vault address: ${opts.vault}`));
@@ -917,8 +1308,8 @@ function registerVeniceCommands(program2) {
917
1308
  }
918
1309
  const fee1 = Number(opts.fee1);
919
1310
  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(", ")}`));
1311
+ if (!VALID_FEES.includes(fee1) || !VALID_FEES.includes(fee2)) {
1312
+ console.error(chalk2.red(`Invalid fee tier. Valid: ${VALID_FEES.join(", ")}`));
922
1313
  process.exit(1);
923
1314
  }
924
1315
  const slippageBps = Number(opts.slippage);
@@ -951,7 +1342,7 @@ function registerVeniceCommands(program2) {
951
1342
  console.error(chalk2.red("No agents registered in vault. Register agents first."));
952
1343
  process.exit(1);
953
1344
  }
954
- const requestedAmount = parseUnits5(opts.amount, assetDecimals);
1345
+ const requestedAmount = parseUnits4(opts.amount, assetDecimals);
955
1346
  const profit = assetBalance > totalDeposited ? assetBalance - totalDeposited : 0n;
956
1347
  const isWeth = assetAddress.toLowerCase() === TOKENS().WETH.toLowerCase();
957
1348
  console.log();
@@ -959,16 +1350,16 @@ function registerVeniceCommands(program2) {
959
1350
  console.log(chalk2.dim("\u2500".repeat(40)));
960
1351
  console.log(` Asset: ${assetSymbol} (${assetDecimals} decimals)`);
961
1352
  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}`);
1353
+ console.log(` Vault balance: ${formatUnits2(assetBalance, assetDecimals)} ${assetSymbol}`);
1354
+ console.log(` Deposited: ${formatUnits2(totalDeposited, assetDecimals)} ${assetSymbol}`);
1355
+ console.log(` Profit: ${formatUnits2(profit, assetDecimals)} ${assetSymbol}`);
965
1356
  console.log(` Agents: ${agents.length} (sVVV will be split equally)`);
966
1357
  console.log(` Routing: ${isWeth ? `WETH \u2192 VVV (fee ${fee2})` : `${assetSymbol} \u2192 WETH (fee ${fee1}) \u2192 VVV (fee ${fee2})`}`);
967
1358
  console.log(` Slippage: ${(slippageBps / 100).toFixed(2)}%`);
968
1359
  console.log(` Vault: ${vaultAddress}`);
969
1360
  console.log();
970
1361
  if (requestedAmount > profit) {
971
- console.warn(chalk2.yellow(` Warning: amount (${opts.amount}) exceeds available profit (${formatUnits3(profit, assetDecimals)})`));
1362
+ console.warn(chalk2.yellow(` Warning: amount (${opts.amount}) exceeds available profit (${formatUnits2(profit, assetDecimals)})`));
972
1363
  console.warn(chalk2.yellow(" This will use deposited capital, not just profits."));
973
1364
  console.log();
974
1365
  }
@@ -998,7 +1389,7 @@ function registerVeniceCommands(program2) {
998
1389
  }
999
1390
  minOut = applySlippage(amountOut, slippageBps);
1000
1391
  quoteSpinner.succeed(
1001
- `Quote: ${formatUnits3(amountOut, 18)} VVV (min: ${formatUnits3(minOut, 18)}, per agent: ${formatUnits3(minOut / BigInt(agents.length), 18)})`
1392
+ `Quote: ${formatUnits2(amountOut, 18)} VVV (min: ${formatUnits2(minOut, 18)}, per agent: ${formatUnits2(minOut / BigInt(agents.length), 18)})`
1002
1393
  );
1003
1394
  } catch (err) {
1004
1395
  quoteSpinner.fail("Failed to fetch quote");
@@ -1016,9 +1407,24 @@ function registerVeniceCommands(program2) {
1016
1407
  console.log(chalk2.bold(`Batch calls (${calls.length}):`));
1017
1408
  console.log(formatBatch(calls));
1018
1409
  console.log();
1410
+ if (opts.writeCalls) {
1411
+ const callsJson = calls.map((c) => ({
1412
+ target: c.target,
1413
+ data: c.data,
1414
+ value: c.value.toString()
1415
+ }));
1416
+ writeFileSync2(opts.writeCalls, JSON.stringify(callsJson, null, 2));
1417
+ const settlePath = `${opts.writeCalls}.settle.json`;
1418
+ writeFileSync2(settlePath, "[]");
1419
+ console.log(chalk2.green(`Execute calls written to: ${opts.writeCalls}`));
1420
+ console.log(chalk2.green(`Settlement calls written to: ${settlePath}`));
1421
+ console.log();
1422
+ console.log(chalk2.dim("Use with: sherwood proposal create --execute-calls <path> --settle-calls <path>"));
1423
+ return;
1424
+ }
1019
1425
  if (!opts.execute) {
1020
1426
  console.log();
1021
- console.log(chalk2.yellow("Dry run complete. Add --execute to submit on-chain."));
1427
+ console.log(chalk2.yellow("Dry run complete. Add --execute to submit on-chain, or --write-calls <path> to export for proposals."));
1022
1428
  return;
1023
1429
  }
1024
1430
  const execSpinner = ora2("Executing batch via vault...").start();
@@ -1049,7 +1455,7 @@ function registerVeniceCommands(program2) {
1049
1455
  console.log(chalk2.yellow(" Run 'sherwood venice fund' first to distribute sVVV to agents."));
1050
1456
  process.exit(1);
1051
1457
  }
1052
- checkSpinner.succeed(`sVVV balance: ${formatUnits3(sVvvBalance, 18)}`);
1458
+ checkSpinner.succeed(`sVVV balance: ${formatUnits2(sVvvBalance, 18)}`);
1053
1459
  } catch (err) {
1054
1460
  checkSpinner.fail("Failed to check sVVV balance");
1055
1461
  console.error(chalk2.red(err instanceof Error ? err.message : String(err)));
@@ -1126,17 +1532,17 @@ function registerVeniceCommands(program2) {
1126
1532
  console.log(chalk2.bold("Venice Inference Status"));
1127
1533
  console.log(chalk2.dim("\u2500".repeat(50)));
1128
1534
  console.log(chalk2.bold("\n Vault"));
1129
- console.log(` Profit available: ${formatUnits3(profit, assetDecimals)} ${assetSymbol}`);
1130
- console.log(` VVV (unstaked): ${formatUnits3(vaultVvvBalance, 18)}`);
1535
+ console.log(` Profit available: ${formatUnits2(profit, assetDecimals)} ${assetSymbol}`);
1536
+ console.log(` VVV (unstaked): ${formatUnits2(vaultVvvBalance, 18)}`);
1131
1537
  console.log(chalk2.bold("\n Agent sVVV Balances"));
1132
1538
  for (const { agent, balance } of agentBalances) {
1133
1539
  const isMe = agent.toLowerCase() === account.address.toLowerCase();
1134
1540
  const label = isMe ? chalk2.green(`${agent} (you)`) : agent;
1135
- console.log(` ${label}: ${formatUnits3(balance, 18)} sVVV`);
1541
+ console.log(` ${label}: ${formatUnits2(balance, 18)} sVVV`);
1136
1542
  }
1137
1543
  console.log(chalk2.bold("\n Your Wallet"));
1138
- console.log(` sVVV: ${formatUnits3(mySvvv, 18)}`);
1139
- console.log(` Pending rewards: ${formatUnits3(myPending, 18)} VVV`);
1544
+ console.log(` sVVV: ${formatUnits2(mySvvv, 18)}`);
1545
+ console.log(` Pending rewards: ${formatUnits2(myPending, 18)} VVV`);
1140
1546
  console.log(chalk2.bold("\n Venice API"));
1141
1547
  console.log(` Key: ${apiKey ? `${apiKey.slice(0, 8)}...${apiKey.slice(-4)}` : chalk2.dim("not provisioned")}`);
1142
1548
  console.log(` Status: ${apiKeyValid ? chalk2.green("valid") : chalk2.red("invalid/missing")}`);
@@ -1147,16 +1553,79 @@ function registerVeniceCommands(program2) {
1147
1553
  process.exit(1);
1148
1554
  }
1149
1555
  });
1556
+ venice.command("models").description("List available Venice inference models").action(async () => {
1557
+ const spinner = ora2("Fetching Venice models...").start();
1558
+ try {
1559
+ const models = await listModels();
1560
+ spinner.succeed(`${models.length} models available`);
1561
+ console.log();
1562
+ for (const model of models) {
1563
+ console.log(` ${model}`);
1564
+ }
1565
+ console.log();
1566
+ } catch (err) {
1567
+ spinner.fail("Failed to list models");
1568
+ console.error(chalk2.red(err instanceof Error ? err.message : String(err)));
1569
+ process.exit(1);
1570
+ }
1571
+ });
1572
+ 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) => {
1573
+ const messages = [];
1574
+ if (opts.system) {
1575
+ messages.push({ role: "system", content: opts.system });
1576
+ }
1577
+ let userContent = opts.prompt;
1578
+ if (opts.data) {
1579
+ try {
1580
+ const data = readFileSync(opts.data, "utf-8");
1581
+ userContent = `Context data:
1582
+ \`\`\`
1583
+ ${data}
1584
+ \`\`\`
1585
+
1586
+ ${opts.prompt}`;
1587
+ } catch (err) {
1588
+ console.error(chalk2.red(`Failed to read data file: ${opts.data}`));
1589
+ console.error(chalk2.red(err instanceof Error ? err.message : String(err)));
1590
+ process.exit(1);
1591
+ }
1592
+ }
1593
+ messages.push({ role: "user", content: userContent });
1594
+ const spinner = ora2(`Running inference (${opts.model})...`).start();
1595
+ try {
1596
+ const result = await chatCompletion({
1597
+ model: opts.model,
1598
+ messages,
1599
+ temperature: opts.temperature !== void 0 ? Number(opts.temperature) : void 0,
1600
+ maxTokens: opts.maxTokens !== void 0 ? Number(opts.maxTokens) : void 0,
1601
+ enableWebSearch: opts.webSearch,
1602
+ disableThinking: opts.thinking === false
1603
+ });
1604
+ spinner.succeed("Inference complete");
1605
+ if (opts.json) {
1606
+ console.log(JSON.stringify(result, null, 2));
1607
+ } else {
1608
+ console.log();
1609
+ console.log(result.content);
1610
+ console.log();
1611
+ console.log(chalk2.dim(`Model: ${result.model} | Tokens: ${result.usage.promptTokens} in, ${result.usage.completionTokens} out, ${result.usage.totalTokens} total`));
1612
+ }
1613
+ } catch (err) {
1614
+ spinner.fail("Inference failed");
1615
+ console.error(chalk2.red(err instanceof Error ? err.message : String(err)));
1616
+ process.exit(1);
1617
+ }
1618
+ });
1150
1619
  }
1151
1620
 
1152
1621
  // src/commands/allowance.ts
1153
- import { parseUnits as parseUnits7, formatUnits as formatUnits4, isAddress as isAddress3 } from "viem";
1622
+ import { parseUnits as parseUnits6, formatUnits as formatUnits3, isAddress as isAddress3 } from "viem";
1154
1623
  import chalk3 from "chalk";
1155
1624
  import ora3 from "ora";
1156
1625
 
1157
1626
  // src/strategies/allowance-disburse.ts
1158
- import { encodeFunctionData as encodeFunctionData4, parseUnits as parseUnits6 } from "viem";
1159
- var ERC20_ABI4 = [
1627
+ import { encodeFunctionData as encodeFunctionData6, parseUnits as parseUnits5 } from "viem";
1628
+ var ERC20_ABI3 = [
1160
1629
  {
1161
1630
  name: "approve",
1162
1631
  type: "function",
@@ -1218,15 +1687,15 @@ var SWAP_ROUTER_EXACT_INPUT_ABI2 = [
1218
1687
  }
1219
1688
  ];
1220
1689
  function buildDisburseBatch(config, vaultAddress, agents, assetAddress, assetDecimals, minUsdc, swapPath) {
1221
- const assetAmount = parseUnits6(config.amount, assetDecimals);
1690
+ const assetAmount = parseUnits5(config.amount, assetDecimals);
1222
1691
  const isUsdc = assetAddress.toLowerCase() === TOKENS().USDC.toLowerCase();
1223
1692
  const isWeth = assetAddress.toLowerCase() === TOKENS().WETH.toLowerCase();
1224
1693
  const calls = [];
1225
1694
  if (!isUsdc) {
1226
1695
  calls.push({
1227
1696
  target: assetAddress,
1228
- data: encodeFunctionData4({
1229
- abi: ERC20_ABI4,
1697
+ data: encodeFunctionData6({
1698
+ abi: ERC20_ABI3,
1230
1699
  functionName: "approve",
1231
1700
  args: [UNISWAP().SWAP_ROUTER, assetAmount]
1232
1701
  }),
@@ -1235,7 +1704,7 @@ function buildDisburseBatch(config, vaultAddress, agents, assetAddress, assetDec
1235
1704
  if (isWeth) {
1236
1705
  calls.push({
1237
1706
  target: UNISWAP().SWAP_ROUTER,
1238
- data: encodeFunctionData4({
1707
+ data: encodeFunctionData6({
1239
1708
  abi: SWAP_ROUTER_EXACT_INPUT_SINGLE_ABI2,
1240
1709
  functionName: "exactInputSingle",
1241
1710
  args: [
@@ -1255,7 +1724,7 @@ function buildDisburseBatch(config, vaultAddress, agents, assetAddress, assetDec
1255
1724
  } else {
1256
1725
  calls.push({
1257
1726
  target: UNISWAP().SWAP_ROUTER,
1258
- data: encodeFunctionData4({
1727
+ data: encodeFunctionData6({
1259
1728
  abi: SWAP_ROUTER_EXACT_INPUT_ABI2,
1260
1729
  functionName: "exactInput",
1261
1730
  args: [
@@ -1275,8 +1744,8 @@ function buildDisburseBatch(config, vaultAddress, agents, assetAddress, assetDec
1275
1744
  for (const agent of agents) {
1276
1745
  calls.push({
1277
1746
  target: TOKENS().USDC,
1278
- data: encodeFunctionData4({
1279
- abi: ERC20_ABI4,
1747
+ data: encodeFunctionData6({
1748
+ abi: ERC20_ABI3,
1280
1749
  functionName: "transfer",
1281
1750
  args: [agent, perAgent]
1282
1751
  }),
@@ -1287,7 +1756,7 @@ function buildDisburseBatch(config, vaultAddress, agents, assetAddress, assetDec
1287
1756
  }
1288
1757
 
1289
1758
  // src/commands/allowance.ts
1290
- var VALID_FEES3 = [500, 3e3, 1e4];
1759
+ var VALID_FEES2 = [500, 3e3, 1e4];
1291
1760
  function registerAllowanceCommands(program2) {
1292
1761
  const allowance = program2.command("allowance").description("Disburse vault profits to agent wallets");
1293
1762
  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 +1766,8 @@ function registerAllowanceCommands(program2) {
1297
1766
  process.exit(1);
1298
1767
  }
1299
1768
  const fee = Number(opts.fee);
1300
- if (!VALID_FEES3.includes(fee)) {
1301
- console.error(chalk3.red(`Invalid fee tier. Valid: ${VALID_FEES3.join(", ")}`));
1769
+ if (!VALID_FEES2.includes(fee)) {
1770
+ console.error(chalk3.red(`Invalid fee tier. Valid: ${VALID_FEES2.join(", ")}`));
1302
1771
  process.exit(1);
1303
1772
  }
1304
1773
  const slippageBps = Number(opts.slippage);
@@ -1331,7 +1800,7 @@ function registerAllowanceCommands(program2) {
1331
1800
  console.error(chalk3.red("No agents registered in vault. Register agents first."));
1332
1801
  process.exit(1);
1333
1802
  }
1334
- const requestedAmount = parseUnits7(opts.amount, assetDecimals);
1803
+ const requestedAmount = parseUnits6(opts.amount, assetDecimals);
1335
1804
  const profit = assetBalance > totalDeposited ? assetBalance - totalDeposited : 0n;
1336
1805
  const isUsdc = assetAddress.toLowerCase() === TOKENS().USDC.toLowerCase();
1337
1806
  const isWeth = assetAddress.toLowerCase() === TOKENS().WETH.toLowerCase();
@@ -1340,9 +1809,9 @@ function registerAllowanceCommands(program2) {
1340
1809
  console.log(chalk3.dim("\u2500".repeat(40)));
1341
1810
  console.log(` Asset: ${assetSymbol} (${assetDecimals} decimals)`);
1342
1811
  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}`);
1812
+ console.log(` Vault balance: ${formatUnits3(assetBalance, assetDecimals)} ${assetSymbol}`);
1813
+ console.log(` Deposited: ${formatUnits3(totalDeposited, assetDecimals)} ${assetSymbol}`);
1814
+ console.log(` Profit: ${formatUnits3(profit, assetDecimals)} ${assetSymbol}`);
1346
1815
  console.log(` Agents: ${agents.length} (USDC will be split equally)`);
1347
1816
  if (!isUsdc) {
1348
1817
  console.log(` Routing: ${isWeth ? `WETH \u2192 USDC (fee ${fee})` : `${assetSymbol} \u2192 WETH \u2192 USDC (fee ${fee})`}`);
@@ -1351,7 +1820,7 @@ function registerAllowanceCommands(program2) {
1351
1820
  console.log(` Vault: ${vaultAddress}`);
1352
1821
  console.log();
1353
1822
  if (requestedAmount > profit) {
1354
- console.warn(chalk3.yellow(` Warning: amount (${opts.amount}) exceeds available profit (${formatUnits4(profit, assetDecimals)})`));
1823
+ console.warn(chalk3.yellow(` Warning: amount (${opts.amount}) exceeds available profit (${formatUnits3(profit, assetDecimals)})`));
1355
1824
  console.warn(chalk3.yellow(" This will use deposited capital, not just profits."));
1356
1825
  console.log();
1357
1826
  }
@@ -1385,7 +1854,7 @@ function registerAllowanceCommands(program2) {
1385
1854
  }
1386
1855
  minUsdc = applySlippage(amountOut, slippageBps);
1387
1856
  quoteSpinner.succeed(
1388
- `Quote: ${formatUnits4(amountOut, 6)} USDC (min: ${formatUnits4(minUsdc, 6)}, per agent: ${formatUnits4(minUsdc / BigInt(agents.length), 6)})`
1857
+ `Quote: ${formatUnits3(amountOut, 6)} USDC (min: ${formatUnits3(minUsdc, 6)}, per agent: ${formatUnits3(minUsdc / BigInt(agents.length), 6)})`
1389
1858
  );
1390
1859
  } catch (err) {
1391
1860
  quoteSpinner.fail("Failed to fetch quote");
@@ -1395,7 +1864,7 @@ function registerAllowanceCommands(program2) {
1395
1864
  }
1396
1865
  const perAgent = minUsdc / BigInt(agents.length);
1397
1866
  if (isUsdc) {
1398
- console.log(chalk3.dim(` Per agent: ${formatUnits4(perAgent, 6)} USDC`));
1867
+ console.log(chalk3.dim(` Per agent: ${formatUnits3(perAgent, 6)} USDC`));
1399
1868
  console.log();
1400
1869
  }
1401
1870
  const config = {
@@ -1462,14 +1931,14 @@ function registerAllowanceCommands(program2) {
1462
1931
  console.log(chalk3.dim("\u2500".repeat(50)));
1463
1932
  console.log(chalk3.bold("\n Vault"));
1464
1933
  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}`);
1934
+ console.log(` Balance: ${formatUnits3(assetBalance, assetDecimals)} ${assetSymbol}`);
1935
+ console.log(` Deposited: ${formatUnits3(totalDeposited, assetDecimals)} ${assetSymbol}`);
1936
+ console.log(` Profit: ${formatUnits3(profit, assetDecimals)} ${assetSymbol}`);
1468
1937
  console.log(chalk3.bold("\n Agent USDC Balances"));
1469
1938
  for (const { agent, balance } of agentBalances) {
1470
1939
  const isMe = agent.toLowerCase() === account.address.toLowerCase();
1471
1940
  const label = isMe ? chalk3.green(`${agent} (you)`) : agent;
1472
- console.log(` ${label}: ${formatUnits4(balance, 6)} USDC`);
1941
+ console.log(` ${label}: ${formatUnits3(balance, 6)} USDC`);
1473
1942
  }
1474
1943
  console.log();
1475
1944
  } catch (err) {
@@ -1641,348 +2110,7 @@ function registerIdentityCommands(program2) {
1641
2110
  import { isAddress as isAddress4 } from "viem";
1642
2111
  import chalk5 from "chalk";
1643
2112
  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
- }
2113
+ import { readFileSync as readFileSync2 } from "fs";
1986
2114
 
1987
2115
  // src/lib/format.ts
1988
2116
  function formatDurationShort(seconds) {
@@ -2026,7 +2154,7 @@ function formatTimestamp(ts) {
2026
2154
  return new Date(Number(ts) * 1e3).toLocaleString();
2027
2155
  }
2028
2156
  function parseCallsFile(path) {
2029
- const raw = readFileSync(path, "utf-8");
2157
+ const raw = readFileSync2(path, "utf-8");
2030
2158
  const parsed = JSON.parse(raw);
2031
2159
  return parsed.map((c) => ({
2032
2160
  target: c.target,
@@ -2512,7 +2640,7 @@ try {
2512
2640
  var require2 = createRequire(import.meta.url);
2513
2641
  var { version: CLI_VERSION } = require2("../package.json");
2514
2642
  async function loadXmtp() {
2515
- return import("./xmtp-BSTLCZWG.js");
2643
+ return import("./xmtp-DX73F6I4.js");
2516
2644
  }
2517
2645
  async function loadCron() {
2518
2646
  return import("./cron-SKYKVZ6K.js");
@@ -3189,7 +3317,7 @@ var vaultCmd = program.command("vault");
3189
3317
  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
3318
  resolveVault(opts);
3191
3319
  const decimals = await getAssetDecimals();
3192
- const amount = parseUnits8(opts.amount, decimals);
3320
+ const amount = parseUnits7(opts.amount, decimals);
3193
3321
  const spinner = ora7(`Depositing ${opts.amount}...`).start();
3194
3322
  try {
3195
3323
  const hash = await deposit(amount);
@@ -3241,81 +3369,8 @@ vaultCmd.command("balance").description("Show LP share balance and asset value")
3241
3369
  process.exit(1);
3242
3370
  }
3243
3371
  });
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
- });
3372
+ var strategy = program.command("strategy").description("Strategy templates \u2014 list, clone, propose");
3373
+ registerStrategyTemplateCommands(strategy);
3319
3374
  program.command("providers").description("List available DeFi providers").action(async () => {
3320
3375
  const { MessariProvider, NansenProvider } = await import("./research-IUHVRHR3.js");
3321
3376
  const providers = [new MoonwellProvider(), new UniswapProvider(), new MessariProvider(), new NansenProvider()];
@@ -3328,7 +3383,7 @@ ${info.name} (${info.type})`);
3328
3383
  }
3329
3384
  });
3330
3385
  try {
3331
- const { registerChatCommands } = await import("./chat-A2ORZVU3.js");
3386
+ const { registerChatCommands } = await import("./chat-G5TRNY5S.js");
3332
3387
  registerChatCommands(program);
3333
3388
  } catch {
3334
3389
  program.command("chat <name> [action] [actionArgs...]").description("Syndicate chat (XMTP) \u2014 requires @xmtp/cli").action(() => {
@@ -3338,14 +3393,14 @@ try {
3338
3393
  process.exit(1);
3339
3394
  });
3340
3395
  }
3341
- var { registerSessionCommands } = await import("./session-3YRZI426.js");
3396
+ var { registerSessionCommands } = await import("./session-VEF5BZIX.js");
3342
3397
  registerSessionCommands(program);
3343
3398
  registerVeniceCommands(program);
3344
3399
  registerAllowanceCommands(program);
3345
3400
  registerIdentityCommands(program);
3346
3401
  registerProposalCommands(program);
3347
3402
  registerGovernorCommands(program);
3348
- var { registerResearchCommands } = await import("./research-63D7YID3.js");
3403
+ var { registerResearchCommands } = await import("./research-FNH3VT57.js");
3349
3404
  registerResearchCommands(program);
3350
3405
  var configCmd = program.command("config");
3351
3406
  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) => {