@sherwoodagent/cli 0.86.0 → 0.87.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/{abis-WVW2EBI3.js → abis-WJQ6IVYV.js} +2 -2
  2. package/dist/{agent-TBLC3WNJ.js → agent-A2JEYGQS.js} +7 -7
  3. package/dist/{chat-PRYOT3ZV.js → chat-OZAAN5LT.js} +8 -8
  4. package/dist/{chunk-I2BBOJNC.js → chunk-37GT3TJX.js} +5 -5
  5. package/dist/{chunk-45FELUWY.js → chunk-3UYGXGMF.js} +2 -2
  6. package/dist/{chunk-6N7EVSAZ.js → chunk-CU42ZD4K.js} +22 -9
  7. package/dist/chunk-CU42ZD4K.js.map +1 -0
  8. package/dist/{chunk-PVTZMRC6.js → chunk-G3FUEM5N.js} +2 -2
  9. package/dist/chunk-G3FUEM5N.js.map +1 -0
  10. package/dist/{chunk-5NFAEOFG.js → chunk-GR3GSU3I.js} +6 -6
  11. package/dist/{chunk-KVB5P2OE.js → chunk-IINWI3LD.js} +2 -2
  12. package/dist/{chunk-F3MDCUGO.js → chunk-J2IZWAEW.js} +75 -227
  13. package/dist/chunk-J2IZWAEW.js.map +1 -0
  14. package/dist/{chunk-ARSQ5UQA.js → chunk-JGD6A2Q4.js} +29 -82
  15. package/dist/chunk-JGD6A2Q4.js.map +1 -0
  16. package/dist/{chunk-QRMZRAMG.js → chunk-MIAMGVZ4.js} +8 -8
  17. package/dist/chunk-MIAMGVZ4.js.map +1 -0
  18. package/dist/{chunk-HODME7UJ.js → chunk-R6F57QQF.js} +14 -23
  19. package/dist/chunk-R6F57QQF.js.map +1 -0
  20. package/dist/{chunk-53W6CYYV.js → chunk-TTF5TDRV.js} +5 -5
  21. package/dist/{chunk-AM6MI2JF.js → chunk-UOIAY6ZG.js} +148 -58
  22. package/dist/chunk-UOIAY6ZG.js.map +1 -0
  23. package/dist/{chunk-LRUSZUEU.js → chunk-V7TNYHIK.js} +337 -490
  24. package/dist/chunk-V7TNYHIK.js.map +1 -0
  25. package/dist/{chunk-M4GX5PL2.js → chunk-W3XWRWML.js} +5 -5
  26. package/dist/{client-3SE632Q2.js → client-C6N2ASTH.js} +3 -3
  27. package/dist/{eas-D3BVHBJH.js → eas-VFTWEI4K.js} +6 -6
  28. package/dist/{governor-35IXAEDJ.js → governor-HVBSI6Z2.js} +10 -8
  29. package/dist/identity-OLD25QEP.js +18 -0
  30. package/dist/index.js +168 -316
  31. package/dist/index.js.map +1 -1
  32. package/dist/{network-FEHRFDPJ.js → network-7445KSCF.js} +2 -2
  33. package/dist/{price-I5T77X7Y.js → price-O3THX2HL.js} +6 -6
  34. package/dist/{providers-BMOHHVUN.js → providers-KZNCCUKT.js} +4 -4
  35. package/dist/{research-NKGP3M4J.js → research-ARFBFXIT.js} +7 -7
  36. package/dist/{session-LJT3SAED.js → session-A7CYZSSA.js} +14 -12
  37. package/dist/session-A7CYZSSA.js.map +1 -0
  38. package/dist/{trade-OTZ7UHDK.js → trade-ZCK332E4.js} +15 -15
  39. package/dist/{vault-7O4N6WZG.js → vault-CDI7BELZ.js} +8 -8
  40. package/dist/{xmtp-R25Q5GWY.js → xmtp-7LAWDKJT.js} +6 -6
  41. package/package.json +1 -1
  42. package/dist/chunk-6N7EVSAZ.js.map +0 -1
  43. package/dist/chunk-AM6MI2JF.js.map +0 -1
  44. package/dist/chunk-ARSQ5UQA.js.map +0 -1
  45. package/dist/chunk-F3MDCUGO.js.map +0 -1
  46. package/dist/chunk-HODME7UJ.js.map +0 -1
  47. package/dist/chunk-LRUSZUEU.js.map +0 -1
  48. package/dist/chunk-PVTZMRC6.js.map +0 -1
  49. package/dist/chunk-QRMZRAMG.js.map +0 -1
  50. package/dist/identity-XOAYQFUW.js +0 -15
  51. package/dist/session-LJT3SAED.js.map +0 -1
  52. /package/dist/{abis-WVW2EBI3.js.map → abis-WJQ6IVYV.js.map} +0 -0
  53. /package/dist/{agent-TBLC3WNJ.js.map → agent-A2JEYGQS.js.map} +0 -0
  54. /package/dist/{chat-PRYOT3ZV.js.map → chat-OZAAN5LT.js.map} +0 -0
  55. /package/dist/{chunk-I2BBOJNC.js.map → chunk-37GT3TJX.js.map} +0 -0
  56. /package/dist/{chunk-45FELUWY.js.map → chunk-3UYGXGMF.js.map} +0 -0
  57. /package/dist/{chunk-5NFAEOFG.js.map → chunk-GR3GSU3I.js.map} +0 -0
  58. /package/dist/{chunk-KVB5P2OE.js.map → chunk-IINWI3LD.js.map} +0 -0
  59. /package/dist/{chunk-53W6CYYV.js.map → chunk-TTF5TDRV.js.map} +0 -0
  60. /package/dist/{chunk-M4GX5PL2.js.map → chunk-W3XWRWML.js.map} +0 -0
  61. /package/dist/{client-3SE632Q2.js.map → client-C6N2ASTH.js.map} +0 -0
  62. /package/dist/{eas-D3BVHBJH.js.map → eas-VFTWEI4K.js.map} +0 -0
  63. /package/dist/{governor-35IXAEDJ.js.map → governor-HVBSI6Z2.js.map} +0 -0
  64. /package/dist/{identity-XOAYQFUW.js.map → identity-OLD25QEP.js.map} +0 -0
  65. /package/dist/{network-FEHRFDPJ.js.map → network-7445KSCF.js.map} +0 -0
  66. /package/dist/{price-I5T77X7Y.js.map → price-O3THX2HL.js.map} +0 -0
  67. /package/dist/{providers-BMOHHVUN.js.map → providers-KZNCCUKT.js.map} +0 -0
  68. /package/dist/{research-NKGP3M4J.js.map → research-ARFBFXIT.js.map} +0 -0
  69. /package/dist/{trade-OTZ7UHDK.js.map → trade-ZCK332E4.js.map} +0 -0
  70. /package/dist/{vault-7O4N6WZG.js.map → vault-CDI7BELZ.js.map} +0 -0
  71. /package/dist/{xmtp-R25Q5GWY.js.map → xmtp-7LAWDKJT.js.map} +0 -0
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  checkApiKeyValid,
16
16
  listModels,
17
17
  provisionApiKey
18
- } from "./chunk-KVB5P2OE.js";
18
+ } from "./chunk-IINWI3LD.js";
19
19
  import {
20
20
  DEFAULT_FEE_TIER,
21
21
  HYPERLIQUID_PERP_STRATEGY_ABI,
@@ -31,7 +31,7 @@ import {
31
31
  parseAlignedOverrides,
32
32
  resolveToken,
33
33
  selectBestFeeTier
34
- } from "./chunk-ARSQ5UQA.js";
34
+ } from "./chunk-JGD6A2Q4.js";
35
35
  import {
36
36
  DEFAULT_ISSUING_CHAIN_ID,
37
37
  appendVidTag,
@@ -47,9 +47,9 @@ import {
47
47
  encodeGuardianStake,
48
48
  encodeGuardianUnstake,
49
49
  encodeJoinRequest,
50
+ encodeMgmtSplit,
51
+ encodePerfSplit,
50
52
  encodePropose,
51
- encodeProposeWithSandbox,
52
- encodeProtocolFeeBps,
53
53
  encodeRedeem,
54
54
  encodeRegisterAgent,
55
55
  encodeSettle,
@@ -64,7 +64,7 @@ import {
64
64
  setCalldataOnly,
65
65
  stripVidTag,
66
66
  verifyVidTag
67
- } from "./chunk-LRUSZUEU.js";
67
+ } from "./chunk-V7TNYHIK.js";
68
68
  import {
69
69
  createApproval,
70
70
  createJoinRequest,
@@ -74,20 +74,20 @@ import {
74
74
  queryJoinRequests,
75
75
  revokeJoinRequest,
76
76
  stripReferrerPrefix
77
- } from "./chunk-M4GX5PL2.js";
77
+ } from "./chunk-W3XWRWML.js";
78
78
  import {
79
79
  createFund,
80
80
  getActiveFunds,
81
81
  getFund,
82
82
  subdomainExists,
83
83
  updateMetadata
84
- } from "./chunk-53W6CYYV.js";
84
+ } from "./chunk-TTF5TDRV.js";
85
85
  import {
86
86
  resolveFund,
87
87
  resolveVaultFund,
88
88
  searchFundAcrossChains,
89
89
  setTextRecord
90
- } from "./chunk-I2BBOJNC.js";
90
+ } from "./chunk-37GT3TJX.js";
91
91
  import {
92
92
  approveDepositor,
93
93
  deposit,
@@ -110,8 +110,8 @@ import {
110
110
  removeDepositor,
111
111
  setAgentFeeBps,
112
112
  setVaultAddress
113
- } from "./chunk-5NFAEOFG.js";
114
- import "./chunk-HODME7UJ.js";
113
+ } from "./chunk-GR3GSU3I.js";
114
+ import "./chunk-R6F57QQF.js";
115
115
  import {
116
116
  PROPOSAL_STATE,
117
117
  PROPOSAL_STATES,
@@ -136,7 +136,6 @@ import {
136
136
  getVoteWeight,
137
137
  hasVoted,
138
138
  parseDuration,
139
- parseProposalIdFromLogs,
140
139
  proposalCount,
141
140
  propose,
142
141
  scanReviewCandidates,
@@ -144,12 +143,13 @@ import {
144
143
  setExecutionWindow,
145
144
  setMaxPerformanceFeeBps,
146
145
  setMaxStrategyDuration,
147
- setProtocolFeeBps,
146
+ setMgmtSplit,
147
+ setPerfSplit,
148
148
  setVetoThresholdBps,
149
149
  setVotingPeriod,
150
150
  settleProposal,
151
151
  vote
152
- } from "./chunk-6N7EVSAZ.js";
152
+ } from "./chunk-CU42ZD4K.js";
153
153
  import {
154
154
  AERODROME,
155
155
  MOONWELL,
@@ -158,7 +158,7 @@ import {
158
158
  TOKENS,
159
159
  UNISWAP,
160
160
  VENICE
161
- } from "./chunk-AM6MI2JF.js";
161
+ } from "./chunk-UOIAY6ZG.js";
162
162
  import {
163
163
  GAS_UNITS,
164
164
  estimateGasCushion,
@@ -169,7 +169,7 @@ import {
169
169
  sendTxWithRetry,
170
170
  waitForReceipt,
171
171
  writeContractWithRetry
172
- } from "./chunk-QRMZRAMG.js";
172
+ } from "./chunk-MIAMGVZ4.js";
173
173
  import {
174
174
  DEFAULT_NETWORK,
175
175
  VALID_NETWORKS,
@@ -179,7 +179,7 @@ import {
179
179
  isTestnet,
180
180
  setNetwork,
181
181
  withNetwork
182
- } from "./chunk-PVTZMRC6.js";
182
+ } from "./chunk-G3FUEM5N.js";
183
183
  import {
184
184
  addFund,
185
185
  cacheGroupId,
@@ -212,7 +212,7 @@ import {
212
212
  SWAP_ROUTER_EXACT_INPUT_SINGLE_ABI,
213
213
  SYNDICATE_VAULT_ABI,
214
214
  VENICE_STAKING_ABI
215
- } from "./chunk-F3MDCUGO.js";
215
+ } from "./chunk-J2IZWAEW.js";
216
216
  import {
217
217
  fetchMetadata,
218
218
  mergeFundMetadata,
@@ -223,13 +223,13 @@ import {
223
223
  import { config as loadDotenv } from "dotenv";
224
224
  import { createRequire } from "module";
225
225
  import { Command, Option } from "commander";
226
- import { parseUnits as parseUnits11, formatUnits as formatUnits9, isAddress as isAddress10 } from "viem";
226
+ import { parseUnits as parseUnits11, formatUnits as formatUnits10, isAddress as isAddress10 } from "viem";
227
227
  import chalk17 from "chalk";
228
228
  import ora9 from "ora";
229
229
  import { input, confirm, select } from "@inquirer/prompts";
230
230
 
231
231
  // src/lib/generated/contract-constants.ts
232
- var MAX_AGENT_FEE_BPS = 3e3;
232
+ var MAX_AGENT_FEE_BPS = 2500;
233
233
 
234
234
  // src/lib/format.ts
235
235
  function formatDurationShort(seconds) {
@@ -890,10 +890,10 @@ function formatBatch(calls) {
890
890
 
891
891
  // src/commands/strategy-propose.ts
892
892
  function registerStrategyProposeCommands(strategy2) {
893
- 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, hyperliquid-perp, hyperliquid-grid").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("--duration <duration>", "Strategy duration (7d, 24h, etc.)").option("--proposer <address>", "Proposer/agent address (required for --calldata-only \u2014 the external signer)").option("--metadata-uri <uri>", "Metadata URI (required for --calldata-only \u2014 IPFS pin needs a signer)").option("--salt <bytes32>", "CREATE2 salt for the deterministic clone (--calldata-only; default: derived from inputs)").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)").option("--feed-ids <list>", "Comma-separated Chainlink feed ids/addresses per token (Portfolio; auto-resolved from registry in push-feed mode)").option("--price-decimals <list>", "Comma-separated raw feed decimals per token (Portfolio; default 8 push / 18 Data Streams)").option("--swap-routes <list>", "Comma-separated per-token swap route spec (Portfolio). Per token: 'v3:<fee>' (mode 0), 'v4:<fee>:<tickSpacing>' (mode 2), or 'v4path:<hop>+<hop>...' (mode 3) where hop='<currency>@<fee>:<tickSpacing>', currency='eth'/'native'=ETH or 'self'=this token or an address. Empty entry = auto-detect / robinhood default.").option(
893
+ 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, hyperliquid-perp, hyperliquid-grid").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("--duration <duration>", "Strategy duration (7d, 24h, etc.)").option("--proposer <address>", "Proposer/agent address (required for --calldata-only \u2014 the external signer)").option("--metadata-uri <uri>", "Metadata URI (required for --calldata-only \u2014 IPFS pin needs a signer)").option("--salt <bytes32>", "CREATE2 salt for the deterministic clone (--calldata-only; default: derived from inputs)").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)").option("--feeds <list>", "Comma-separated Chainlink AggregatorV3 push-feed addresses per token (Portfolio; auto-resolved from the registry when a slot is left empty)").option("--price-decimals <list>", "Comma-separated raw feed decimals per token (Portfolio; default 8). Must equal the feed's live decimals().").option("--swap-routes <list>", "Comma-separated per-token swap route spec (Portfolio). Per token: 'v3:<fee>' (mode 0), 'v4:<fee>:<tickSpacing>' (mode 2), or 'v4path:<hop>+<hop>...' (mode 3) where hop='<currency>@<fee>:<tickSpacing>', currency='eth'/'native'=ETH or 'self'=this token or an address. Empty entry = auto-detect / robinhood default.").option(
894
894
  "--max-drawdown-bps <bps>",
895
895
  "Declared loss allowance for this proposal, in bps of --amount (0-10000, default 2000 = 20%). This is the envelope voters and guardians are asked to accept, and the basis the settle-price guard sizes its tolerance from \u2014 not a formality."
896
- ).option("--max-price-ages <list>", "Comma-separated per-token max Chainlink push-feed staleness in seconds (Portfolio, push mode; robinhood-mainnet equity feeds default to 345600 = 96h, crypto/stable feeds to the contract default). Empty entry = registry default.").option("--leverage <number>", "Leverage multiplier (Hyperliquid Perp, default: 10)").option("--asset-index <number>", "Perp asset index (Hyperliquid Perp, default: 0 for BTC)").option("--max-position <amount>", "Max position size in USD (Hyperliquid Perp, default: 100000)").option("--max-trades-per-day <n>", "Max trades per day (Hyperliquid Perp, default: 50)").option("--max-order-size <amount>", "Max single-order size in asset units (Hyperliquid Grid, default: 10000)").option("--max-orders-per-tick <n>", "Max orders per tick (Hyperliquid Grid, default: 20)").option("--asset-indices <list>", "Comma-separated perp asset indices (Hyperliquid Grid, default: 0)").action(async (templateKey, opts) => {
896
+ ).option("--leverage <number>", "Leverage multiplier (Hyperliquid Perp, default: 10)").option("--asset-index <number>", "Perp asset index (Hyperliquid Perp, default: 0 for BTC)").option("--max-position <amount>", "Max position size in USD (Hyperliquid Perp, default: 100000)").option("--max-trades-per-day <n>", "Max trades per day (Hyperliquid Perp, default: 50)").option("--max-order-size <amount>", "Max single-order size in asset units (Hyperliquid Grid, default: 10000)").option("--max-orders-per-tick <n>", "Max orders per tick (Hyperliquid Grid, default: 20)").option("--asset-indices <list>", "Comma-separated perp asset indices (Hyperliquid Grid, default: 0)").action(async (templateKey, opts) => {
897
897
  const vault = opts.vault;
898
898
  if (!isAddress3(vault)) {
899
899
  console.error(chalk3.red("Invalid vault address"));
@@ -977,7 +977,7 @@ function registerStrategyProposeCommands(strategy2) {
977
977
  process.exit(1);
978
978
  }
979
979
  if (!opts.agent) opts.agent = proposer;
980
- const { parseDuration: parseDuration3, getGovernorOf: getGovernorOf2 } = await import("./governor-35IXAEDJ.js");
980
+ const { parseDuration: parseDuration3, getGovernorOf: getGovernorOf2 } = await import("./governor-HVBSI6Z2.js");
981
981
  const strategyDuration2 = parseDuration3(opts.duration);
982
982
  const chainId = getChain().id;
983
983
  try {
@@ -1208,9 +1208,9 @@ function registerStrategyProposeCommands(strategy2) {
1208
1208
  console.error(chalk3.red("Missing --name or --duration. Use --write-calls to skip proposal submission."));
1209
1209
  process.exit(1);
1210
1210
  }
1211
- const { propose: propose2 } = await import("./governor-35IXAEDJ.js");
1211
+ const { propose: propose2 } = await import("./governor-HVBSI6Z2.js");
1212
1212
  const { pinJSON } = await import("./ipfs-M56BLMCW.js");
1213
- const { parseDuration: parseDuration2 } = await import("./governor-35IXAEDJ.js");
1213
+ const { parseDuration: parseDuration2 } = await import("./governor-HVBSI6Z2.js");
1214
1214
  const strategyDuration = parseDuration2(opts.duration);
1215
1215
  const metaSpinner = ora2("Pinning metadata to IPFS...").start();
1216
1216
  let metadataURI;
@@ -1242,7 +1242,7 @@ function registerStrategyProposeCommands(strategy2) {
1242
1242
  metadataURI = `data:application/json;base64,${Buffer.from(inline).toString("base64")}`;
1243
1243
  }
1244
1244
  try {
1245
- const { getAgentFeeBps: getAgentFeeBps2 } = await import("./vault-7O4N6WZG.js");
1245
+ const { getAgentFeeBps: getAgentFeeBps2 } = await import("./vault-CDI7BELZ.js");
1246
1246
  const feeBps = await getAgentFeeBps2(vault);
1247
1247
  console.log(
1248
1248
  chalk3.yellow(
@@ -1332,10 +1332,10 @@ function registerStrategyTemplateCommands(strategy2) {
1332
1332
  console.log(chalk4.dim("Full proposal: sherwood strategy propose <template> --vault <addr> ..."));
1333
1333
  console.log();
1334
1334
  });
1335
- 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, hyperliquid-perp, hyperliquid-grid").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)").option("--feed-ids <list>", "Comma-separated Chainlink feed ids/addresses per token (Portfolio; auto-resolved from registry in push-feed mode)").option("--price-decimals <list>", "Comma-separated raw feed decimals per token (Portfolio; default 8 push / 18 Data Streams)").option("--swap-routes <list>", "Comma-separated per-token swap route spec (Portfolio). Per token: 'v3:<fee>' (mode 0), 'v4:<fee>:<tickSpacing>' (mode 2), or 'v4path:<hop>+<hop>...' (mode 3) where hop='<currency>@<fee>:<tickSpacing>', currency='eth'/'native'=ETH or 'self'=this token or an address. Empty entry = auto-detect / robinhood default.").option(
1335
+ 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, hyperliquid-perp, hyperliquid-grid").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)").option("--feeds <list>", "Comma-separated Chainlink AggregatorV3 push-feed addresses per token (Portfolio; auto-resolved from the registry when a slot is left empty)").option("--price-decimals <list>", "Comma-separated raw feed decimals per token (Portfolio; default 8). Must equal the feed's live decimals().").option("--swap-routes <list>", "Comma-separated per-token swap route spec (Portfolio). Per token: 'v3:<fee>' (mode 0), 'v4:<fee>:<tickSpacing>' (mode 2), or 'v4path:<hop>+<hop>...' (mode 3) where hop='<currency>@<fee>:<tickSpacing>', currency='eth'/'native'=ETH or 'self'=this token or an address. Empty entry = auto-detect / robinhood default.").option(
1336
1336
  "--max-drawdown-bps <bps>",
1337
1337
  "Declared loss allowance for this proposal, in bps of --amount (0-10000, default 2000 = 20%). This is the envelope voters and guardians are asked to accept, and the basis the settle-price guard sizes its tolerance from \u2014 not a formality."
1338
- ).option("--max-price-ages <list>", "Comma-separated per-token max Chainlink push-feed staleness in seconds (Portfolio, push mode; robinhood-mainnet equity feeds default to 345600 = 96h, crypto/stable feeds to the contract default). Empty entry = registry default.").option("--leverage <number>", "Leverage multiplier (Hyperliquid Perp, default: 10)").option("--asset-index <number>", "Perp asset index (Hyperliquid Perp, default: 0 for BTC)").option("--max-position <amount>", "Max position size in USD (Hyperliquid Perp, default: 100000)").option("--max-trades-per-day <n>", "Max trades per day (Hyperliquid Perp, default: 50)").option("--max-order-size <amount>", "Max single-order size in asset units (Hyperliquid Grid, default: 10000)").option("--max-orders-per-tick <n>", "Max orders per tick (Hyperliquid Grid, default: 20)").option("--asset-indices <list>", "Comma-separated perp asset indices (Hyperliquid Grid, default: 0)").action(async (templateKey, opts) => {
1338
+ ).option("--leverage <number>", "Leverage multiplier (Hyperliquid Perp, default: 10)").option("--asset-index <number>", "Perp asset index (Hyperliquid Perp, default: 0 for BTC)").option("--max-position <amount>", "Max position size in USD (Hyperliquid Perp, default: 100000)").option("--max-trades-per-day <n>", "Max trades per day (Hyperliquid Perp, default: 50)").option("--max-order-size <amount>", "Max single-order size in asset units (Hyperliquid Grid, default: 10000)").option("--max-orders-per-tick <n>", "Max orders per tick (Hyperliquid Grid, default: 20)").option("--asset-indices <list>", "Comma-separated perp asset indices (Hyperliquid Grid, default: 0)").action(async (templateKey, opts) => {
1339
1339
  const vault = opts.vault;
1340
1340
  if (!isAddress4(vault)) {
1341
1341
  console.error(chalk4.red("Invalid vault address"));
@@ -1403,10 +1403,10 @@ function registerStrategyTemplateCommands(strategy2) {
1403
1403
  console.log(chalk4.dim("Use this address in your proposal batch calls."));
1404
1404
  console.log();
1405
1405
  });
1406
- 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, hyperliquid-perp, hyperliquid-grid").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)").option("--feed-ids <list>", "Comma-separated Chainlink feed ids/addresses per token (Portfolio; auto-resolved from registry in push-feed mode)").option("--price-decimals <list>", "Comma-separated raw feed decimals per token (Portfolio; default 8 push / 18 Data Streams)").option("--swap-routes <list>", "Comma-separated per-token swap route spec (Portfolio). Per token: 'v3:<fee>' (mode 0), 'v4:<fee>:<tickSpacing>' (mode 2), or 'v4path:<hop>+<hop>...' (mode 3) where hop='<currency>@<fee>:<tickSpacing>', currency='eth'/'native'=ETH or 'self'=this token or an address. Empty entry = auto-detect / robinhood default.").option(
1406
+ 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, hyperliquid-perp, hyperliquid-grid").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)").option("--feeds <list>", "Comma-separated Chainlink AggregatorV3 push-feed addresses per token (Portfolio; auto-resolved from the registry when a slot is left empty)").option("--price-decimals <list>", "Comma-separated raw feed decimals per token (Portfolio; default 8). Must equal the feed's live decimals().").option("--swap-routes <list>", "Comma-separated per-token swap route spec (Portfolio). Per token: 'v3:<fee>' (mode 0), 'v4:<fee>:<tickSpacing>' (mode 2), or 'v4path:<hop>+<hop>...' (mode 3) where hop='<currency>@<fee>:<tickSpacing>', currency='eth'/'native'=ETH or 'self'=this token or an address. Empty entry = auto-detect / robinhood default.").option(
1407
1407
  "--max-drawdown-bps <bps>",
1408
1408
  "Declared loss allowance for this proposal, in bps of --amount (0-10000, default 2000 = 20%). This is the envelope voters and guardians are asked to accept, and the basis the settle-price guard sizes its tolerance from \u2014 not a formality."
1409
- ).option("--max-price-ages <list>", "Comma-separated per-token max Chainlink push-feed staleness in seconds (Portfolio, push mode; robinhood-mainnet equity feeds default to 345600 = 96h, crypto/stable feeds to the contract default). Empty entry = registry default.").option("--leverage <number>", "Leverage multiplier (Hyperliquid Perp, default: 10)").option("--asset-index <number>", "Perp asset index (Hyperliquid Perp, default: 0 for BTC)").option("--max-position <amount>", "Max position size in USD (Hyperliquid Perp, default: 100000)").option("--max-trades-per-day <n>", "Max trades per day (Hyperliquid Perp, default: 50)").option("--max-order-size <amount>", "Max single-order size in asset units (Hyperliquid Grid, default: 10000)").option("--max-orders-per-tick <n>", "Max orders per tick (Hyperliquid Grid, default: 20)").option("--asset-indices <list>", "Comma-separated perp asset indices (Hyperliquid Grid, default: 0)").action(async (templateKey, opts) => {
1409
+ ).option("--leverage <number>", "Leverage multiplier (Hyperliquid Perp, default: 10)").option("--asset-index <number>", "Perp asset index (Hyperliquid Perp, default: 0 for BTC)").option("--max-position <amount>", "Max position size in USD (Hyperliquid Perp, default: 100000)").option("--max-trades-per-day <n>", "Max trades per day (Hyperliquid Perp, default: 50)").option("--max-order-size <amount>", "Max single-order size in asset units (Hyperliquid Grid, default: 10000)").option("--max-orders-per-tick <n>", "Max orders per tick (Hyperliquid Grid, default: 20)").option("--asset-indices <list>", "Comma-separated perp asset indices (Hyperliquid Grid, default: 0)").action(async (templateKey, opts) => {
1410
1410
  const clone = opts.clone;
1411
1411
  const vault = opts.vault;
1412
1412
  if (!isAddress4(clone)) {
@@ -1513,7 +1513,7 @@ function registerStrategyTemplateCommands(strategy2) {
1513
1513
  let prices = [];
1514
1514
  let priceSource = null;
1515
1515
  try {
1516
- const { getTokenPricesInAsset } = await import("./price-I5T77X7Y.js");
1516
+ const { getTokenPricesInAsset } = await import("./price-O3THX2HL.js");
1517
1517
  prices = await getTokenPricesInAsset({
1518
1518
  tokens: allocations.map((a, i) => ({
1519
1519
  token: a.token,
@@ -1664,7 +1664,7 @@ function registerStrategyTemplateCommands(strategy2) {
1664
1664
  if (stateStr === "Executed") {
1665
1665
  console.log();
1666
1666
  console.log(chalk4.dim("Rebalance: sherwood strategy rebalance " + clone));
1667
- console.log(chalk4.dim("With new weights: sherwood strategy rebalance " + clone + " --new-weights 2500,2500,2000,1500,1500"));
1667
+ console.log(chalk4.dim("Tighten slippage first: sherwood strategy rebalance " + clone + " --max-slippage 200"));
1668
1668
  }
1669
1669
  console.log();
1670
1670
  } catch (err) {
@@ -1673,7 +1673,7 @@ function registerStrategyTemplateCommands(strategy2) {
1673
1673
  process.exit(1);
1674
1674
  }
1675
1675
  });
1676
- 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) => {
1676
+ strategy2.command("rebalance").description("Rebalance a portfolio strategy \u2014 trade each allocation back to its target weight (Portfolio only)").argument("<clone>", "Strategy clone address").option("--max-slippage <bps>", "Tighten max slippage bps before rebalancing (weights and routes are frozen at init \u2014 only a LOWER tolerance is accepted)").option("--dry-run", "Show what would happen without executing").action(async (cloneArg, opts) => {
1677
1677
  if (!isAddress4(cloneArg)) {
1678
1678
  console.error(chalk4.red("Invalid clone address"));
1679
1679
  process.exit(1);
@@ -1721,50 +1721,29 @@ function registerStrategyTemplateCommands(strategy2) {
1721
1721
  ` ${chalk4.bold(symbol.padEnd(8))} ${weightPct.padEnd(8)} bal: ${formatUnits5(balance, decimals)}`
1722
1722
  );
1723
1723
  }
1724
- let newWeightsBps;
1725
- if (opts.newWeights) {
1726
- newWeightsBps = opts.newWeights.split(",").map((w) => Number(w.trim()));
1727
- if (newWeightsBps.length !== allocations.length) {
1728
- console.error(chalk4.red(`--new-weights must have ${allocations.length} values (one per token)`));
1729
- process.exit(1);
1730
- }
1731
- const weightSum = newWeightsBps.reduce((a, b) => a + b, 0);
1732
- if (weightSum !== 1e4) {
1733
- console.error(chalk4.red(`Weights must sum to 10000, got ${weightSum}`));
1734
- process.exit(1);
1735
- }
1736
- console.log();
1737
- console.log(chalk4.bold("New Target Weights"));
1738
- console.log(chalk4.dim("\u2500".repeat(50)));
1739
- for (let i = 0; i < allocations.length; i++) {
1740
- const [, symbol] = tokenData[i];
1741
- const oldPct = (Number(allocations[i].targetWeightBps) / 100).toFixed(1);
1742
- const newPct = (newWeightsBps[i] / 100).toFixed(1);
1743
- const arrow = oldPct !== newPct ? chalk4.yellow("\u2192") : chalk4.dim("\u2192");
1744
- console.log(` ${chalk4.bold(symbol.padEnd(8))} ${oldPct}% ${arrow} ${newPct}%`);
1745
- }
1724
+ const newMaxSlippageBps = opts.maxSlippage ? Number(opts.maxSlippage) : void 0;
1725
+ if (newMaxSlippageBps !== void 0 && (!Number.isInteger(newMaxSlippageBps) || newMaxSlippageBps <= 0)) {
1726
+ console.error(chalk4.red(`--max-slippage must be a positive integer bps, got '${opts.maxSlippage}'`));
1727
+ process.exit(1);
1746
1728
  }
1747
1729
  if (opts.dryRun) {
1748
1730
  console.log();
1749
1731
  console.log(chalk4.yellow("Dry run \u2014 no transactions sent."));
1750
1732
  console.log(chalk4.dim("The rebalance would:"));
1751
- console.log(chalk4.dim(" 1. Sell all current token positions back to " + assetSymbol));
1752
- console.log(chalk4.dim(" 2. Re-buy at " + (newWeightsBps ? "new" : "current") + " target weights"));
1733
+ if (newMaxSlippageBps !== void 0) {
1734
+ console.log(chalk4.dim(" 0. Tighten max slippage to " + newMaxSlippageBps + " bps"));
1735
+ }
1736
+ console.log(chalk4.dim(" 1. Price every allocation off its bound push feed"));
1737
+ console.log(chalk4.dim(` 2. Trade each slot's drift back to its target weight, via ${assetSymbol}`));
1753
1738
  console.log();
1754
1739
  return;
1755
1740
  }
1756
- if (newWeightsBps) {
1757
- const maxSlip = Number(opts.maxSlippage || "0");
1758
- const swapData = await publicClient.readContract({
1759
- address: clone,
1760
- abi: PORTFOLIO_STRATEGY_ABI,
1761
- functionName: "getSwapExtraData"
1762
- });
1763
- const updateSpinner = ora3("Updating target weights...").start();
1741
+ if (newMaxSlippageBps !== void 0) {
1742
+ const updateSpinner = ora3("Tightening max slippage...").start();
1764
1743
  try {
1765
1744
  const innerData = encodeAbiParameters3(
1766
1745
  [{ type: "uint256[]" }, { type: "uint256" }, { type: "bytes[]" }],
1767
- [newWeightsBps.map((w) => BigInt(w)), BigInt(maxSlip), swapData]
1746
+ [[], BigInt(newMaxSlippageBps), []]
1768
1747
  );
1769
1748
  const updateHash = await writeContractWithRetry({
1770
1749
  account,
@@ -1776,25 +1755,25 @@ function registerStrategyTemplateCommands(strategy2) {
1776
1755
  });
1777
1756
  const receipt = await waitForReceipt(updateHash);
1778
1757
  if (receipt.status === "reverted") throw new Error("updateParams reverted");
1779
- updateSpinner.succeed("Target weights updated");
1758
+ updateSpinner.succeed(`Max slippage tightened to ${newMaxSlippageBps} bps`);
1780
1759
  console.log(chalk4.dim(` Tx: ${getExplorerUrl(updateHash)}`));
1781
1760
  } catch (err) {
1782
- updateSpinner.fail("Failed to update weights");
1761
+ updateSpinner.fail("Failed to tighten slippage");
1783
1762
  console.error(chalk4.red(formatContractError(err)));
1784
1763
  process.exit(1);
1785
1764
  }
1786
1765
  }
1787
- const rebalanceSpinner = ora3("Rebalancing portfolio (sell all \u2192 re-buy at targets)...").start();
1766
+ const rebalanceSpinner = ora3("Rebalancing portfolio (trading each slot's drift)...").start();
1788
1767
  try {
1789
1768
  const rebalanceHash = await writeContractWithRetry({
1790
1769
  account,
1791
1770
  chain,
1792
1771
  address: clone,
1793
1772
  abi: PORTFOLIO_STRATEGY_ABI,
1794
- functionName: "rebalance"
1773
+ functionName: "rebalanceDelta"
1795
1774
  });
1796
1775
  const receipt = await waitForReceipt(rebalanceHash);
1797
- if (receipt.status === "reverted") throw new Error("rebalance() reverted on-chain");
1776
+ if (receipt.status === "reverted") throw new Error("rebalanceDelta() reverted on-chain");
1798
1777
  rebalanceSpinner.succeed("Portfolio rebalanced");
1799
1778
  console.log(chalk4.dim(` Tx: ${getExplorerUrl(rebalanceHash)}`));
1800
1779
  } catch (err) {
@@ -1943,7 +1922,7 @@ function registerVeniceCommands(program2) {
1943
1922
  console.log(chalk5.dim(` Key: ${apiKey.slice(0, 8)}...${apiKey.slice(-4)}`));
1944
1923
  console.log(chalk5.dim(" Saved to ~/.sherwood/config.json"));
1945
1924
  try {
1946
- const { createVeniceProvisionAttestation, getEasScanUrl } = await import("./eas-D3BVHBJH.js");
1925
+ const { createVeniceProvisionAttestation, getEasScanUrl } = await import("./eas-VFTWEI4K.js");
1947
1926
  const attestation = await createVeniceProvisionAttestation(account.address);
1948
1927
  if (attestation) {
1949
1928
  console.log(chalk5.dim(` Attested: ${getEasScanUrl(attestation.uid)}`));
@@ -2299,10 +2278,10 @@ ${opts.prompt}`;
2299
2278
  console.log(chalk5.dim(`Model: ${result.model} | Tokens: ${result.usage.promptTokens} in, ${result.usage.completionTokens} out, ${result.usage.totalTokens} total`));
2300
2279
  }
2301
2280
  try {
2302
- const { createVeniceInferenceAttestation, getEasScanUrl } = await import("./eas-D3BVHBJH.js");
2281
+ const { createVeniceInferenceAttestation, getEasScanUrl } = await import("./eas-VFTWEI4K.js");
2303
2282
  const { keccak256: keccak2562, toHex, isAddress: isAddr } = await import("viem");
2304
2283
  const { getChainContracts: getChainContracts2 } = await import("./config-ZGFS4I7K.js");
2305
- const { getChain: getActiveChain } = await import("./network-FEHRFDPJ.js");
2284
+ const { getChain: getActiveChain } = await import("./network-7445KSCF.js");
2306
2285
  const vaultRecipient = opts.vault && isAddr(opts.vault) ? opts.vault : getChainContracts2(getActiveChain().id).vault;
2307
2286
  const promptHash = keccak2562(toHex(userContent)).slice(0, 18);
2308
2287
  const attestation = await createVeniceInferenceAttestation(
@@ -2600,7 +2579,7 @@ function registerAllowanceCommands(program2) {
2600
2579
  }
2601
2580
 
2602
2581
  // src/commands/proposal.ts
2603
- import { isAddress as isAddress7, encodeFunctionData as encodeFunctionData3, parseUnits as parseUnits8 } from "viem";
2582
+ import { isAddress as isAddress7, encodeFunctionData as encodeFunctionData3, formatUnits as formatUnits8, parseUnits as parseUnits8 } from "viem";
2604
2583
  import chalk8 from "chalk";
2605
2584
  import ora6 from "ora";
2606
2585
  import { readFileSync as readFileSync2 } from "fs";
@@ -3298,8 +3277,8 @@ function printSimulationResult(result, callType) {
3298
3277
  }
3299
3278
  async function sendSimulationAlert(subdomain, proposalId, vault, execResult, settleResult) {
3300
3279
  try {
3301
- const xmtp = await import("./xmtp-R25Q5GWY.js");
3302
- const { getAccount: getAccount2 } = await import("./client-3SE632Q2.js");
3280
+ const xmtp = await import("./xmtp-7LAWDKJT.js");
3281
+ const { getAccount: getAccount2 } = await import("./client-C6N2ASTH.js");
3303
3282
  const group = await xmtp.getGroup("", subdomain);
3304
3283
  const allRisks = [...execResult.risks, ...settleResult?.risks ?? []];
3305
3284
  const criticals = allRisks.filter((r) => r.level === "critical");
@@ -3408,47 +3387,12 @@ function parseCallsFile(path) {
3408
3387
  value: BigInt(c.value || "0")
3409
3388
  }));
3410
3389
  }
3411
- function parseSandboxCallsFile(path) {
3412
- const raw = readFileSync2(path, "utf-8");
3413
- const parsed = JSON.parse(raw);
3414
- if (!Array.isArray(parsed)) {
3415
- console.error(chalk8.red(" sandbox calls file must be a JSON array of {target, data} objects"));
3416
- process.exit(1);
3390
+ function parseWoodAmount(raw, flag) {
3391
+ const s = String(raw ?? "").trim();
3392
+ if (!/^\d+(\.\d+)?$/.test(s)) {
3393
+ throw new Error(`${flag} must be a non-negative decimal WOOD amount, got "${s}".`);
3417
3394
  }
3418
- return parsed.map((c, i) => {
3419
- if (!c || typeof c !== "object") {
3420
- console.error(chalk8.red(` sandbox.calls[${i}] must be an object`));
3421
- process.exit(1);
3422
- }
3423
- const rec = c;
3424
- if (rec.value !== void 0 && rec.value !== null) {
3425
- console.error(
3426
- chalk8.red(
3427
- ` sandbox.calls[${i}] must be (address,bytes) only \u2014 ICallSandbox.Call has no value field.`
3428
- )
3429
- );
3430
- process.exit(1);
3431
- }
3432
- if (typeof rec.target !== "string" || !isAddress7(rec.target)) {
3433
- console.error(chalk8.red(` sandbox.calls[${i}].target must be a 0x-prefixed address`));
3434
- process.exit(1);
3435
- }
3436
- if (typeof rec.data !== "string" || !/^0x[0-9a-fA-F]*$/.test(rec.data)) {
3437
- console.error(chalk8.red(` sandbox.calls[${i}].data must be a 0x-prefixed hex string`));
3438
- process.exit(1);
3439
- }
3440
- return { target: rec.target, data: rec.data };
3441
- });
3442
- }
3443
- function parseSandboxTokens(list) {
3444
- if (list === void 0 || list.trim().length === 0) return [];
3445
- return list.split(",").map((x) => x.trim()).filter((x) => x.length > 0).map((a, i) => {
3446
- if (!isAddress7(a)) {
3447
- console.error(chalk8.red(` --sandbox-tokens[${i}] is not a 0x-prefixed address: ${a}`));
3448
- process.exit(1);
3449
- }
3450
- return a;
3451
- });
3395
+ return parseUnits8(s, 18);
3452
3396
  }
3453
3397
  var GUARDIAN_REGISTRY_ABI = [
3454
3398
  {
@@ -3486,7 +3430,10 @@ var GUARDIAN_REGISTRY_ABI = [
3486
3430
  inputs: [
3487
3431
  { name: "governor", type: "address" },
3488
3432
  { name: "proposalId", type: "uint256" },
3489
- { name: "support", type: "uint8" }
3433
+ { name: "support", type: "uint8" },
3434
+ // The approver's declaration: WOOD it puts behind this proposal,
3435
+ // clamped to its free budget by ExposureLedger. Ignored on a Block.
3436
+ { name: "lockWood", type: "uint256" }
3490
3437
  ],
3491
3438
  outputs: [],
3492
3439
  stateMutability: "nonpayable"
@@ -3744,171 +3691,6 @@ function registerProposalCommands(program2) {
3744
3691
  process.exit(1);
3745
3692
  }
3746
3693
  });
3747
- proposal.command("create-sandbox").description("Submit a strategy proposal with a CallSandbox payload (ICallSandbox.Call is (address,bytes) only \u2014 no value)").requiredOption("--vault <address>", "Vault address the proposal targets").requiredOption("--name <name>", "Strategy name").requiredOption("--description <text>", "Strategy rationale and risk summary").requiredOption("--duration <duration>", "Strategy duration (e.g. 7d, 24h, 3600)").requiredOption("--execute-calls <path>", "Path to JSON file with execute Call[] array").requiredOption("--settle-calls <path>", "Path to JSON file with settlement Call[] array").requiredOption(
3748
- "--sandbox-calls <path>",
3749
- "Path to JSON file of ICallSandbox.Call[] \u2014 [{target, data}, ...]. A value field is refused."
3750
- ).requiredOption(
3751
- "--sandbox-funding <amount>",
3752
- "Vault-asset amount the sandbox is funded with (decimals resolved from the asset). Must be > 0 and <= --max-capital."
3753
- ).option("--sandbox-tokens <list>", "Comma-separated declared non-asset token addresses the payload may hold").option("--strategy <address>", "Live-NAV strategy clone address (default: none \u2014 queue-only)").option(
3754
- "--max-capital <amount>",
3755
- "REQUIRED. Ceiling on what the execute batch may deploy, in vault-asset units."
3756
- ).option(
3757
- "--max-drawdown-bps <bps>",
3758
- "Declared loss allowance in bps of --max-capital (0-10000, default 2000 = 20%)."
3759
- ).option("--execute-caps <list>", "Comma-separated per-execute-call outflow caps in asset units").option("--settle-caps <list>", "Comma-separated per-settlement-call outflow caps in asset units").option("--metadata-uri <uri>", "Override \u2014 skip IPFS upload and use this URI directly").action(async (opts) => {
3760
- try {
3761
- const vault = opts.vault;
3762
- if (!isAddress7(vault)) {
3763
- console.error(chalk8.red(`Invalid vault address: ${opts.vault}`));
3764
- process.exit(1);
3765
- }
3766
- const strategyDuration = parseDuration(opts.duration);
3767
- const executeCalls = parseCallsFile(opts.executeCalls);
3768
- const settleCalls = parseCallsFile(opts.settleCalls);
3769
- const sandboxCalls = parseSandboxCallsFile(opts.sandboxCalls);
3770
- const declaredTokens = parseSandboxTokens(opts.sandboxTokens);
3771
- const ZERO_STRATEGY = "0x0000000000000000000000000000000000000000";
3772
- let strategy2 = ZERO_STRATEGY;
3773
- if (opts.strategy) {
3774
- if (!isAddress7(opts.strategy)) {
3775
- console.error(chalk8.red(`Invalid strategy address: ${opts.strategy}`));
3776
- process.exit(1);
3777
- }
3778
- strategy2 = opts.strategy;
3779
- }
3780
- const envelope = await resolveRiskEnvelope(opts, vault);
3781
- const asset = await getAssetAddress();
3782
- const decimals = await getPublicClient().readContract({
3783
- address: asset,
3784
- abi: ERC20_ABI,
3785
- functionName: "decimals"
3786
- });
3787
- const funding = parseUnits8(String(opts.sandboxFunding), decimals);
3788
- const executeCallCaps = resolveCaps(
3789
- opts.executeCaps,
3790
- executeCalls.length,
3791
- envelope.maxCapital,
3792
- "--execute-caps"
3793
- );
3794
- const settleCallCaps = resolveCaps(
3795
- opts.settleCaps,
3796
- settleCalls.length,
3797
- 0n,
3798
- "--settle-caps"
3799
- );
3800
- const buildAction = async (metadataURI2) => encodeProposeWithSandbox(
3801
- {
3802
- vault,
3803
- governor: await getGovernorOf(vault),
3804
- strategy: strategy2,
3805
- metadataURI: metadataURI2,
3806
- strategyDuration,
3807
- envelope,
3808
- executeCalls,
3809
- executeCallCaps,
3810
- settlementCalls: settleCalls,
3811
- settlementCallCaps: settleCallCaps,
3812
- sandbox: { funding, calls: sandboxCalls, declaredTokens }
3813
- },
3814
- getChain().id
3815
- );
3816
- if (isCalldataOnly()) {
3817
- if (!opts.metadataUri) {
3818
- console.error(
3819
- chalk8.red(
3820
- "--calldata-only requires --metadata-uri (IPFS metadata upload needs a signing wallet)"
3821
- )
3822
- );
3823
- process.exit(1);
3824
- }
3825
- try {
3826
- emitCalldata(await buildAction(opts.metadataUri));
3827
- } catch (err) {
3828
- console.error(chalk8.red(err instanceof Error ? err.message : String(err)));
3829
- process.exit(1);
3830
- }
3831
- return;
3832
- }
3833
- let metadataURI = opts.metadataUri || "";
3834
- if (!metadataURI) {
3835
- const spinner2 = ora6({ text: W3("Uploading metadata to IPFS..."), color: "green" }).start();
3836
- try {
3837
- const account = getAccount();
3838
- const metadata = {
3839
- schema: "sherwood/proposal/v1",
3840
- name: opts.name,
3841
- description: opts.description,
3842
- chain: getNetwork(),
3843
- strategies: [],
3844
- terms: {},
3845
- links: {}
3846
- };
3847
- const proposalMeta = {
3848
- ...metadata,
3849
- proposer: account.address,
3850
- vault,
3851
- strategyDuration: Number(strategyDuration),
3852
- sandbox: true,
3853
- createdAt: Math.floor(Date.now() / 1e3)
3854
- };
3855
- metadataURI = await uploadMetadata(proposalMeta);
3856
- spinner2.succeed(G3(`Metadata pinned: ${DIM3(metadataURI)}`));
3857
- } catch (err) {
3858
- spinner2.warn(chalk8.yellow("IPFS upload failed \u2014 using inline metadata"));
3859
- const json = JSON.stringify({ name: opts.name, description: opts.description, vault, sandbox: true });
3860
- metadataURI = `data:application/json;base64,${Buffer.from(json).toString("base64")}`;
3861
- }
3862
- }
3863
- console.log();
3864
- console.log(LABEL3(" \u25C6 Sandbox Proposal Summary"));
3865
- SEP3();
3866
- console.log(W3(` Name: ${BOLD3(opts.name)}`));
3867
- console.log(W3(` Vault: ${G3(vault)}`));
3868
- console.log(W3(` Duration: ${formatDurationShort(strategyDuration)}`));
3869
- console.log(W3(` Calls: ${executeCalls.length} execute + ${settleCalls.length} settle + ${sandboxCalls.length} sandbox`));
3870
- console.log(W3(` Sandbox funding: ${opts.sandboxFunding}`));
3871
- console.log(W3(` Metadata: ${DIM3(metadataURI.length > 50 ? metadataURI.slice(0, 50) + "..." : metadataURI)}`));
3872
- SEP3();
3873
- const spinner = ora6({ text: W3("Submitting sandbox proposal..."), color: "green" }).start();
3874
- let action;
3875
- try {
3876
- action = await buildAction(metadataURI);
3877
- } catch (err) {
3878
- spinner.fail(err instanceof Error ? err.message : String(err));
3879
- process.exit(1);
3880
- }
3881
- const tx = action.txs[0];
3882
- const hash = await sendTxWithRetry({
3883
- account: getAccount(),
3884
- chain: getChain(),
3885
- to: tx.to,
3886
- data: tx.data,
3887
- value: 0n
3888
- });
3889
- const receipt = await waitForReceipt(hash);
3890
- let proposalId = parseProposalIdFromLogs(receipt.logs);
3891
- if (proposalId === void 0) {
3892
- try {
3893
- proposalId = await proposalCount(vault);
3894
- } catch {
3895
- proposalId = 0n;
3896
- }
3897
- }
3898
- spinner.succeed(G3("Sandbox proposal submitted"));
3899
- console.log();
3900
- console.log(LABEL3(" \u25C6 Sandbox Proposal Created"));
3901
- SEP3();
3902
- console.log(W3(` Proposal ID: ${G3(`#${proposalId}`)}`));
3903
- console.log(W3(` Tx: ${DIM3(getExplorerUrl(receipt.transactionHash))}`));
3904
- SEP3();
3905
- console.log();
3906
- } catch (err) {
3907
- console.error(chalk8.red(`
3908
- \u2716 ${formatContractError(err)}`));
3909
- process.exit(1);
3910
- }
3911
- });
3912
3694
  proposal.command("list").description("List a vault's proposals (per-vault governor \u2014 IDs are scoped to the vault)").requiredOption("--vault <address>", "Vault whose proposals to list").option("--state <filter>", "Filter by state: draft, pending, approved, executed, settled, all", "all").action(async (opts) => {
3913
3695
  const spinner = ora6("Loading proposals...").start();
3914
3696
  try {
@@ -3941,7 +3723,7 @@ function registerProposalCommands(program2) {
3941
3723
  console.log(BOLD3(`Proposals (${proposals.length})`));
3942
3724
  console.log(DIM3("\u2500".repeat(90)));
3943
3725
  console.log(
3944
- DIM3(" ID ") + DIM3("Agent".padEnd(14)) + DIM3("State".padEnd(12)) + DIM3("Fee".padEnd(8)) + DIM3("Votes (For/Against)".padEnd(22)) + DIM3("Duration".padEnd(10)) + DIM3("Created")
3726
+ DIM3(" ID ") + DIM3("Agent".padEnd(14)) + DIM3("State".padEnd(12)) + DIM3("Fee".padEnd(8)) + DIM3("Against/Votable".padEnd(22)) + DIM3("Duration".padEnd(10)) + DIM3("Created")
3945
3727
  );
3946
3728
  console.log(DIM3("\u2500".repeat(90)));
3947
3729
  for (const p of proposals) {
@@ -3949,7 +3731,7 @@ function registerProposalCommands(program2) {
3949
3731
  const created = p.snapshotTimestamp > 0n ? new Date(Number(p.snapshotTimestamp) * 1e3).toLocaleDateString() : "\u2014";
3950
3732
  const agent = `${p.proposer.slice(0, 6)}...${p.proposer.slice(-4)}`;
3951
3733
  const dur = formatDurationShort(p.strategyDuration);
3952
- const votes = `${formatShares(p.votesFor)}/${formatShares(p.votesAgainst)}`;
3734
+ const votes = `${formatShares(p.votesAgainst)}/${formatShares(p.votableSupply)}`;
3953
3735
  const fee = `${Number(p.performanceFeeBps) / 100}%`;
3954
3736
  console.log(
3955
3737
  ` ${String(p.id).padEnd(4)}${agent.padEnd(14)}${state.padEnd(12)}${fee.padEnd(8)}${votes.padEnd(22)}${dur.padEnd(10)}${created}`
@@ -3975,11 +3757,9 @@ function registerProposalCommands(program2) {
3975
3757
  }
3976
3758
  const p = await getProposal(vault, id);
3977
3759
  const state = await getProposalState(vault, id);
3978
- const params = await getGovernorParams(vault);
3979
3760
  spinner.stop();
3980
3761
  const stateLabel = PROPOSAL_STATES[state] || "Unknown";
3981
- const totalVotes = p.votesFor + p.votesAgainst;
3982
- const quorumNeeded = totalVotes > 0n ? `${Number(params.vetoThresholdBps) / 100}%` : "\u2014";
3762
+ const vetoBar = p.votableSupply > 0n ? p.votableSupply * p.vetoThresholdBps / 10000n || 1n : null;
3983
3763
  console.log();
3984
3764
  console.log(LABEL3(` \u25C6 Proposal #${p.id}`));
3985
3765
  SEP3();
@@ -4007,10 +3787,11 @@ function registerProposalCommands(program2) {
4007
3787
  console.log(W3(` Executed At: ${formatTimestamp(p.executedAt)}`));
4008
3788
  console.log();
4009
3789
  console.log(LABEL3(" Votes"));
4010
- console.log(W3(` For: ${formatShares(p.votesFor)}`));
4011
3790
  console.log(W3(` Against: ${formatShares(p.votesAgainst)}`));
4012
- console.log(W3(` Abstain: ${formatShares(p.votesAbstain)}`));
4013
- console.log(W3(` Veto Threshold: ${quorumNeeded}`));
3791
+ console.log(W3(` Votable Supply: ${formatShares(p.votableSupply)}`));
3792
+ console.log(W3(
3793
+ ` Veto Threshold: ${vetoBar === null ? DIM3("\u2014 (no votable supply \u2014 veto check skipped)") : `${Number(p.vetoThresholdBps) / 100}% (${formatShares(vetoBar)})`}`
3794
+ ));
4014
3795
  if (state === PROPOSAL_STATE.Executed || state === PROPOSAL_STATE.Settled) {
4015
3796
  try {
4016
3797
  const cap = await getCapitalSnapshot(vault, id);
@@ -4097,15 +3878,19 @@ function registerProposalCommands(program2) {
4097
3878
  process.exit(1);
4098
3879
  }
4099
3880
  });
4100
- proposal.command("review-vote").description("Cast a guardian review vote (Approve or Block) on a live review").requiredOption("--id <proposalId>", "Proposal ID").requiredOption("--vault <address>", "Vault the proposal belongs to (per-vault governor)").requiredOption("--support <approve|block>", "Guardian review vote: approve or block").action(async (opts) => {
3881
+ proposal.command("review-vote").description("Cast a guardian review vote (Approve or Block) on a live review").requiredOption("--id <proposalId>", "Proposal ID").requiredOption("--vault <address>", "Vault the proposal belongs to (per-vault governor)").requiredOption("--support <approve|block>", "Guardian review vote: approve or block").requiredOption(
3882
+ "--lock-wood <wood>",
3883
+ "WOOD you declare behind an Approve (decimal WOOD). Clamped to your free budget by ExposureLedger; ignored on a Block."
3884
+ ).action(async (opts) => {
4101
3885
  try {
4102
3886
  const proposalId = parseBigIntArg(opts.id, "proposal ID");
4103
3887
  const vault = parseVaultOption(opts.vault);
4104
3888
  const vote2 = parseGuardianReviewVote(String(opts.support));
3889
+ const lockWood = parseWoodAmount(opts.lockWood, "--lock-wood");
4105
3890
  const chainId = getChain().id;
4106
3891
  const governor = await getGovernorOf(vault);
4107
3892
  if (isCalldataOnly()) {
4108
- emitCalldata(encodeVoteOnProposal({ governor, proposalId, vote: vote2 }, chainId));
3893
+ emitCalldata(encodeVoteOnProposal({ governor, proposalId, vote: vote2, lockWood }, chainId));
4109
3894
  return;
4110
3895
  }
4111
3896
  const account = getAccount();
@@ -4166,6 +3951,9 @@ function registerProposalCommands(program2) {
4166
3951
  SEP3();
4167
3952
  if (vote2 === "Approve") {
4168
3953
  console.log(chalk8.yellow.bold(" \u26A0 UNDERWRITING"));
3954
+ console.log(chalk8.yellow(
3955
+ ` Declared lock: ${formatUnits8(lockWood, 18)} WOOD`
3956
+ ));
4169
3957
  console.log(chalk8.yellow(
4170
3958
  " Approve books this proposal's coverage against YOUR own slashable"
4171
3959
  ));
@@ -4183,7 +3971,7 @@ function registerProposalCommands(program2) {
4183
3971
  ));
4184
3972
  SEP3();
4185
3973
  }
4186
- const action = encodeVoteOnProposal({ governor, proposalId, vote: vote2 }, chainId);
3974
+ const action = encodeVoteOnProposal({ governor, proposalId, vote: vote2, lockWood }, chainId);
4187
3975
  const tx = action.txs[0];
4188
3976
  const voteSpinner = ora6({ text: W3("Submitting review vote..."), color: "green" }).start();
4189
3977
  const hash = await sendTxWithRetry({
@@ -4849,7 +4637,7 @@ function registerProposalCommands(program2) {
4849
4637
  }
4850
4638
 
4851
4639
  // src/commands/queue.ts
4852
- import { parseUnits as parseUnits9, formatUnits as formatUnits8, isAddress as isAddress8, parseEventLogs } from "viem";
4640
+ import { parseUnits as parseUnits9, formatUnits as formatUnits9, isAddress as isAddress8, parseEventLogs } from "viem";
4853
4641
  import chalk9 from "chalk";
4854
4642
  import ora7 from "ora";
4855
4643
  var WETH_ABI = [
@@ -5038,7 +4826,7 @@ function registerQueueCommands(program2) {
5038
4826
  );
5039
4827
  if (ethBalance < shortfall + minGas) {
5040
4828
  throw new Error(
5041
- `Insufficient ETH on ${getChain().name}. Have ${formatUnits8(ethBalance, 18)} ETH, need ${formatUnits8(shortfall, 18)} to wrap + ~${formatUnits8(minGas, 18)} for gas.`
4829
+ `Insufficient ETH on ${getChain().name}. Have ${formatUnits9(ethBalance, 18)} ETH, need ${formatUnits9(shortfall, 18)} to wrap + ~${formatUnits9(minGas, 18)} for gas.`
5042
4830
  );
5043
4831
  }
5044
4832
  spinner.text = "Wrapping ETH \u2192 WETH...";
@@ -5386,14 +5174,78 @@ function registerGovernorCommands(program2) {
5386
5174
  succeed: (v) => `Cooldown period updated to ${v}s (reverts if the vault has an open proposal).`,
5387
5175
  failText: "Failed to set cooldown"
5388
5176
  });
5389
- governor.command("set-protocol-fee").description("Set the protocol fee in bps on ProtocolConfig (protocol owner only)").requiredOption("--bps <n>", "New protocol fee in bps (e.g. 500 = 5%, max 1000 = 10%)").action(async (opts) => {
5390
- const rawValue = String(opts.bps);
5177
+ const parseSplitLeg = (raw, label) => {
5178
+ const v = Number(parseBigIntArg(String(raw), label));
5179
+ if (v > 1e4) {
5180
+ console.error(chalk10.red(`--${label} must be <= 10000 bps, got ${v}`));
5181
+ process.exit(1);
5182
+ }
5183
+ return v;
5184
+ };
5185
+ const requireSum = (legs) => {
5186
+ const sum = Object.values(legs).reduce((a, b) => a + b, 0);
5187
+ if (sum !== 1e4) {
5188
+ const parts = Object.entries(legs).map(([k, v]) => `--${k} ${v}`).join(" ");
5189
+ console.error(
5190
+ chalk10.red(`split legs must sum to 10000 bps, got ${sum} (${parts})`)
5191
+ );
5192
+ process.exit(1);
5193
+ }
5194
+ };
5195
+ const fmtSplit = (legs) => Object.entries(legs).map(([k, v]) => `${k} ${Number(v) / 100}%`).join(" / ");
5196
+ governor.command("set-mgmt-split").description("Set the management-fee split on ProtocolConfig (protocol owner only; legs sum to 10000 bps)").requiredOption("--agent <bps>", "Agent share in bps (e.g. 6000 = 60%)").requiredOption("--protocol <bps>", "Protocol share in bps").requiredOption("--guardian <bps>", "Guardian share in bps").action(async (opts) => {
5197
+ const s = {
5198
+ agentBps: parseSplitLeg(opts.agent, "agent"),
5199
+ protocolBps: parseSplitLeg(opts.protocol, "protocol"),
5200
+ guardianBps: parseSplitLeg(opts.guardian, "guardian")
5201
+ };
5202
+ requireSum({ agent: s.agentBps, protocol: s.protocolBps, guardian: s.guardianBps });
5203
+ if (isCalldataOnly()) {
5204
+ try {
5205
+ emitCalldata(
5206
+ encodeMgmtSplit(
5207
+ { protocolConfig: await getProtocolConfigAddress(), ...s },
5208
+ getChain().id
5209
+ )
5210
+ );
5211
+ } catch (err) {
5212
+ console.error(chalk10.red(formatContractError(err)));
5213
+ process.exit(1);
5214
+ }
5215
+ return;
5216
+ }
5217
+ const spinner = ora8("Setting management-fee split...").start();
5218
+ try {
5219
+ const hash = await setMgmtSplit(s);
5220
+ const configAddress = await getProtocolConfigAddress();
5221
+ spinner.succeed(
5222
+ G4(`Management-fee split updated to ${fmtSplit({ agent: s.agentBps, protocol: s.protocolBps, guardian: s.guardianBps })} on ProtocolConfig (${configAddress}). In-flight proposals keep their propose-time snapshot.`)
5223
+ );
5224
+ console.log(DIM4(` ${getExplorerUrl(hash)}`));
5225
+ } catch (err) {
5226
+ spinner.fail("Failed to set management-fee split");
5227
+ console.error(chalk10.red(formatContractError(err)));
5228
+ process.exit(1);
5229
+ }
5230
+ });
5231
+ governor.command("set-perf-split").description("Set the performance-fee split on ProtocolConfig (protocol owner only; legs sum to 10000 bps)").requiredOption("--agent <bps>", "Agent share in bps (e.g. 5000 = 50%)").requiredOption("--protocol <bps>", "Protocol share in bps").requiredOption("--guardian <bps>", "Guardian share in bps").requiredOption("--owner <bps>", "Vault-owner share in bps").action(async (opts) => {
5232
+ const s = {
5233
+ agentBps: parseSplitLeg(opts.agent, "agent"),
5234
+ protocolBps: parseSplitLeg(opts.protocol, "protocol"),
5235
+ guardianBps: parseSplitLeg(opts.guardian, "guardian"),
5236
+ ownerBps: parseSplitLeg(opts.owner, "owner")
5237
+ };
5238
+ requireSum({
5239
+ agent: s.agentBps,
5240
+ protocol: s.protocolBps,
5241
+ guardian: s.guardianBps,
5242
+ owner: s.ownerBps
5243
+ });
5391
5244
  if (isCalldataOnly()) {
5392
5245
  try {
5393
- const value = parseBigIntArg(rawValue, "bps");
5394
5246
  emitCalldata(
5395
- encodeProtocolFeeBps(
5396
- { protocolConfig: await getProtocolConfigAddress(), value },
5247
+ encodePerfSplit(
5248
+ { protocolConfig: await getProtocolConfigAddress(), ...s },
5397
5249
  getChain().id
5398
5250
  )
5399
5251
  );
@@ -5403,16 +5255,16 @@ function registerGovernorCommands(program2) {
5403
5255
  }
5404
5256
  return;
5405
5257
  }
5406
- const spinner = ora8("Setting protocol fee...").start();
5258
+ const spinner = ora8("Setting performance-fee split...").start();
5407
5259
  try {
5408
- const hash = await setProtocolFeeBps(parseBigIntArg(rawValue, "bps"));
5260
+ const hash = await setPerfSplit(s);
5409
5261
  const configAddress = await getProtocolConfigAddress();
5410
5262
  spinner.succeed(
5411
- G4(`Protocol fee updated to ${Number(rawValue) / 100}% on ProtocolConfig (${configAddress}).`)
5263
+ G4(`Performance-fee split updated to ${fmtSplit({ agent: s.agentBps, protocol: s.protocolBps, guardian: s.guardianBps, owner: s.ownerBps })} on ProtocolConfig (${configAddress}). In-flight proposals keep their propose-time snapshot.`)
5412
5264
  );
5413
5265
  console.log(DIM4(` ${getExplorerUrl(hash)}`));
5414
5266
  } catch (err) {
5415
- spinner.fail("Failed to set protocol fee");
5267
+ spinner.fail("Failed to set performance-fee split");
5416
5268
  console.error(chalk10.red(formatContractError(err)));
5417
5269
  process.exit(1);
5418
5270
  }
@@ -7985,9 +7837,9 @@ try {
7985
7837
  } catch {
7986
7838
  }
7987
7839
  var require2 = createRequire(import.meta.url);
7988
- var CLI_VERSION = "0.86.0";
7840
+ var CLI_VERSION = "0.87.0";
7989
7841
  async function loadXmtp() {
7990
- return import("./xmtp-R25Q5GWY.js");
7842
+ return import("./xmtp-7LAWDKJT.js");
7991
7843
  }
7992
7844
  async function loadCron() {
7993
7845
  return import("./cron-E3UAZF5B.js");
@@ -8033,7 +7885,7 @@ program.name("sherwood").description("CLI for agent-managed onchain vaults").ver
8033
7885
  }).addOption(
8034
7886
  new Option(
8035
7887
  "--chain <network>",
8036
- "Target network (default: robinhood-fork, chain 9994663 \u2014 where the product lives; robinhood-testnet is chain 46630)"
7888
+ "Target network (default: robinhood-fork, chain 9994663 \u2014 where the product lives; robinhood-testnet is chain 46630 and still runs a PRE-V1 stack this CLI cannot talk to)"
8037
7889
  ).choices(VALID_NETWORKS).default(DEFAULT_NETWORK)
8038
7890
  ).option(
8039
7891
  "--calldata-only",
@@ -8658,7 +8510,7 @@ vaultCmd.command("add").description("Register an agent on a vault (creator only)
8658
8510
  agentId = BigInt(opts.agentId);
8659
8511
  } else {
8660
8512
  spinner.text = "Looking up ERC-8004 identity from wallet...";
8661
- const { lookupAgentIdByWallet } = await import("./identity-XOAYQFUW.js");
8513
+ const { lookupAgentIdByWallet } = await import("./identity-OLD25QEP.js");
8662
8514
  const resolved = await lookupAgentIdByWallet(agentWallet);
8663
8515
  if (resolved === null) {
8664
8516
  spinner.fail("Agent wallet does not own an ERC-8004 identity NFT");
@@ -9342,7 +9194,7 @@ vaultCmd.command("redeem").description("Redeem vault shares for the underlying a
9342
9194
  `));
9343
9195
  process.exit(1);
9344
9196
  }
9345
- const sharesDisplay = formatUnits9(shares, shareDecimals);
9197
+ const sharesDisplay = formatUnits10(shares, shareDecimals);
9346
9198
  const spinner = ora9(`Redeeming ${sharesDisplay} shares...`).start();
9347
9199
  try {
9348
9200
  const { hash, assetsOut } = await redeem(
@@ -9351,7 +9203,7 @@ vaultCmd.command("redeem").description("Redeem vault shares for the underlying a
9351
9203
  );
9352
9204
  spinner.succeed(`Redeemed: ${hash}`);
9353
9205
  console.log(chalk17.dim(` ${getExplorerUrl(hash)}`));
9354
- console.log(` Assets received: ${formatUnits9(assetsOut, assetDecimals)}`);
9206
+ console.log(` Assets received: ${formatUnits10(assetsOut, assetDecimals)}`);
9355
9207
  } catch (err) {
9356
9208
  spinner.fail("Redeem failed");
9357
9209
  console.error(chalk17.red(formatContractError(err)));
@@ -9361,7 +9213,7 @@ vaultCmd.command("redeem").description("Redeem vault shares for the underlying a
9361
9213
  var strategy = program.command("strategy").description("Strategy templates \u2014 list, clone, propose");
9362
9214
  registerStrategyTemplateCommands(strategy);
9363
9215
  program.command("providers").description("List available providers (research + swap routing)").action(async () => {
9364
- const { listProviders } = await import("./providers-BMOHHVUN.js");
9216
+ const { listProviders } = await import("./providers-KZNCCUKT.js");
9365
9217
  for (const info of listProviders()) {
9366
9218
  console.log(`
9367
9219
  ${info.name} (${info.type})`);
@@ -9371,7 +9223,7 @@ ${info.name} (${info.type})`);
9371
9223
  }
9372
9224
  });
9373
9225
  try {
9374
- const { registerChatCommands } = await import("./chat-PRYOT3ZV.js");
9226
+ const { registerChatCommands } = await import("./chat-OZAAN5LT.js");
9375
9227
  registerChatCommands(program);
9376
9228
  } catch {
9377
9229
  program.command("chat <name> [action] [actionArgs...]").description("Vault chat (XMTP) \u2014 requires @xmtp/cli").action(() => {
@@ -9381,7 +9233,7 @@ try {
9381
9233
  process.exit(1);
9382
9234
  });
9383
9235
  }
9384
- var { registerSessionCommands } = await import("./session-LJT3SAED.js");
9236
+ var { registerSessionCommands } = await import("./session-A7CYZSSA.js");
9385
9237
  registerSessionCommands(program);
9386
9238
  registerVeniceCommands(program);
9387
9239
  registerAllowanceCommands(program);
@@ -9390,11 +9242,11 @@ registerProposalCommands(program);
9390
9242
  registerQueueCommands(program);
9391
9243
  registerGovernorCommands(program);
9392
9244
  registerGuardianCommands(program);
9393
- var { registerResearchCommands } = await import("./research-NKGP3M4J.js");
9245
+ var { registerResearchCommands } = await import("./research-ARFBFXIT.js");
9394
9246
  registerResearchCommands(program);
9395
- var { registerAgentCommands } = await import("./agent-TBLC3WNJ.js");
9247
+ var { registerAgentCommands } = await import("./agent-A2JEYGQS.js");
9396
9248
  registerAgentCommands(program);
9397
- var { registerTradeCommands } = await import("./trade-OTZ7UHDK.js");
9249
+ var { registerTradeCommands } = await import("./trade-ZCK332E4.js");
9398
9250
  registerTradeCommands(program);
9399
9251
  registerGridCommand(program);
9400
9252
  var configCmd = program.command("config");