@sherwoodagent/cli 0.22.8 → 0.25.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.
Files changed (48) hide show
  1. package/dist/{chat-JYAJMP4D.js → chat-52PWNH7Y.js} +7 -7
  2. package/dist/{chunk-4YIE7GHL.js → chunk-66VFSLPS.js} +2 -2
  3. package/dist/{chunk-VO6DCRWV.js → chunk-7PCI3WHQ.js} +4 -4
  4. package/dist/{chunk-LVPBJ7MI.js → chunk-GWIW6DDG.js} +2 -2
  5. package/dist/{chunk-4Y6Y4SVH.js → chunk-JEVBC7DV.js} +4 -4
  6. package/dist/{chunk-G7JQRLQE.js → chunk-QBNWBWD3.js} +5 -5
  7. package/dist/{chunk-B42ZCCZ7.js → chunk-RGOTBJIN.js} +28 -3
  8. package/dist/chunk-RGOTBJIN.js.map +1 -0
  9. package/dist/{chunk-ASJTFFZH.js → chunk-SBVHGUQJ.js} +3 -3
  10. package/dist/{chunk-Y7LS6DEF.js → chunk-VQIFAXUY.js} +5 -5
  11. package/dist/{chunk-JR5K5WVR.js → chunk-ZPLUJT4V.js} +169 -13
  12. package/dist/chunk-ZPLUJT4V.js.map +1 -0
  13. package/dist/{client-SCIY6SUP.js → client-CWQFYZ5Z.js} +4 -4
  14. package/dist/{config-BGS5MX4D.js → config-YXZKPSZB.js} +8 -2
  15. package/dist/{eas-X5UIEM6D.js → eas-YZIHYQAI.js} +6 -6
  16. package/dist/{governor-ZKRSAHG4.js → governor-YPDAOT3S.js} +6 -6
  17. package/dist/index.js +741 -74
  18. package/dist/index.js.map +1 -1
  19. package/dist/{network-UTSOP7EF.js → network-QEMP3DSP.js} +3 -3
  20. package/dist/price-SWNEGWO4.js +156 -0
  21. package/dist/price-SWNEGWO4.js.map +1 -0
  22. package/dist/{research-EWH43HFN.js → research-PH77VSE4.js} +9 -9
  23. package/dist/research-ZX7T2ZIJ.js +14 -0
  24. package/dist/{session-XZNKLK3I.js → session-NMA4RLKC.js} +12 -12
  25. package/dist/{trade-VKSXEHWW.js → trade-N2A74UPX.js} +16 -16
  26. package/dist/{xmtp-XKHXATPQ.js → xmtp-YNOGIZ4G.js} +6 -6
  27. package/package.json +1 -1
  28. package/dist/chunk-B42ZCCZ7.js.map +0 -1
  29. package/dist/chunk-JR5K5WVR.js.map +0 -1
  30. package/dist/research-W3M3VZBD.js +0 -14
  31. /package/dist/{chat-JYAJMP4D.js.map → chat-52PWNH7Y.js.map} +0 -0
  32. /package/dist/{chunk-4YIE7GHL.js.map → chunk-66VFSLPS.js.map} +0 -0
  33. /package/dist/{chunk-VO6DCRWV.js.map → chunk-7PCI3WHQ.js.map} +0 -0
  34. /package/dist/{chunk-LVPBJ7MI.js.map → chunk-GWIW6DDG.js.map} +0 -0
  35. /package/dist/{chunk-4Y6Y4SVH.js.map → chunk-JEVBC7DV.js.map} +0 -0
  36. /package/dist/{chunk-G7JQRLQE.js.map → chunk-QBNWBWD3.js.map} +0 -0
  37. /package/dist/{chunk-ASJTFFZH.js.map → chunk-SBVHGUQJ.js.map} +0 -0
  38. /package/dist/{chunk-Y7LS6DEF.js.map → chunk-VQIFAXUY.js.map} +0 -0
  39. /package/dist/{client-SCIY6SUP.js.map → client-CWQFYZ5Z.js.map} +0 -0
  40. /package/dist/{config-BGS5MX4D.js.map → config-YXZKPSZB.js.map} +0 -0
  41. /package/dist/{eas-X5UIEM6D.js.map → eas-YZIHYQAI.js.map} +0 -0
  42. /package/dist/{governor-ZKRSAHG4.js.map → governor-YPDAOT3S.js.map} +0 -0
  43. /package/dist/{network-UTSOP7EF.js.map → network-QEMP3DSP.js.map} +0 -0
  44. /package/dist/{research-EWH43HFN.js.map → research-PH77VSE4.js.map} +0 -0
  45. /package/dist/{research-W3M3VZBD.js.map → research-ZX7T2ZIJ.js.map} +0 -0
  46. /package/dist/{session-XZNKLK3I.js.map → session-NMA4RLKC.js.map} +0 -0
  47. /package/dist/{trade-VKSXEHWW.js.map → trade-N2A74UPX.js.map} +0 -0
  48. /package/dist/{xmtp-XKHXATPQ.js.map → xmtp-YNOGIZ4G.js.map} +0 -0
package/dist/index.js CHANGED
@@ -1,4 +1,22 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ approveDepositor,
4
+ deposit,
5
+ depositWithEthWrap,
6
+ executeBatch,
7
+ getAssetDecimals,
8
+ getBalance,
9
+ getVaultAddress,
10
+ getVaultInfo,
11
+ isAgent,
12
+ preflightDeposit,
13
+ registerAgent,
14
+ removeDepositor,
15
+ resolveSyndicate,
16
+ resolveVaultSyndicate,
17
+ setTextRecord,
18
+ setVaultAddress
19
+ } from "./chunk-QBNWBWD3.js";
2
20
  import {
3
21
  UniswapProvider,
4
22
  applySlippage,
@@ -9,7 +27,7 @@ import {
9
27
  getQuote,
10
28
  listModels,
11
29
  provisionApiKey
12
- } from "./chunk-Y7LS6DEF.js";
30
+ } from "./chunk-VQIFAXUY.js";
13
31
  import {
14
32
  PROPOSAL_STATE,
15
33
  PROPOSAL_STATES,
@@ -40,7 +58,7 @@ import {
40
58
  setVotingPeriod,
41
59
  settleProposal,
42
60
  vote
43
- } from "./chunk-VO6DCRWV.js";
61
+ } from "./chunk-7PCI3WHQ.js";
44
62
  import {
45
63
  fetchMetadata,
46
64
  uploadMetadata
@@ -54,43 +72,28 @@ import {
54
72
  queryJoinRequests,
55
73
  revokeAttestation,
56
74
  stripReferrerPrefix
57
- } from "./chunk-4Y6Y4SVH.js";
58
- import {
59
- approveDepositor,
60
- deposit,
61
- depositWithEthWrap,
62
- executeBatch,
63
- getAssetDecimals,
64
- getBalance,
65
- getVaultAddress,
66
- getVaultInfo,
67
- isAgent,
68
- preflightDeposit,
69
- registerAgent,
70
- removeDepositor,
71
- resolveSyndicate,
72
- resolveVaultSyndicate,
73
- setTextRecord,
74
- setVaultAddress
75
- } from "./chunk-G7JQRLQE.js";
75
+ } from "./chunk-JEVBC7DV.js";
76
76
  import {
77
77
  AERODROME,
78
78
  AGENT_REGISTRY,
79
79
  BASE_STRATEGY_ABI,
80
+ CHAINLINK,
80
81
  EAS_SCHEMAS,
81
82
  ERC20_ABI,
82
83
  MOONWELL,
84
+ PORTFOLIO_STRATEGY_ABI,
83
85
  SHERWOOD,
84
86
  STRATEGY_TEMPLATES,
85
87
  SWAP_ROUTER_ABI,
86
88
  SWAP_ROUTER_EXACT_INPUT_SINGLE_ABI,
87
89
  SYNDICATE_FACTORY_ABI,
88
90
  SYNDICATE_VAULT_ABI,
91
+ SYNTHRA,
89
92
  TOKENS,
90
93
  UNISWAP,
91
94
  VENICE,
92
95
  VENICE_STAKING_ABI
93
- } from "./chunk-JR5K5WVR.js";
96
+ } from "./chunk-ZPLUJT4V.js";
94
97
  import {
95
98
  formatContractError,
96
99
  getAccount,
@@ -98,8 +101,9 @@ import {
98
101
  sendTxWithRetry,
99
102
  waitForReceipt,
100
103
  writeContractWithRetry
101
- } from "./chunk-ASJTFFZH.js";
104
+ } from "./chunk-SBVHGUQJ.js";
102
105
  import {
106
+ CHAIN_REGISTRY,
103
107
  VALID_NETWORKS,
104
108
  getChain,
105
109
  getExplorerUrl,
@@ -107,11 +111,13 @@ import {
107
111
  getRpcUrl,
108
112
  isTestnet,
109
113
  setNetwork
110
- } from "./chunk-LVPBJ7MI.js";
114
+ } from "./chunk-GWIW6DDG.js";
111
115
  import {
112
116
  addSyndicate,
113
117
  cacheGroupId,
118
+ cacheSwapRoute,
114
119
  getAgentId,
120
+ getCachedSwapRoute,
115
121
  getChainContracts,
116
122
  getNotifyTo,
117
123
  getPrimarySyndicate,
@@ -127,7 +133,7 @@ import {
127
133
  setPrivateKey,
128
134
  setUniswapApiKey,
129
135
  setVeniceApiKey
130
- } from "./chunk-B42ZCCZ7.js";
136
+ } from "./chunk-RGOTBJIN.js";
131
137
 
132
138
  // src/index.ts
133
139
  import { config as loadDotenv } from "dotenv";
@@ -173,7 +179,7 @@ var MoonwellProvider = class {
173
179
  };
174
180
 
175
181
  // src/commands/strategy-template.ts
176
- import { parseUnits, isAddress, erc20Abi } from "viem";
182
+ import { parseUnits, formatUnits, isAddress, erc20Abi, encodeAbiParameters as encodeAbiParameters7, decodeAbiParameters } from "viem";
177
183
  import chalk from "chalk";
178
184
  import ora from "ora";
179
185
  import { writeFileSync, mkdirSync } from "fs";
@@ -483,8 +489,189 @@ function buildSettleCalls5(clone) {
483
489
  ];
484
490
  }
485
491
 
492
+ // src/strategies/portfolio-template.ts
493
+ import { encodeAbiParameters as encodeAbiParameters6, encodeFunctionData as encodeFunctionData6 } from "viem";
494
+ function buildInitData6(asset, swapAdapter, chainlinkVerifier, allocations, totalAmount, maxSlippageBps) {
495
+ const tokens = allocations.map((a) => a.token);
496
+ const weightsBps = allocations.map((a) => BigInt(a.weightBps));
497
+ const swapExtraData = allocations.map((a) => a.swapExtraData);
498
+ return encodeAbiParameters6(
499
+ [
500
+ { type: "address" },
501
+ { type: "address" },
502
+ { type: "address" },
503
+ { type: "address[]" },
504
+ { type: "uint256[]" },
505
+ { type: "uint256" },
506
+ { type: "uint256" },
507
+ { type: "bytes[]" }
508
+ ],
509
+ [
510
+ asset,
511
+ swapAdapter,
512
+ chainlinkVerifier,
513
+ tokens,
514
+ weightsBps,
515
+ totalAmount,
516
+ BigInt(maxSlippageBps),
517
+ swapExtraData
518
+ ]
519
+ );
520
+ }
521
+ function buildExecuteCalls6(clone, asset, totalAmount) {
522
+ return [
523
+ {
524
+ target: asset,
525
+ data: encodeFunctionData6({
526
+ abi: ERC20_ABI,
527
+ functionName: "approve",
528
+ args: [clone, totalAmount]
529
+ }),
530
+ value: 0n
531
+ },
532
+ {
533
+ target: clone,
534
+ data: encodeFunctionData6({
535
+ abi: BASE_STRATEGY_ABI,
536
+ functionName: "execute"
537
+ }),
538
+ value: 0n
539
+ }
540
+ ];
541
+ }
542
+ function buildSettleCalls6(clone) {
543
+ return [
544
+ {
545
+ target: clone,
546
+ data: encodeFunctionData6({
547
+ abi: BASE_STRATEGY_ABI,
548
+ functionName: "settle"
549
+ }),
550
+ value: 0n
551
+ }
552
+ ];
553
+ }
554
+
486
555
  // src/commands/strategy-template.ts
556
+ import { concat as concat2, numberToHex } from "viem";
487
557
  var ZERO = "0x0000000000000000000000000000000000000000";
558
+ function encodeV3Path(tokens, fees) {
559
+ if (tokens.length !== fees.length + 1) throw new Error("tokens.length must be fees.length + 1");
560
+ const parts = [];
561
+ for (let i = 0; i < tokens.length; i++) {
562
+ parts.push(tokens[i].toLowerCase());
563
+ if (i < fees.length) {
564
+ parts.push(numberToHex(fees[i], { size: 3 }));
565
+ }
566
+ }
567
+ return concat2(parts);
568
+ }
569
+ function buildSwapExtraData(network, tokenIn, tokenOut, feeTier, hop) {
570
+ if (network === "robinhood-testnet") {
571
+ return encodeAbiParameters7([{ type: "uint24" }], [feeTier]);
572
+ }
573
+ if (hop) {
574
+ const path = encodeV3Path([tokenIn, hop.via, tokenOut], [hop.feeIn, hop.feeOut]);
575
+ const encoded2 = encodeAbiParameters7([{ type: "bytes" }], [path]);
576
+ return `0x01${encoded2.slice(2)}`;
577
+ }
578
+ const encoded = encodeAbiParameters7([{ type: "uint24" }], [feeTier]);
579
+ return `0x00${encoded.slice(2)}`;
580
+ }
581
+ async function detectSwapRoute(asset, token, preferredFeeTier) {
582
+ const network = getNetwork();
583
+ if (network === "robinhood-testnet") {
584
+ return {
585
+ extraData: buildSwapExtraData(network, asset, token, preferredFeeTier),
586
+ routeDesc: `direct (fee ${preferredFeeTier})`
587
+ };
588
+ }
589
+ const chainId = CHAIN_REGISTRY[network].chain.id;
590
+ const cached = getCachedSwapRoute(chainId, asset, token);
591
+ if (cached) {
592
+ const extraData = cached.hop ? buildSwapExtraData(network, asset, token, 0, {
593
+ via: cached.hop.via,
594
+ feeIn: cached.hop.feeIn,
595
+ feeOut: cached.hop.feeOut
596
+ }) : buildSwapExtraData(network, asset, token, cached.feeTier);
597
+ const desc = cached.hop ? `cached multi-hop: ${cached.hop.feeIn}\u2192WETH\u2192${cached.hop.feeOut}` : `cached direct (fee ${cached.feeTier})`;
598
+ return { extraData, routeDesc: desc };
599
+ }
600
+ const publicClient = getPublicClient();
601
+ const quoterAddr = UNISWAP().QUOTER_V2;
602
+ const quoterAbi = [{
603
+ type: "function",
604
+ name: "quoteExactInputSingle",
605
+ inputs: [{ type: "tuple", components: [
606
+ { type: "address", name: "tokenIn" },
607
+ { type: "address", name: "tokenOut" },
608
+ { type: "uint256", name: "amountIn" },
609
+ { type: "uint24", name: "fee" },
610
+ { type: "uint160", name: "sqrtPriceLimitX96" }
611
+ ] }],
612
+ outputs: [
613
+ { type: "uint256" },
614
+ { type: "uint160" },
615
+ { type: "uint32" },
616
+ { type: "uint256" }
617
+ ],
618
+ stateMutability: "nonpayable"
619
+ }];
620
+ const testAmount = asset.toLowerCase() === TOKENS().USDC.toLowerCase() ? 1000000n : 1000000000000000n;
621
+ const feeTiers = [preferredFeeTier, 1e4, 3e3, 500].filter((v, i, a) => a.indexOf(v) === i);
622
+ for (const fee of feeTiers) {
623
+ try {
624
+ await publicClient.simulateContract({
625
+ address: quoterAddr,
626
+ abi: quoterAbi,
627
+ functionName: "quoteExactInputSingle",
628
+ args: [{ tokenIn: asset, tokenOut: token, amountIn: testAmount, fee, sqrtPriceLimitX96: 0n }]
629
+ });
630
+ cacheSwapRoute(chainId, asset, token, { mode: "direct", feeTier: fee, detectedAt: Math.floor(Date.now() / 1e3) });
631
+ return {
632
+ extraData: buildSwapExtraData(network, asset, token, fee),
633
+ routeDesc: `direct (fee ${fee})`
634
+ };
635
+ } catch {
636
+ }
637
+ }
638
+ const weth = TOKENS().WETH;
639
+ if (asset.toLowerCase() === weth.toLowerCase()) return null;
640
+ let bestAssetToWethFee = null;
641
+ for (const fee of [500, 3e3, 1e4]) {
642
+ try {
643
+ await publicClient.simulateContract({
644
+ address: quoterAddr,
645
+ abi: quoterAbi,
646
+ functionName: "quoteExactInputSingle",
647
+ args: [{ tokenIn: asset, tokenOut: weth, amountIn: testAmount, fee, sqrtPriceLimitX96: 0n }]
648
+ });
649
+ bestAssetToWethFee = fee;
650
+ break;
651
+ } catch {
652
+ }
653
+ }
654
+ if (!bestAssetToWethFee) return null;
655
+ const wethTestAmount = 1000000000000000n;
656
+ for (const fee of feeTiers) {
657
+ try {
658
+ await publicClient.simulateContract({
659
+ address: quoterAddr,
660
+ abi: quoterAbi,
661
+ functionName: "quoteExactInputSingle",
662
+ args: [{ tokenIn: weth, tokenOut: token, amountIn: wethTestAmount, fee, sqrtPriceLimitX96: 0n }]
663
+ });
664
+ const hop = { via: weth, feeIn: bestAssetToWethFee, feeOut: fee };
665
+ cacheSwapRoute(chainId, asset, token, { mode: "multi-hop", feeTier: 0, hop, detectedAt: Math.floor(Date.now() / 1e3) });
666
+ return {
667
+ extraData: buildSwapExtraData(network, asset, token, 0, hop),
668
+ routeDesc: `multi-hop: ${bestAssetToWethFee}\u2192WETH\u2192${fee}`
669
+ };
670
+ } catch {
671
+ }
672
+ }
673
+ return null;
674
+ }
488
675
  var TEMPLATES = [
489
676
  {
490
677
  name: "Moonwell Supply",
@@ -515,6 +702,12 @@ var TEMPLATES = [
515
702
  key: "mamo-yield",
516
703
  description: "Deposit into Mamo for optimized yield across Moonwell + Morpho vaults",
517
704
  addressKey: "MAMO_YIELD"
705
+ },
706
+ {
707
+ name: "Portfolio",
708
+ key: "portfolio",
709
+ description: "Weighted portfolio of tokens (stock tokens, crypto) with rebalancing",
710
+ addressKey: "PORTFOLIO"
518
711
  }
519
712
  ];
520
713
  function resolveTemplate(key) {
@@ -709,6 +902,67 @@ async function buildInitDataForTemplate(templateKey, opts, vault) {
709
902
  assetAmount: amount
710
903
  };
711
904
  }
905
+ if (templateKey === "portfolio") {
906
+ if (!opts.amount) {
907
+ console.error(chalk.red("--amount is required for portfolio template"));
908
+ process.exit(1);
909
+ }
910
+ if (!opts.tokens || !opts.weights) {
911
+ console.error(chalk.red("--tokens and --weights are required for portfolio template"));
912
+ console.error(chalk.dim(" --tokens: comma-separated token addresses or symbols"));
913
+ console.error(chalk.dim(" --weights: comma-separated bps (must sum to 10000)"));
914
+ process.exit(1);
915
+ }
916
+ const tokens = TOKENS();
917
+ const defaultAsset = getNetwork() === "robinhood-testnet" ? "WETH" : "USDC";
918
+ const assetSymbol = opts.asset || defaultAsset;
919
+ const asset = resolveToken(assetSymbol);
920
+ const decimals = assetSymbol.toUpperCase() === "USDC" ? 6 : 18;
921
+ const totalAmount = parseUnits(opts.amount, decimals);
922
+ const maxSlippageBps = Number(opts.maxSlippage || "500");
923
+ const feeTier = opts.feeTier || "3000";
924
+ const tokenAddrs = opts.tokens.split(",").map((t) => {
925
+ const trimmed = t.trim();
926
+ if (isAddress(trimmed)) return trimmed;
927
+ const allTokens = tokens;
928
+ const resolved = allTokens[trimmed.toUpperCase()];
929
+ if (resolved && resolved !== ZERO) return resolved;
930
+ console.error(chalk.red(`Unknown token: ${trimmed}`));
931
+ process.exit(1);
932
+ });
933
+ const weightsBps = opts.weights.split(",").map((w) => Number(w.trim()));
934
+ if (tokenAddrs.length !== weightsBps.length) {
935
+ console.error(chalk.red("--tokens and --weights must have same length"));
936
+ process.exit(1);
937
+ }
938
+ if (weightsBps.reduce((a, b) => a + b, 0) !== 1e4) {
939
+ console.error(chalk.red(`Weights must sum to 10000`));
940
+ process.exit(1);
941
+ }
942
+ const swapAdapter = opts.swapAdapter || resolveSwapAdapter();
943
+ const chainlinkVerifier = CHAINLINK().VERIFIER_PROXY;
944
+ console.log(chalk.dim(" Detecting swap routes..."));
945
+ const allocations = [];
946
+ for (let i = 0; i < tokenAddrs.length; i++) {
947
+ const token = tokenAddrs[i];
948
+ const route = await detectSwapRoute(asset, token, Number(feeTier));
949
+ if (!route) {
950
+ console.error(chalk.red(`No swap route found for ${token}. No Uniswap V3 pool (direct or via WETH).`));
951
+ process.exit(1);
952
+ }
953
+ console.log(chalk.dim(` ${token.slice(0, 10)}... \u2192 ${route.routeDesc}`));
954
+ allocations.push({
955
+ token,
956
+ weightBps: weightsBps[i],
957
+ swapExtraData: route.extraData
958
+ });
959
+ }
960
+ return {
961
+ initData: buildInitData6(asset, swapAdapter, chainlinkVerifier, allocations, totalAmount, maxSlippageBps),
962
+ asset,
963
+ assetAmount: totalAmount
964
+ };
965
+ }
712
966
  throw new Error(`No init builder for template: ${templateKey}`);
713
967
  }
714
968
  function buildCallsForTemplate(templateKey, clone, asset, assetAmount, extraApprovals) {
@@ -744,8 +998,28 @@ function buildCallsForTemplate(templateKey, clone, asset, assetAmount, extraAppr
744
998
  settleCalls: buildSettleCalls5(clone)
745
999
  };
746
1000
  }
1001
+ if (templateKey === "portfolio") {
1002
+ return {
1003
+ executeCalls: buildExecuteCalls6(clone, asset, assetAmount),
1004
+ settleCalls: buildSettleCalls6(clone)
1005
+ };
1006
+ }
747
1007
  throw new Error(`No call builder for template: ${templateKey}`);
748
1008
  }
1009
+ function resolveSwapAdapter() {
1010
+ const network = getNetwork();
1011
+ if (network === "robinhood-testnet") {
1012
+ if (SYNTHRA().ROUTER === ZERO) {
1013
+ console.error(chalk.red("Synthra DEX not available"));
1014
+ process.exit(1);
1015
+ }
1016
+ return "0xdae81cDCfcB14c56fCeB788A147Fcd6CbEdfEeca";
1017
+ }
1018
+ const adapterAddr = UNISWAP().SWAP_ADAPTER;
1019
+ if (adapterAddr !== ZERO) return adapterAddr;
1020
+ console.error(chalk.red("UniswapSwapAdapter not deployed yet. Use --swap-adapter to specify manually."));
1021
+ process.exit(1);
1022
+ }
749
1023
  function resolveToken(symbolOrAddress) {
750
1024
  if (isAddress(symbolOrAddress)) return symbolOrAddress;
751
1025
  const upper = symbolOrAddress.toUpperCase();
@@ -792,23 +1066,39 @@ function serializeCalls(calls) {
792
1066
  function registerStrategyTemplateCommands(strategy2) {
793
1067
  strategy2.command("list").description("List available strategy templates").action(() => {
794
1068
  const templates = STRATEGY_TEMPLATES();
1069
+ const network = getNetwork();
795
1070
  console.log();
796
- console.log(chalk.bold("Strategy Templates"));
1071
+ console.log(chalk.bold("Strategy Templates"), chalk.dim(`(${network})`));
797
1072
  console.log(chalk.dim("\u2500".repeat(60)));
1073
+ let availableCount = 0;
798
1074
  for (const t of TEMPLATES) {
799
1075
  const addr = templates[t.addressKey];
800
- const deployed = addr !== ZERO;
1076
+ if (addr !== ZERO) {
1077
+ availableCount++;
1078
+ console.log();
1079
+ console.log(` ${chalk.bold(t.name)} (${chalk.cyan(t.key)})`);
1080
+ console.log(` ${t.description}`);
1081
+ console.log(` Template: ${chalk.green(addr)}`);
1082
+ }
1083
+ }
1084
+ if (availableCount === 0) {
801
1085
  console.log();
802
- console.log(` ${chalk.bold(t.name)} (${chalk.cyan(t.key)})`);
803
- console.log(` ${t.description}`);
804
- console.log(` Template: ${deployed ? chalk.green(addr) : chalk.red("not deployed")}`);
1086
+ console.log(chalk.yellow(" No strategy templates deployed on this network."));
1087
+ }
1088
+ const unavailable = TEMPLATES.filter((t) => templates[t.addressKey] === ZERO);
1089
+ if (unavailable.length > 0) {
1090
+ console.log();
1091
+ console.log(chalk.dim(` Not available on ${network}:`));
1092
+ for (const t of unavailable) {
1093
+ console.log(chalk.dim(` ${t.name} (${t.key})`));
1094
+ }
805
1095
  }
806
1096
  console.log();
807
1097
  console.log(chalk.dim("Clone a template: sherwood strategy clone <template> --vault <addr> ..."));
808
1098
  console.log(chalk.dim("Full proposal: sherwood strategy propose <template> --vault <addr> ..."));
809
1099
  console.log();
810
1100
  });
811
- strategy2.command("clone").description("Clone a strategy template and initialize it").argument("<template>", "Template: moonwell-supply, aerodrome-lp, venice-inference, wsteth-moonwell, mamo-yield").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%)").option("--mamo-factory <address>", "Mamo StrategyFactory address (Mamo)").action(async (templateKey, opts) => {
1101
+ strategy2.command("clone").description("Clone a strategy template and initialize it").argument("<template>", "Template: moonwell-supply, aerodrome-lp, venice-inference, wsteth-moonwell, mamo-yield, portfolio").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%)").option("--mamo-factory <address>", "Mamo StrategyFactory address (Mamo)").option("--tokens <list>", "Comma-separated token addresses or symbols (Portfolio)").option("--weights <list>", "Comma-separated weights in bps, must sum to 10000 (Portfolio)").option("--max-slippage <bps>", "Max slippage bps (Portfolio, default: 500)").option("--fee-tier <n>", "Pool fee tier (Portfolio, default: 3000)").option("--swap-adapter <address>", "Swap adapter address (Portfolio)").action(async (templateKey, opts) => {
812
1102
  const vault = opts.vault;
813
1103
  if (!isAddress(vault)) {
814
1104
  console.error(chalk.red("Invalid vault address"));
@@ -856,7 +1146,7 @@ function registerStrategyTemplateCommands(strategy2) {
856
1146
  console.log(chalk.dim("Use this address in your proposal batch calls."));
857
1147
  console.log();
858
1148
  });
859
- strategy2.command("init").description("Initialize an already-deployed but uninitialized strategy clone").argument("<template>", "Template: moonwell-supply, aerodrome-lp, venice-inference, wsteth-moonwell, mamo-yield").requiredOption("--clone <address>", "Clone address to initialize").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%)").option("--mamo-factory <address>", "Mamo StrategyFactory address (Mamo)").action(async (templateKey, opts) => {
1149
+ strategy2.command("init").description("Initialize an already-deployed but uninitialized strategy clone").argument("<template>", "Template: moonwell-supply, aerodrome-lp, venice-inference, wsteth-moonwell, mamo-yield, portfolio").requiredOption("--clone <address>", "Clone address to initialize").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%)").option("--mamo-factory <address>", "Mamo StrategyFactory address (Mamo)").option("--tokens <list>", "Comma-separated token addresses or symbols (Portfolio)").option("--weights <list>", "Comma-separated weights in bps, must sum to 10000 (Portfolio)").option("--max-slippage <bps>", "Max slippage bps (Portfolio, default: 500)").option("--fee-tier <n>", "Pool fee tier (Portfolio, default: 3000)").option("--swap-adapter <address>", "Swap adapter address (Portfolio)").action(async (templateKey, opts) => {
860
1150
  const clone = opts.clone;
861
1151
  const vault = opts.vault;
862
1152
  if (!isAddress(clone)) {
@@ -912,7 +1202,7 @@ function registerStrategyTemplateCommands(strategy2) {
912
1202
  console.log(` Proposer: ${chalk.green(getAccount().address)}`);
913
1203
  console.log();
914
1204
  });
915
- 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, mamo-yield").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%)").option("--mamo-factory <address>", "Mamo StrategyFactory address (Mamo)").action(async (templateKey, opts) => {
1205
+ 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, mamo-yield, portfolio").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%)").option("--mamo-factory <address>", "Mamo StrategyFactory address (Mamo)").option("--tokens <list>", "Comma-separated token addresses or symbols (Portfolio)").option("--weights <list>", "Comma-separated weights in bps, must sum to 10000 (Portfolio)").option("--max-slippage <bps>", "Max slippage bps (Portfolio, default: 500)").option("--fee-tier <n>", "Pool fee tier (Portfolio, default: 3000)").option("--swap-adapter <address>", "Swap adapter address (Portfolio)").action(async (templateKey, opts) => {
916
1206
  const vault = opts.vault;
917
1207
  if (!isAddress(vault)) {
918
1208
  console.error(chalk.red("Invalid vault address"));
@@ -1003,9 +1293,9 @@ function registerStrategyTemplateCommands(strategy2) {
1003
1293
  console.error(chalk.red("Missing --name, --performance-fee, or --duration. Use --write-calls to skip proposal submission."));
1004
1294
  process.exit(1);
1005
1295
  }
1006
- const { propose: propose2 } = await import("./governor-ZKRSAHG4.js");
1296
+ const { propose: propose2 } = await import("./governor-YPDAOT3S.js");
1007
1297
  const { pinJSON } = await import("./ipfs-22YLNQ2C.js");
1008
- const { parseDuration: parseDuration2 } = await import("./governor-ZKRSAHG4.js");
1298
+ const { parseDuration: parseDuration2 } = await import("./governor-YPDAOT3S.js");
1009
1299
  const performanceFeeBps = BigInt(opts.performanceFee);
1010
1300
  if (performanceFeeBps < 0n || performanceFeeBps > 10000n) {
1011
1301
  console.error(chalk.red("--performance-fee must be 0-10000 (basis points)"));
@@ -1063,6 +1353,380 @@ function registerStrategyTemplateCommands(strategy2) {
1063
1353
  }
1064
1354
  console.log();
1065
1355
  });
1356
+ strategy2.command("status").description("Show portfolio strategy status \u2014 allocations, balances, drift, PnL (Portfolio only)").argument("<clone>", "Strategy clone address").option("--json", "Output machine-readable JSON for agent consumption").action(async (cloneArg, opts) => {
1357
+ if (!isAddress(cloneArg)) {
1358
+ console.error(chalk.red("Invalid clone address"));
1359
+ process.exit(1);
1360
+ }
1361
+ const clone = cloneArg;
1362
+ const publicClient = getPublicClient();
1363
+ const network = getNetwork();
1364
+ const jsonMode = !!opts.json;
1365
+ const spinner = jsonMode ? null : ora("Reading strategy state...").start();
1366
+ try {
1367
+ const [stateRaw, vault, proposer, assetAddr, totalAmount, maxSlippage, allocations, swapExtraData] = await Promise.all([
1368
+ publicClient.readContract({ address: clone, abi: PORTFOLIO_STRATEGY_ABI, functionName: "state" }),
1369
+ publicClient.readContract({ address: clone, abi: PORTFOLIO_STRATEGY_ABI, functionName: "vault" }),
1370
+ publicClient.readContract({ address: clone, abi: PORTFOLIO_STRATEGY_ABI, functionName: "proposer" }),
1371
+ publicClient.readContract({ address: clone, abi: PORTFOLIO_STRATEGY_ABI, functionName: "asset" }),
1372
+ publicClient.readContract({ address: clone, abi: PORTFOLIO_STRATEGY_ABI, functionName: "totalAmount" }),
1373
+ publicClient.readContract({ address: clone, abi: PORTFOLIO_STRATEGY_ABI, functionName: "maxSlippageBps" }),
1374
+ publicClient.readContract({ address: clone, abi: PORTFOLIO_STRATEGY_ABI, functionName: "getAllocations" }),
1375
+ publicClient.readContract({ address: clone, abi: PORTFOLIO_STRATEGY_ABI, functionName: "getSwapExtraData" })
1376
+ ]);
1377
+ const stateNames = ["Pending", "Executed", "Settled"];
1378
+ const stateStr = stateNames[Number(stateRaw)] || `Unknown(${stateRaw})`;
1379
+ const [assetDecimals, assetSymbol] = await Promise.all([
1380
+ publicClient.readContract({ address: assetAddr, abi: erc20Abi, functionName: "decimals" }),
1381
+ publicClient.readContract({ address: assetAddr, abi: erc20Abi, functionName: "symbol" })
1382
+ ]);
1383
+ const tokenReads = allocations.map((a) => Promise.all([
1384
+ publicClient.readContract({ address: a.token, abi: erc20Abi, functionName: "balanceOf", args: [clone] }),
1385
+ publicClient.readContract({ address: a.token, abi: erc20Abi, functionName: "symbol" }),
1386
+ publicClient.readContract({ address: a.token, abi: erc20Abi, functionName: "decimals" })
1387
+ ]));
1388
+ const tokenData = await Promise.all(tokenReads);
1389
+ const assetBalance = await publicClient.readContract({
1390
+ address: assetAddr,
1391
+ abi: erc20Abi,
1392
+ functionName: "balanceOf",
1393
+ args: [clone]
1394
+ });
1395
+ const feeTiers = swapExtraData.map((data) => {
1396
+ try {
1397
+ const [fee] = decodeAbiParameters([{ type: "uint24" }], data);
1398
+ return Number(fee) > 0 && Number(fee) <= 1e5 ? Number(fee) : 3e3;
1399
+ } catch {
1400
+ return 3e3;
1401
+ }
1402
+ });
1403
+ let prices = [];
1404
+ let priceSource = null;
1405
+ try {
1406
+ const { getTokenPricesInAsset } = await import("./price-SWNEGWO4.js");
1407
+ prices = await getTokenPricesInAsset({
1408
+ tokens: allocations.map((a, i) => ({
1409
+ token: a.token,
1410
+ tokenDecimals: tokenData[i][2],
1411
+ feeTier: feeTiers[i]
1412
+ })),
1413
+ asset: assetAddr,
1414
+ assetDecimals
1415
+ });
1416
+ priceSource = prices.find((p) => p !== null)?.source ?? null;
1417
+ } catch {
1418
+ }
1419
+ const pricesAvailable = prices.some((p) => p !== null);
1420
+ const assetBalanceNum = Number(formatUnits(assetBalance, assetDecimals));
1421
+ let totalPortfolioValue = assetBalanceNum;
1422
+ let totalInvested = 0n;
1423
+ const allocStatuses = allocations.map((alloc, i) => {
1424
+ const [balance, symbol, decimals] = tokenData[i];
1425
+ const balanceNum = Number(formatUnits(balance, decimals));
1426
+ const investedNum = Number(formatUnits(alloc.investedAmount, assetDecimals));
1427
+ totalInvested += alloc.investedAmount;
1428
+ const price = prices[i];
1429
+ let currentValue = null;
1430
+ if (price) {
1431
+ currentValue = balanceNum * price.price;
1432
+ totalPortfolioValue += currentValue;
1433
+ }
1434
+ return {
1435
+ token: alloc.token,
1436
+ symbol,
1437
+ decimals,
1438
+ targetWeightBps: Number(alloc.targetWeightBps),
1439
+ balance: formatUnits(balance, decimals),
1440
+ balanceRaw: balance.toString(),
1441
+ investedAmount: formatUnits(alloc.investedAmount, assetDecimals),
1442
+ currentPrice: price?.price ?? null,
1443
+ currentValue,
1444
+ actualWeightBps: null,
1445
+ // computed after totals
1446
+ driftBps: null,
1447
+ pnl: currentValue !== null ? currentValue - investedNum : null,
1448
+ pnlPct: currentValue !== null && investedNum > 0 ? (currentValue - investedNum) / investedNum * 100 : null
1449
+ };
1450
+ });
1451
+ let maxDriftBps = 0;
1452
+ let maxDriftToken = "";
1453
+ if (pricesAvailable && totalPortfolioValue > 0) {
1454
+ for (const a of allocStatuses) {
1455
+ if (a.currentValue !== null) {
1456
+ a.actualWeightBps = Math.round(a.currentValue / totalPortfolioValue * 1e4);
1457
+ a.driftBps = a.actualWeightBps - a.targetWeightBps;
1458
+ if (Math.abs(a.driftBps) > Math.abs(maxDriftBps)) {
1459
+ maxDriftBps = a.driftBps;
1460
+ maxDriftToken = a.symbol;
1461
+ }
1462
+ }
1463
+ }
1464
+ }
1465
+ const totalInvestedNum = Number(formatUnits(totalInvested, assetDecimals));
1466
+ const totalPnl = pricesAvailable ? totalPortfolioValue - totalInvestedNum : null;
1467
+ const totalPnlPct = totalPnl !== null && totalInvestedNum > 0 ? totalPnl / totalInvestedNum * 100 : null;
1468
+ spinner?.succeed("Strategy state loaded");
1469
+ if (jsonMode) {
1470
+ const result = {
1471
+ clone,
1472
+ vault,
1473
+ proposer,
1474
+ state: stateStr,
1475
+ network,
1476
+ asset: { address: assetAddr, symbol: assetSymbol, decimals: assetDecimals },
1477
+ totalDeployed: formatUnits(totalAmount, assetDecimals),
1478
+ maxSlippageBps: Number(maxSlippage),
1479
+ assetBalance: formatUnits(assetBalance, assetDecimals),
1480
+ pricesAvailable,
1481
+ priceSource,
1482
+ portfolio: {
1483
+ totalValue: pricesAvailable ? totalPortfolioValue : null,
1484
+ totalPnl,
1485
+ totalPnlPct
1486
+ },
1487
+ allocations: allocStatuses,
1488
+ maxDriftBps: pricesAvailable ? maxDriftBps : null,
1489
+ maxDriftToken: pricesAvailable ? maxDriftToken : null,
1490
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1491
+ };
1492
+ process.stdout.write(JSON.stringify(result) + "\n");
1493
+ return;
1494
+ }
1495
+ const stateColor = stateStr === "Executed" ? chalk.green : stateStr === "Settled" ? chalk.blue : chalk.yellow;
1496
+ console.log();
1497
+ console.log(chalk.bold("Portfolio Strategy Status"));
1498
+ console.log(chalk.dim("\u2500".repeat(70)));
1499
+ console.log(` Clone: ${chalk.cyan(clone)}`);
1500
+ console.log(` Vault: ${chalk.cyan(vault)}`);
1501
+ console.log(` Proposer: ${chalk.cyan(proposer)}`);
1502
+ console.log(` State: ${stateColor(stateStr)}`);
1503
+ console.log(` Asset: ${assetSymbol} (${assetAddr})`);
1504
+ console.log(` Deployed: ${formatUnits(totalAmount, assetDecimals)} ${assetSymbol}`);
1505
+ console.log(` Max Slip: ${Number(maxSlippage) / 100}%`);
1506
+ console.log(` Asset held: ${formatUnits(assetBalance, assetDecimals)} ${assetSymbol}`);
1507
+ if (pricesAvailable) {
1508
+ const pvStr = totalPortfolioValue.toFixed(6);
1509
+ const pnlStr = totalPnl !== null ? (totalPnl >= 0 ? "+" : "") + totalPnl.toFixed(6) : "n/a";
1510
+ const pnlPctStr = totalPnlPct !== null ? (totalPnlPct >= 0 ? "+" : "") + totalPnlPct.toFixed(1) + "%" : "";
1511
+ const pnlColor = totalPnl !== null && totalPnl >= 0 ? chalk.green : chalk.red;
1512
+ console.log(` Value: ${pvStr} ${assetSymbol} | PnL: ${pnlColor(`${pnlStr} (${pnlPctStr})`)}`);
1513
+ console.log(` Price src: ${priceSource}`);
1514
+ }
1515
+ console.log();
1516
+ console.log(chalk.bold("Allocations"));
1517
+ console.log(chalk.dim("\u2500".repeat(70)));
1518
+ if (pricesAvailable) {
1519
+ console.log(
1520
+ chalk.dim(" Token".padEnd(10)) + chalk.dim("Target".padEnd(9)) + chalk.dim("Actual".padEnd(9)) + chalk.dim("Drift".padEnd(8)) + chalk.dim("Value".padEnd(16)) + chalk.dim("PnL".padEnd(16)) + chalk.dim("Balance")
1521
+ );
1522
+ for (const a of allocStatuses) {
1523
+ const targetStr = (a.targetWeightBps / 100).toFixed(1) + "%";
1524
+ const actualStr = a.actualWeightBps !== null ? (a.actualWeightBps / 100).toFixed(1) + "%" : "\u2014";
1525
+ const driftStr = a.driftBps !== null ? (a.driftBps >= 0 ? "+" : "") + a.driftBps : "\u2014";
1526
+ const driftColor = a.driftBps !== null ? Math.abs(a.driftBps) > 500 ? chalk.red : Math.abs(a.driftBps) > 200 ? chalk.yellow : chalk.dim : chalk.dim;
1527
+ const valueStr = a.currentValue !== null ? a.currentValue.toFixed(6) : "\u2014";
1528
+ const pnlPctStr = a.pnlPct !== null ? (a.pnlPct >= 0 ? "+" : "") + a.pnlPct.toFixed(1) + "%" : "\u2014";
1529
+ const pnlColor = a.pnlPct !== null ? a.pnlPct >= 0 ? chalk.green : chalk.red : chalk.dim;
1530
+ console.log(
1531
+ ` ${chalk.bold(a.symbol.padEnd(8))}${targetStr.padEnd(9)}${actualStr.padEnd(9)}${driftColor(driftStr.toString().padEnd(8))}${valueStr.padEnd(16)}${pnlColor(pnlPctStr.padEnd(16))}${a.balance}`
1532
+ );
1533
+ }
1534
+ } else {
1535
+ console.log(chalk.dim(" Prices unavailable \u2014 showing raw balances only."));
1536
+ console.log();
1537
+ console.log(
1538
+ chalk.dim(" Token".padEnd(12)) + chalk.dim("Weight".padEnd(10)) + chalk.dim("Invested".padEnd(18)) + chalk.dim("Balance".padEnd(18)) + chalk.dim("Address")
1539
+ );
1540
+ for (const a of allocStatuses) {
1541
+ const weightPct = (a.targetWeightBps / 100).toFixed(1) + "%";
1542
+ console.log(
1543
+ ` ${chalk.bold(a.symbol.padEnd(10))}${weightPct.padEnd(10)}${a.investedAmount.padEnd(18)}${a.balance.padEnd(18)}${chalk.dim(a.token)}`
1544
+ );
1545
+ }
1546
+ }
1547
+ console.log(chalk.dim("\u2500".repeat(70)));
1548
+ console.log(` Total invested: ${formatUnits(totalInvested, assetDecimals)} ${assetSymbol}`);
1549
+ if (pricesAvailable && maxDriftToken) {
1550
+ const absMaxDrift = Math.abs(maxDriftBps);
1551
+ const driftColor = absMaxDrift > 500 ? chalk.red : absMaxDrift > 200 ? chalk.yellow : chalk.green;
1552
+ console.log(` Max drift: ${driftColor(`${maxDriftBps > 0 ? "+" : ""}${maxDriftBps} bps`)} (${maxDriftToken})`);
1553
+ }
1554
+ if (stateStr === "Executed") {
1555
+ console.log();
1556
+ console.log(chalk.dim("Rebalance: sherwood strategy rebalance " + clone));
1557
+ console.log(chalk.dim("With new weights: sherwood strategy rebalance " + clone + " --new-weights 2500,2500,2000,1500,1500"));
1558
+ }
1559
+ console.log();
1560
+ } catch (err) {
1561
+ spinner?.fail("Failed to read strategy state");
1562
+ console.error(chalk.red(formatContractError(err)));
1563
+ process.exit(1);
1564
+ }
1565
+ });
1566
+ strategy2.command("rebalance").description("Rebalance a portfolio strategy \u2014 sell all positions, re-buy at target weights (Portfolio only)").argument("<clone>", "Strategy clone address").option("--new-weights <list>", "Comma-separated new weights in bps (must sum to 10000). Updates weights before rebalancing.").option("--max-slippage <bps>", "New max slippage bps (used with --new-weights)").option("--dry-run", "Show what would happen without executing").action(async (cloneArg, opts) => {
1567
+ if (!isAddress(cloneArg)) {
1568
+ console.error(chalk.red("Invalid clone address"));
1569
+ process.exit(1);
1570
+ }
1571
+ const clone = cloneArg;
1572
+ const publicClient = getPublicClient();
1573
+ const account = getAccount();
1574
+ const chain = getChain();
1575
+ const verifySpinner = ora("Verifying strategy state...").start();
1576
+ try {
1577
+ const [stateRaw, proposer, assetAddr, allocations] = await Promise.all([
1578
+ publicClient.readContract({ address: clone, abi: PORTFOLIO_STRATEGY_ABI, functionName: "state" }),
1579
+ publicClient.readContract({ address: clone, abi: PORTFOLIO_STRATEGY_ABI, functionName: "proposer" }),
1580
+ publicClient.readContract({ address: clone, abi: PORTFOLIO_STRATEGY_ABI, functionName: "asset" }),
1581
+ publicClient.readContract({ address: clone, abi: PORTFOLIO_STRATEGY_ABI, functionName: "getAllocations" })
1582
+ ]);
1583
+ if (Number(stateRaw) !== 1) {
1584
+ const stateNames = ["Pending", "Executed", "Settled"];
1585
+ verifySpinner.fail(`Strategy is ${stateNames[Number(stateRaw)] || "Unknown"}, must be Executed to rebalance`);
1586
+ process.exit(1);
1587
+ }
1588
+ if (proposer.toLowerCase() !== account.address.toLowerCase()) {
1589
+ verifySpinner.fail(`Only the proposer (${proposer}) can rebalance. Your wallet: ${account.address}`);
1590
+ process.exit(1);
1591
+ }
1592
+ const [assetDecimals, assetSymbol] = await Promise.all([
1593
+ publicClient.readContract({ address: assetAddr, abi: erc20Abi, functionName: "decimals" }),
1594
+ publicClient.readContract({ address: assetAddr, abi: erc20Abi, functionName: "symbol" })
1595
+ ]);
1596
+ const tokenReads = allocations.map((a) => Promise.all([
1597
+ publicClient.readContract({ address: a.token, abi: erc20Abi, functionName: "balanceOf", args: [clone] }),
1598
+ publicClient.readContract({ address: a.token, abi: erc20Abi, functionName: "symbol" }),
1599
+ publicClient.readContract({ address: a.token, abi: erc20Abi, functionName: "decimals" })
1600
+ ]));
1601
+ const tokenData = await Promise.all(tokenReads);
1602
+ verifySpinner.succeed("Strategy is Executed \u2014 ready to rebalance");
1603
+ console.log();
1604
+ console.log(chalk.bold("Current Allocations"));
1605
+ console.log(chalk.dim("\u2500".repeat(50)));
1606
+ for (let i = 0; i < allocations.length; i++) {
1607
+ const alloc = allocations[i];
1608
+ const [balance, symbol, decimals] = tokenData[i];
1609
+ const weightPct = (Number(alloc.targetWeightBps) / 100).toFixed(1) + "%";
1610
+ console.log(
1611
+ ` ${chalk.bold(symbol.padEnd(8))} ${weightPct.padEnd(8)} bal: ${formatUnits(balance, decimals)}`
1612
+ );
1613
+ }
1614
+ let newWeightsBps;
1615
+ if (opts.newWeights) {
1616
+ newWeightsBps = opts.newWeights.split(",").map((w) => Number(w.trim()));
1617
+ if (newWeightsBps.length !== allocations.length) {
1618
+ console.error(chalk.red(`--new-weights must have ${allocations.length} values (one per token)`));
1619
+ process.exit(1);
1620
+ }
1621
+ const weightSum = newWeightsBps.reduce((a, b) => a + b, 0);
1622
+ if (weightSum !== 1e4) {
1623
+ console.error(chalk.red(`Weights must sum to 10000, got ${weightSum}`));
1624
+ process.exit(1);
1625
+ }
1626
+ console.log();
1627
+ console.log(chalk.bold("New Target Weights"));
1628
+ console.log(chalk.dim("\u2500".repeat(50)));
1629
+ for (let i = 0; i < allocations.length; i++) {
1630
+ const [, symbol] = tokenData[i];
1631
+ const oldPct = (Number(allocations[i].targetWeightBps) / 100).toFixed(1);
1632
+ const newPct = (newWeightsBps[i] / 100).toFixed(1);
1633
+ const arrow = oldPct !== newPct ? chalk.yellow("\u2192") : chalk.dim("\u2192");
1634
+ console.log(` ${chalk.bold(symbol.padEnd(8))} ${oldPct}% ${arrow} ${newPct}%`);
1635
+ }
1636
+ }
1637
+ if (opts.dryRun) {
1638
+ console.log();
1639
+ console.log(chalk.yellow("Dry run \u2014 no transactions sent."));
1640
+ console.log(chalk.dim("The rebalance would:"));
1641
+ console.log(chalk.dim(" 1. Sell all current token positions back to " + assetSymbol));
1642
+ console.log(chalk.dim(" 2. Re-buy at " + (newWeightsBps ? "new" : "current") + " target weights"));
1643
+ console.log();
1644
+ return;
1645
+ }
1646
+ if (newWeightsBps) {
1647
+ const maxSlip = Number(opts.maxSlippage || "0");
1648
+ const swapData = await publicClient.readContract({
1649
+ address: clone,
1650
+ abi: PORTFOLIO_STRATEGY_ABI,
1651
+ functionName: "getSwapExtraData"
1652
+ });
1653
+ const updateSpinner = ora("Updating target weights...").start();
1654
+ try {
1655
+ const innerData = encodeAbiParameters7(
1656
+ [{ type: "uint256[]" }, { type: "uint256" }, { type: "bytes[]" }],
1657
+ [newWeightsBps.map((w) => BigInt(w)), BigInt(maxSlip), swapData]
1658
+ );
1659
+ const updateHash = await writeContractWithRetry({
1660
+ account,
1661
+ chain,
1662
+ address: clone,
1663
+ abi: PORTFOLIO_STRATEGY_ABI,
1664
+ functionName: "updateParams",
1665
+ args: [innerData]
1666
+ });
1667
+ const receipt = await waitForReceipt(updateHash);
1668
+ if (receipt.status === "reverted") throw new Error("updateParams reverted");
1669
+ updateSpinner.succeed("Target weights updated");
1670
+ console.log(chalk.dim(` Tx: ${getExplorerUrl(updateHash)}`));
1671
+ } catch (err) {
1672
+ updateSpinner.fail("Failed to update weights");
1673
+ console.error(chalk.red(formatContractError(err)));
1674
+ process.exit(1);
1675
+ }
1676
+ }
1677
+ const rebalanceSpinner = ora("Rebalancing portfolio (sell all \u2192 re-buy at targets)...").start();
1678
+ try {
1679
+ const rebalanceHash = await writeContractWithRetry({
1680
+ account,
1681
+ chain,
1682
+ address: clone,
1683
+ abi: PORTFOLIO_STRATEGY_ABI,
1684
+ functionName: "rebalance"
1685
+ });
1686
+ const receipt = await waitForReceipt(rebalanceHash);
1687
+ if (receipt.status === "reverted") throw new Error("rebalance() reverted on-chain");
1688
+ rebalanceSpinner.succeed("Portfolio rebalanced");
1689
+ console.log(chalk.dim(` Tx: ${getExplorerUrl(rebalanceHash)}`));
1690
+ } catch (err) {
1691
+ rebalanceSpinner.fail("Rebalance failed");
1692
+ console.error(chalk.red(formatContractError(err)));
1693
+ process.exit(1);
1694
+ }
1695
+ const postSpinner = ora("Reading updated allocations...").start();
1696
+ const postAllocations = await publicClient.readContract({
1697
+ address: clone,
1698
+ abi: PORTFOLIO_STRATEGY_ABI,
1699
+ functionName: "getAllocations"
1700
+ });
1701
+ const postReads = postAllocations.map((a) => Promise.all([
1702
+ publicClient.readContract({ address: a.token, abi: erc20Abi, functionName: "balanceOf", args: [clone] }),
1703
+ publicClient.readContract({ address: a.token, abi: erc20Abi, functionName: "symbol" }),
1704
+ publicClient.readContract({ address: a.token, abi: erc20Abi, functionName: "decimals" })
1705
+ ]));
1706
+ const postData = await Promise.all(postReads);
1707
+ postSpinner.succeed("Updated allocations");
1708
+ console.log();
1709
+ console.log(chalk.bold("Post-Rebalance Allocations"));
1710
+ console.log(chalk.dim("\u2500".repeat(50)));
1711
+ for (let i = 0; i < postAllocations.length; i++) {
1712
+ const alloc = postAllocations[i];
1713
+ const [balance, symbol, decimals] = postData[i];
1714
+ const weightPct = (Number(alloc.targetWeightBps) / 100).toFixed(1) + "%";
1715
+ const investedStr = formatUnits(alloc.investedAmount, assetDecimals);
1716
+ console.log(
1717
+ ` ${chalk.bold(symbol.padEnd(8))} ${weightPct.padEnd(8)} invested: ${investedStr.padEnd(14)} bal: ${formatUnits(balance, decimals)}`
1718
+ );
1719
+ }
1720
+ console.log();
1721
+ console.log(chalk.green("\u2713 Rebalance complete"));
1722
+ console.log();
1723
+ } catch (err) {
1724
+ if (err.code === "ERR_MODULE_NOT_FOUND" || err instanceof TypeError) throw err;
1725
+ verifySpinner.fail("Pre-flight check failed");
1726
+ console.error(chalk.red(formatContractError(err)));
1727
+ process.exit(1);
1728
+ }
1729
+ });
1066
1730
  }
1067
1731
 
1068
1732
  // src/lib/factory.ts
@@ -1250,7 +1914,7 @@ async function getActiveSyndicates2(creator) {
1250
1914
  }
1251
1915
 
1252
1916
  // src/commands/venice.ts
1253
- import { formatUnits as formatUnits2, isAddress as isAddress2 } from "viem";
1917
+ import { formatUnits as formatUnits3, isAddress as isAddress2 } from "viem";
1254
1918
  import chalk2 from "chalk";
1255
1919
  import ora2 from "ora";
1256
1920
  import { readFileSync } from "fs";
@@ -1273,7 +1937,7 @@ function registerVeniceCommands(program2) {
1273
1937
  console.log(chalk2.yellow(" Use the VeniceInferenceStrategy via a proposal to distribute sVVV to agents."));
1274
1938
  process.exit(1);
1275
1939
  }
1276
- checkSpinner.succeed(`sVVV balance: ${formatUnits2(sVvvBalance, 18)}`);
1940
+ checkSpinner.succeed(`sVVV balance: ${formatUnits3(sVvvBalance, 18)}`);
1277
1941
  } catch (err) {
1278
1942
  checkSpinner.fail("Failed to check sVVV balance");
1279
1943
  console.error(chalk2.red(formatContractError(err)));
@@ -1286,7 +1950,7 @@ function registerVeniceCommands(program2) {
1286
1950
  console.log(chalk2.dim(` Key: ${apiKey.slice(0, 8)}...${apiKey.slice(-4)}`));
1287
1951
  console.log(chalk2.dim(" Saved to ~/.sherwood/config.json"));
1288
1952
  try {
1289
- const { createVeniceProvisionAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-X5UIEM6D.js");
1953
+ const { createVeniceProvisionAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-YZIHYQAI.js");
1290
1954
  const { uid } = await createVeniceProvisionAttestation(account.address);
1291
1955
  if (uid !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
1292
1956
  console.log(chalk2.dim(` Attested: ${getEasScanUrl2(uid)}`));
@@ -1358,17 +2022,17 @@ function registerVeniceCommands(program2) {
1358
2022
  console.log(chalk2.bold("Venice Inference Status"));
1359
2023
  console.log(chalk2.dim("\u2500".repeat(50)));
1360
2024
  console.log(chalk2.bold("\n Vault"));
1361
- console.log(` Profit available: ${formatUnits2(profit, assetDecimals)} ${assetSymbol}`);
1362
- console.log(` VVV (unstaked): ${formatUnits2(vaultVvvBalance, 18)}`);
2025
+ console.log(` Profit available: ${formatUnits3(profit, assetDecimals)} ${assetSymbol}`);
2026
+ console.log(` VVV (unstaked): ${formatUnits3(vaultVvvBalance, 18)}`);
1363
2027
  console.log(chalk2.bold("\n Agent sVVV Balances"));
1364
2028
  for (const { agent, balance } of agentBalances) {
1365
2029
  const isMe = agent.toLowerCase() === account.address.toLowerCase();
1366
2030
  const label = isMe ? chalk2.green(`${agent} (you)`) : agent;
1367
- console.log(` ${label}: ${formatUnits2(balance, 18)} sVVV`);
2031
+ console.log(` ${label}: ${formatUnits3(balance, 18)} sVVV`);
1368
2032
  }
1369
2033
  console.log(chalk2.bold("\n Your Wallet"));
1370
- console.log(` sVVV: ${formatUnits2(mySvvv, 18)}`);
1371
- console.log(` Pending rewards: ${formatUnits2(myPending, 18)} VVV`);
2034
+ console.log(` sVVV: ${formatUnits3(mySvvv, 18)}`);
2035
+ console.log(` Pending rewards: ${formatUnits3(myPending, 18)} VVV`);
1372
2036
  console.log(chalk2.bold("\n Venice API"));
1373
2037
  console.log(` Key: ${apiKey ? `${apiKey.slice(0, 8)}...${apiKey.slice(-4)}` : chalk2.dim("not provisioned")}`);
1374
2038
  console.log(` Status: ${apiKeyValid ? chalk2.green("valid") : chalk2.red("invalid/missing")}`);
@@ -1437,10 +2101,10 @@ ${opts.prompt}`;
1437
2101
  console.log(chalk2.dim(`Model: ${result.model} | Tokens: ${result.usage.promptTokens} in, ${result.usage.completionTokens} out, ${result.usage.totalTokens} total`));
1438
2102
  }
1439
2103
  try {
1440
- const { createVeniceInferenceAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-X5UIEM6D.js");
2104
+ const { createVeniceInferenceAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-YZIHYQAI.js");
1441
2105
  const { keccak256, toHex, isAddress: isAddr } = await import("viem");
1442
- const { getChainContracts: getChainContracts2 } = await import("./config-BGS5MX4D.js");
1443
- const { getChain: getActiveChain } = await import("./network-UTSOP7EF.js");
2106
+ const { getChainContracts: getChainContracts2 } = await import("./config-YXZKPSZB.js");
2107
+ const { getChain: getActiveChain } = await import("./network-QEMP3DSP.js");
1444
2108
  const vaultRecipient = opts.vault && isAddr(opts.vault) ? opts.vault : getChainContracts2(getActiveChain().id).vault;
1445
2109
  const promptHash = keccak256(toHex(userContent)).slice(0, 18);
1446
2110
  const { uid } = await createVeniceInferenceAttestation(
@@ -1464,12 +2128,12 @@ ${opts.prompt}`;
1464
2128
  }
1465
2129
 
1466
2130
  // src/commands/allowance.ts
1467
- import { parseUnits as parseUnits4, formatUnits as formatUnits3, isAddress as isAddress3 } from "viem";
2131
+ import { parseUnits as parseUnits4, formatUnits as formatUnits4, isAddress as isAddress3 } from "viem";
1468
2132
  import chalk3 from "chalk";
1469
2133
  import ora3 from "ora";
1470
2134
 
1471
2135
  // src/strategies/allowance-disburse.ts
1472
- import { encodeFunctionData as encodeFunctionData6, parseUnits as parseUnits3 } from "viem";
2136
+ import { encodeFunctionData as encodeFunctionData7, parseUnits as parseUnits3 } from "viem";
1473
2137
  function buildDisburseBatch(config, vaultAddress, agents, assetAddress, assetDecimals, minUsdc, swapPath) {
1474
2138
  const assetAmount = parseUnits3(config.amount, assetDecimals);
1475
2139
  const isUsdc = assetAddress.toLowerCase() === TOKENS().USDC.toLowerCase();
@@ -1478,7 +2142,7 @@ function buildDisburseBatch(config, vaultAddress, agents, assetAddress, assetDec
1478
2142
  if (!isUsdc) {
1479
2143
  calls.push({
1480
2144
  target: assetAddress,
1481
- data: encodeFunctionData6({
2145
+ data: encodeFunctionData7({
1482
2146
  abi: ERC20_ABI,
1483
2147
  functionName: "approve",
1484
2148
  args: [UNISWAP().SWAP_ROUTER, assetAmount]
@@ -1488,7 +2152,7 @@ function buildDisburseBatch(config, vaultAddress, agents, assetAddress, assetDec
1488
2152
  if (isWeth) {
1489
2153
  calls.push({
1490
2154
  target: UNISWAP().SWAP_ROUTER,
1491
- data: encodeFunctionData6({
2155
+ data: encodeFunctionData7({
1492
2156
  abi: SWAP_ROUTER_EXACT_INPUT_SINGLE_ABI,
1493
2157
  functionName: "exactInputSingle",
1494
2158
  args: [
@@ -1508,7 +2172,7 @@ function buildDisburseBatch(config, vaultAddress, agents, assetAddress, assetDec
1508
2172
  } else {
1509
2173
  calls.push({
1510
2174
  target: UNISWAP().SWAP_ROUTER,
1511
- data: encodeFunctionData6({
2175
+ data: encodeFunctionData7({
1512
2176
  abi: SWAP_ROUTER_ABI,
1513
2177
  functionName: "exactInput",
1514
2178
  args: [
@@ -1528,7 +2192,7 @@ function buildDisburseBatch(config, vaultAddress, agents, assetAddress, assetDec
1528
2192
  for (const agent of agents) {
1529
2193
  calls.push({
1530
2194
  target: TOKENS().USDC,
1531
- data: encodeFunctionData6({
2195
+ data: encodeFunctionData7({
1532
2196
  abi: ERC20_ABI,
1533
2197
  functionName: "transfer",
1534
2198
  args: [agent, perAgent]
@@ -1593,9 +2257,9 @@ function registerAllowanceCommands(program2) {
1593
2257
  console.log(chalk3.dim("\u2500".repeat(40)));
1594
2258
  console.log(` Asset: ${assetSymbol} (${assetDecimals} decimals)`);
1595
2259
  console.log(` Amount: ${opts.amount} ${assetSymbol}`);
1596
- console.log(` Vault balance: ${formatUnits3(assetBalance, assetDecimals)} ${assetSymbol}`);
1597
- console.log(` Deposited: ${formatUnits3(totalDeposited, assetDecimals)} ${assetSymbol}`);
1598
- console.log(` Profit: ${formatUnits3(profit, assetDecimals)} ${assetSymbol}`);
2260
+ console.log(` Vault balance: ${formatUnits4(assetBalance, assetDecimals)} ${assetSymbol}`);
2261
+ console.log(` Deposited: ${formatUnits4(totalDeposited, assetDecimals)} ${assetSymbol}`);
2262
+ console.log(` Profit: ${formatUnits4(profit, assetDecimals)} ${assetSymbol}`);
1599
2263
  console.log(` Agents: ${agents.length} (USDC will be split equally)`);
1600
2264
  if (!isUsdc) {
1601
2265
  console.log(` Routing: ${isWeth ? `WETH \u2192 USDC (fee ${fee})` : `${assetSymbol} \u2192 WETH \u2192 USDC (fee ${fee})`}`);
@@ -1604,7 +2268,7 @@ function registerAllowanceCommands(program2) {
1604
2268
  console.log(` Vault: ${vaultAddress}`);
1605
2269
  console.log();
1606
2270
  if (requestedAmount > profit) {
1607
- console.warn(chalk3.yellow(` Warning: amount (${opts.amount}) exceeds available profit (${formatUnits3(profit, assetDecimals)})`));
2271
+ console.warn(chalk3.yellow(` Warning: amount (${opts.amount}) exceeds available profit (${formatUnits4(profit, assetDecimals)})`));
1608
2272
  console.warn(chalk3.yellow(" This will use deposited capital, not just profits."));
1609
2273
  console.log();
1610
2274
  }
@@ -1638,7 +2302,7 @@ function registerAllowanceCommands(program2) {
1638
2302
  }
1639
2303
  minUsdc = applySlippage(amountOut, slippageBps);
1640
2304
  quoteSpinner.succeed(
1641
- `Quote: ${formatUnits3(amountOut, 6)} USDC (min: ${formatUnits3(minUsdc, 6)}, per agent: ${formatUnits3(minUsdc / BigInt(agents.length), 6)})`
2305
+ `Quote: ${formatUnits4(amountOut, 6)} USDC (min: ${formatUnits4(minUsdc, 6)}, per agent: ${formatUnits4(minUsdc / BigInt(agents.length), 6)})`
1642
2306
  );
1643
2307
  } catch (err) {
1644
2308
  quoteSpinner.fail("Failed to fetch quote");
@@ -1648,7 +2312,7 @@ function registerAllowanceCommands(program2) {
1648
2312
  }
1649
2313
  const perAgent = minUsdc / BigInt(agents.length);
1650
2314
  if (isUsdc) {
1651
- console.log(chalk3.dim(` Per agent: ${formatUnits3(perAgent, 6)} USDC`));
2315
+ console.log(chalk3.dim(` Per agent: ${formatUnits4(perAgent, 6)} USDC`));
1652
2316
  console.log();
1653
2317
  }
1654
2318
  const config = {
@@ -1715,14 +2379,14 @@ function registerAllowanceCommands(program2) {
1715
2379
  console.log(chalk3.dim("\u2500".repeat(50)));
1716
2380
  console.log(chalk3.bold("\n Vault"));
1717
2381
  console.log(` Asset: ${assetSymbol}`);
1718
- console.log(` Balance: ${formatUnits3(assetBalance, assetDecimals)} ${assetSymbol}`);
1719
- console.log(` Deposited: ${formatUnits3(totalDeposited, assetDecimals)} ${assetSymbol}`);
1720
- console.log(` Profit: ${formatUnits3(profit, assetDecimals)} ${assetSymbol}`);
2382
+ console.log(` Balance: ${formatUnits4(assetBalance, assetDecimals)} ${assetSymbol}`);
2383
+ console.log(` Deposited: ${formatUnits4(totalDeposited, assetDecimals)} ${assetSymbol}`);
2384
+ console.log(` Profit: ${formatUnits4(profit, assetDecimals)} ${assetSymbol}`);
1721
2385
  console.log(chalk3.bold("\n Agent USDC Balances"));
1722
2386
  for (const { agent, balance } of agentBalances) {
1723
2387
  const isMe = agent.toLowerCase() === account.address.toLowerCase();
1724
2388
  const label = isMe ? chalk3.green(`${agent} (you)`) : agent;
1725
- console.log(` ${label}: ${formatUnits3(balance, 6)} USDC`);
2389
+ console.log(` ${label}: ${formatUnits4(balance, 6)} USDC`);
1726
2390
  }
1727
2391
  console.log();
1728
2392
  } catch (err) {
@@ -1927,7 +2591,7 @@ function parseBigIntArg(value, name) {
1927
2591
  }
1928
2592
 
1929
2593
  // src/lib/simulate.ts
1930
- import { decodeFunctionData, encodeFunctionData as encodeFunctionData7 } from "viem";
2594
+ import { decodeFunctionData, encodeFunctionData as encodeFunctionData8 } from "viem";
1931
2595
  import chalk5 from "chalk";
1932
2596
  var G = chalk5.green;
1933
2597
  var W = chalk5.white;
@@ -2446,7 +3110,7 @@ async function simulateViaApi(vault, calls) {
2446
3110
  async function simulateViaEthCall(vault, calls) {
2447
3111
  const governor = getGovernorAddress();
2448
3112
  const client = getPublicClient();
2449
- const input2 = encodeFunctionData7({
3113
+ const input2 = encodeFunctionData8({
2450
3114
  abi: SYNDICATE_VAULT_ABI,
2451
3115
  functionName: "executeGovernorBatch",
2452
3116
  args: [calls.map((c) => ({ target: c.target, data: c.data, value: c.value }))]
@@ -2569,8 +3233,8 @@ function printSimulationResult(result, callType) {
2569
3233
  }
2570
3234
  async function sendSimulationAlert(subdomain, proposalId, vault, execResult, settleResult) {
2571
3235
  try {
2572
- const xmtp = await import("./xmtp-XKHXATPQ.js");
2573
- const { getAccount: getAccount2 } = await import("./client-SCIY6SUP.js");
3236
+ const xmtp = await import("./xmtp-YNOGIZ4G.js");
3237
+ const { getAccount: getAccount2 } = await import("./client-CWQFYZ5Z.js");
2574
3238
  const group = await xmtp.getGroup("", subdomain);
2575
3239
  const allRisks = [...execResult.risks, ...settleResult?.risks ?? []];
2576
3240
  const criticals = allRisks.filter((r) => r.level === "critical");
@@ -3262,7 +3926,7 @@ try {
3262
3926
  var require2 = createRequire(import.meta.url);
3263
3927
  var { version: CLI_VERSION } = require2("../package.json");
3264
3928
  async function loadXmtp() {
3265
- return import("./xmtp-XKHXATPQ.js");
3929
+ return import("./xmtp-YNOGIZ4G.js");
3266
3930
  }
3267
3931
  async function loadCron() {
3268
3932
  return import("./cron-EOULYO3R.js");
@@ -3508,8 +4172,11 @@ syndicate.command("create").description("Create a new syndicate via the factory
3508
4172
  const xmtp = await loadXmtp();
3509
4173
  const xmtpClient = await xmtp.getXmtpClient();
3510
4174
  const groupId = await xmtp.createSyndicateGroup(xmtpClient, subdomain, opts.publicChat);
3511
- await setTextRecord(subdomain, "xmtpGroupId", groupId, result.vault);
3512
4175
  cacheGroupId(subdomain, groupId);
4176
+ try {
4177
+ await setTextRecord(subdomain, "xmtpGroupId", groupId, result.vault);
4178
+ } catch {
4179
+ }
3513
4180
  } catch {
3514
4181
  console.warn(chalk8.yellow("\n \u26A0 Could not create XMTP chat group"));
3515
4182
  console.warn(chalk8.dim(` Recover later with: sherwood chat ${subdomain} init`));
@@ -4222,7 +4889,7 @@ vaultCmd.command("balance").description("Show LP share balance and asset value")
4222
4889
  var strategy = program.command("strategy").description("Strategy templates \u2014 list, clone, propose");
4223
4890
  registerStrategyTemplateCommands(strategy);
4224
4891
  program.command("providers").description("List available DeFi providers").action(async () => {
4225
- const { MessariProvider, NansenProvider } = await import("./research-W3M3VZBD.js");
4892
+ const { MessariProvider, NansenProvider } = await import("./research-ZX7T2ZIJ.js");
4226
4893
  const providers = [new MoonwellProvider(), new UniswapProvider(), new MessariProvider(), new NansenProvider()];
4227
4894
  for (const p of providers) {
4228
4895
  const info = p.info();
@@ -4233,7 +4900,7 @@ ${info.name} (${info.type})`);
4233
4900
  }
4234
4901
  });
4235
4902
  try {
4236
- const { registerChatCommands } = await import("./chat-JYAJMP4D.js");
4903
+ const { registerChatCommands } = await import("./chat-52PWNH7Y.js");
4237
4904
  registerChatCommands(program);
4238
4905
  } catch {
4239
4906
  program.command("chat <name> [action] [actionArgs...]").description("Syndicate chat (XMTP) \u2014 requires @xmtp/cli").action(() => {
@@ -4243,16 +4910,16 @@ try {
4243
4910
  process.exit(1);
4244
4911
  });
4245
4912
  }
4246
- var { registerSessionCommands } = await import("./session-XZNKLK3I.js");
4913
+ var { registerSessionCommands } = await import("./session-NMA4RLKC.js");
4247
4914
  registerSessionCommands(program);
4248
4915
  registerVeniceCommands(program);
4249
4916
  registerAllowanceCommands(program);
4250
4917
  registerIdentityCommands(program);
4251
4918
  registerProposalCommands(program);
4252
4919
  registerGovernorCommands(program);
4253
- var { registerResearchCommands } = await import("./research-EWH43HFN.js");
4920
+ var { registerResearchCommands } = await import("./research-PH77VSE4.js");
4254
4921
  registerResearchCommands(program);
4255
- var { registerTradeCommands } = await import("./trade-VKSXEHWW.js");
4922
+ var { registerTradeCommands } = await import("./trade-N2A74UPX.js");
4256
4923
  registerTradeCommands(program);
4257
4924
  var configCmd = program.command("config");
4258
4925
  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.)").option("--uniswap-api-key <key>", "Uniswap Trading API key (from developers.uniswap.org)").option("--venice-api-key <key>", "Venice AI inference API key").action((opts) => {