@sherwoodagent/cli 0.89.2 → 0.90.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 (59) hide show
  1. package/dist/{agent-K5ZKUS47.js → agent-QU2OSWS4.js} +4 -4
  2. package/dist/{chat-QVDOCCJN.js → chat-PFXIL6WT.js} +9 -9
  3. package/dist/{chunk-QEX2TWJV.js → chunk-2SIUEMPV.js} +2 -2
  4. package/dist/{chunk-TWJXGEBZ.js → chunk-5XMQQ4LX.js} +2 -2
  5. package/dist/{chunk-JLOROS5V.js → chunk-74QYNLJE.js} +2 -2
  6. package/dist/{chunk-VD65GJOY.js → chunk-DQZJULNP.js} +2 -2
  7. package/dist/{chunk-Z53JZORT.js → chunk-I7NYU4JB.js} +28 -4
  8. package/dist/chunk-I7NYU4JB.js.map +1 -0
  9. package/dist/{chunk-OYP35TFK.js → chunk-J3JMMJLN.js} +3 -3
  10. package/dist/{chunk-LVGYGXAV.js → chunk-KPTKX226.js} +2 -2
  11. package/dist/{chunk-TGWTRG3V.js → chunk-KRASHQRB.js} +6 -6
  12. package/dist/{chunk-VVCSD2M6.js → chunk-KXUA5BEH.js} +2 -2
  13. package/dist/{chunk-6IC2NRED.js → chunk-WIUPOQNS.js} +2 -2
  14. package/dist/{chunk-H6AJMPLT.js → chunk-XQHRKFFH.js} +49 -10
  15. package/dist/chunk-XQHRKFFH.js.map +1 -0
  16. package/dist/{chunk-MIW4CBSU.js → chunk-ZCRVHITI.js} +10 -10
  17. package/dist/{client-DA434B67.js → client-3BFK7TNL.js} +2 -2
  18. package/dist/{eas-F6KOWI4W.js → eas-YTAAJSLU.js} +3 -3
  19. package/dist/{governor-6HCXMRIA.js → governor-53PNPDCZ.js} +3 -3
  20. package/dist/{identity-2JEZE7DV.js → identity-M6UIVBNX.js} +5 -5
  21. package/dist/index.js +432 -218
  22. package/dist/index.js.map +1 -1
  23. package/dist/{launchpad-XM4546BO.js → launchpad-LHMBN4TV.js} +3 -3
  24. package/dist/{lighter-preflight-7J3IO27F.js → lighter-preflight-R7C6HY3M.js} +6 -6
  25. package/dist/{price-4FGWEPKX.js → price-PBFF6NFU.js} +4 -4
  26. package/dist/{providers-XEEFKITN.js → providers-22A42FKP.js} +3 -3
  27. package/dist/{research-CJOSSD4U.js → research-ZH34O7YL.js} +5 -5
  28. package/dist/{session-775NKMGH.js → session-GSWEE4M3.js} +7 -7
  29. package/dist/{trade-PCK3LONN.js → trade-SHK7GKSI.js} +10 -10
  30. package/dist/{vault-NLMVWYDF.js → vault-3T6UHCPE.js} +6 -6
  31. package/dist/{xmtp-H5GTOJ43.js → xmtp-EOMKXRTB.js} +3 -3
  32. package/package.json +1 -1
  33. package/dist/chunk-H6AJMPLT.js.map +0 -1
  34. package/dist/chunk-Z53JZORT.js.map +0 -1
  35. /package/dist/{agent-K5ZKUS47.js.map → agent-QU2OSWS4.js.map} +0 -0
  36. /package/dist/{chat-QVDOCCJN.js.map → chat-PFXIL6WT.js.map} +0 -0
  37. /package/dist/{chunk-QEX2TWJV.js.map → chunk-2SIUEMPV.js.map} +0 -0
  38. /package/dist/{chunk-TWJXGEBZ.js.map → chunk-5XMQQ4LX.js.map} +0 -0
  39. /package/dist/{chunk-JLOROS5V.js.map → chunk-74QYNLJE.js.map} +0 -0
  40. /package/dist/{chunk-VD65GJOY.js.map → chunk-DQZJULNP.js.map} +0 -0
  41. /package/dist/{chunk-OYP35TFK.js.map → chunk-J3JMMJLN.js.map} +0 -0
  42. /package/dist/{chunk-LVGYGXAV.js.map → chunk-KPTKX226.js.map} +0 -0
  43. /package/dist/{chunk-TGWTRG3V.js.map → chunk-KRASHQRB.js.map} +0 -0
  44. /package/dist/{chunk-VVCSD2M6.js.map → chunk-KXUA5BEH.js.map} +0 -0
  45. /package/dist/{chunk-6IC2NRED.js.map → chunk-WIUPOQNS.js.map} +0 -0
  46. /package/dist/{chunk-MIW4CBSU.js.map → chunk-ZCRVHITI.js.map} +0 -0
  47. /package/dist/{client-DA434B67.js.map → client-3BFK7TNL.js.map} +0 -0
  48. /package/dist/{eas-F6KOWI4W.js.map → eas-YTAAJSLU.js.map} +0 -0
  49. /package/dist/{governor-6HCXMRIA.js.map → governor-53PNPDCZ.js.map} +0 -0
  50. /package/dist/{identity-2JEZE7DV.js.map → identity-M6UIVBNX.js.map} +0 -0
  51. /package/dist/{launchpad-XM4546BO.js.map → launchpad-LHMBN4TV.js.map} +0 -0
  52. /package/dist/{lighter-preflight-7J3IO27F.js.map → lighter-preflight-R7C6HY3M.js.map} +0 -0
  53. /package/dist/{price-4FGWEPKX.js.map → price-PBFF6NFU.js.map} +0 -0
  54. /package/dist/{providers-XEEFKITN.js.map → providers-22A42FKP.js.map} +0 -0
  55. /package/dist/{research-CJOSSD4U.js.map → research-ZH34O7YL.js.map} +0 -0
  56. /package/dist/{session-775NKMGH.js.map → session-GSWEE4M3.js.map} +0 -0
  57. /package/dist/{trade-PCK3LONN.js.map → trade-SHK7GKSI.js.map} +0 -0
  58. /package/dist/{vault-NLMVWYDF.js.map → vault-3T6UHCPE.js.map} +0 -0
  59. /package/dist/{xmtp-H5GTOJ43.js.map → xmtp-EOMKXRTB.js.map} +0 -0
package/dist/index.js CHANGED
@@ -15,14 +15,17 @@ import {
15
15
  checkApiKeyValid,
16
16
  listModels,
17
17
  provisionApiKey
18
- } from "./chunk-JLOROS5V.js";
18
+ } from "./chunk-74QYNLJE.js";
19
19
  import {
20
20
  createFund,
21
21
  getActiveFunds,
22
22
  getFund,
23
23
  subdomainExists,
24
24
  updateMetadata
25
- } from "./chunk-VD65GJOY.js";
25
+ } from "./chunk-DQZJULNP.js";
26
+ import {
27
+ addLaunchpadTemplateOptions
28
+ } from "./chunk-KXUA5BEH.js";
26
29
  import {
27
30
  FORK_UNOBSERVABLE_REASON,
28
31
  LIGHTER_API_PROFILES,
@@ -54,8 +57,8 @@ import {
54
57
  removeDepositor,
55
58
  setAgentFeeBps,
56
59
  setVaultAddress
57
- } from "./chunk-OYP35TFK.js";
58
- import "./chunk-TGWTRG3V.js";
60
+ } from "./chunk-J3JMMJLN.js";
61
+ import "./chunk-KRASHQRB.js";
59
62
  import {
60
63
  CL_DEFAULTS,
61
64
  DEFAULT_FEE_TIER,
@@ -89,7 +92,22 @@ import {
89
92
  selectBestFeeTier,
90
93
  twapTickFromCumulatives,
91
94
  v3SingleHopExtraData
92
- } from "./chunk-MIW4CBSU.js";
95
+ } from "./chunk-ZCRVHITI.js";
96
+ import {
97
+ CLAIM_SETTLE_BUFFER_SECONDS,
98
+ DEFAULT_DEADLINE_MARGIN_SECONDS,
99
+ LaunchpadInputError,
100
+ buildLaunchpadInitParams,
101
+ decideLaunchDrawdown,
102
+ defaultMaxFeeIn,
103
+ liveFeeCost,
104
+ parseAmountFlag,
105
+ parseDeadline,
106
+ parseDurationSeconds,
107
+ parseSettleSlippageBps,
108
+ parseVenue,
109
+ sameAddr
110
+ } from "./chunk-T5NA6KOF.js";
93
111
  import {
94
112
  DEFAULT_ISSUING_CHAIN_ID,
95
113
  STRATEGY_FACTORY_ABI,
@@ -109,6 +127,7 @@ import {
109
127
  encodeJoinRequest,
110
128
  encodeMgmtSplit,
111
129
  encodePerfSplit,
130
+ encodePrepareOwnerStake,
112
131
  encodePropose,
113
132
  encodeRedeem,
114
133
  encodeRegisterAgent,
@@ -123,12 +142,12 @@ import {
123
142
  registerIdentityCommands,
124
143
  stripVidTag,
125
144
  verifyVidTag
126
- } from "./chunk-H6AJMPLT.js";
145
+ } from "./chunk-XQHRKFFH.js";
127
146
  import {
128
- fetchMetadata,
129
- mergeFundMetadata,
130
- uploadMetadata
131
- } from "./chunk-YMB4RJOR.js";
147
+ emitCalldata,
148
+ isCalldataOnly,
149
+ setCalldataOnly
150
+ } from "./chunk-GAQMHH44.js";
132
151
  import {
133
152
  createApproval,
134
153
  createJoinRequest,
@@ -138,13 +157,13 @@ import {
138
157
  queryJoinRequests,
139
158
  revokeJoinRequest,
140
159
  stripReferrerPrefix
141
- } from "./chunk-QEX2TWJV.js";
160
+ } from "./chunk-2SIUEMPV.js";
142
161
  import {
143
162
  resolveFund,
144
163
  resolveVaultFund,
145
164
  searchFundAcrossChains,
146
165
  setTextRecord
147
- } from "./chunk-LVGYGXAV.js";
166
+ } from "./chunk-KPTKX226.js";
148
167
  import {
149
168
  PROPOSAL_STATE,
150
169
  PROPOSAL_STATES,
@@ -183,50 +202,7 @@ import {
183
202
  setVotingPeriod,
184
203
  settleProposal,
185
204
  vote
186
- } from "./chunk-TWJXGEBZ.js";
187
- import {
188
- LIGHTER_DEFAULT_API_KEY_INDEX,
189
- LIGHTER_MAX_DEPOSIT,
190
- acknowledgeShortfallCalldata,
191
- buildPostSettleRecoveryCalls,
192
- deriveBurnPubKey,
193
- encodeCancelAll,
194
- encodeClosePosition,
195
- encodeRotateKey,
196
- guardrailActionCalldata,
197
- initiateReturnCalldata,
198
- isLighterPubKeyOnCurve,
199
- queueWithdrawCalldata,
200
- recoverResidualsCalldata,
201
- registerAgentKeyCalldata,
202
- usdgToTicksFloor,
203
- validateLighterKeyIndex,
204
- validateLighterMarket,
205
- validateLighterPubKey
206
- } from "./chunk-TCUSM4V7.js";
207
- import {
208
- addLaunchpadTemplateOptions
209
- } from "./chunk-VVCSD2M6.js";
210
- import {
211
- CLAIM_SETTLE_BUFFER_SECONDS,
212
- DEFAULT_DEADLINE_MARGIN_SECONDS,
213
- LaunchpadInputError,
214
- buildLaunchpadInitParams,
215
- decideLaunchDrawdown,
216
- defaultMaxFeeIn,
217
- liveFeeCost,
218
- parseAmountFlag,
219
- parseDeadline,
220
- parseDurationSeconds,
221
- parseSettleSlippageBps,
222
- parseVenue,
223
- sameAddr
224
- } from "./chunk-T5NA6KOF.js";
225
- import {
226
- emitCalldata,
227
- isCalldataOnly,
228
- setCalldataOnly
229
- } from "./chunk-GAQMHH44.js";
205
+ } from "./chunk-5XMQQ4LX.js";
230
206
  import {
231
207
  AERODROME,
232
208
  LAUNCH_ADAPTERS,
@@ -247,7 +223,7 @@ import {
247
223
  sendTxWithRetry,
248
224
  waitForReceipt,
249
225
  writeContractWithRetry
250
- } from "./chunk-Z53JZORT.js";
226
+ } from "./chunk-I7NYU4JB.js";
251
227
  import {
252
228
  DEFAULT_NETWORK,
253
229
  VALID_NETWORKS,
@@ -284,6 +260,26 @@ import {
284
260
  setUniswapApiKey,
285
261
  setVeniceApiKey
286
262
  } from "./chunk-QUAVAJWE.js";
263
+ import {
264
+ LIGHTER_DEFAULT_API_KEY_INDEX,
265
+ LIGHTER_MAX_DEPOSIT,
266
+ acknowledgeShortfallCalldata,
267
+ buildPostSettleRecoveryCalls,
268
+ deriveBurnPubKey,
269
+ encodeCancelAll,
270
+ encodeClosePosition,
271
+ encodeRotateKey,
272
+ guardrailActionCalldata,
273
+ initiateReturnCalldata,
274
+ isLighterPubKeyOnCurve,
275
+ queueWithdrawCalldata,
276
+ recoverResidualsCalldata,
277
+ registerAgentKeyCalldata,
278
+ usdgToTicksFloor,
279
+ validateLighterKeyIndex,
280
+ validateLighterMarket,
281
+ validateLighterPubKey
282
+ } from "./chunk-TCUSM4V7.js";
287
283
  import {
288
284
  BASE_STRATEGY_ABI,
289
285
  ERC20_ABI,
@@ -297,6 +293,11 @@ import {
297
293
  SYNDICATE_VAULT_ABI,
298
294
  VENICE_STAKING_ABI
299
295
  } from "./chunk-SCWNKVNW.js";
296
+ import {
297
+ fetchMetadata,
298
+ mergeFundMetadata,
299
+ uploadMetadata
300
+ } from "./chunk-YMB4RJOR.js";
300
301
 
301
302
  // src/index.ts
302
303
  import { config as loadDotenv } from "dotenv";
@@ -522,7 +523,7 @@ var HYPERLIQUID_GRID_STRATEGY_ABI = [
522
523
  ];
523
524
 
524
525
  // src/commands/strategy-template-lib.ts
525
- import { parseUnits as parseUnits3, formatUnits as formatUnits4, isAddress as isAddress4, erc20Abi as erc20Abi3 } from "viem";
526
+ import { parseUnits as parseUnits3, formatUnits as formatUnits4, isAddress as isAddress4, erc20Abi as erc20Abi3, encodeFunctionData, maxUint256 } from "viem";
526
527
  import chalk3 from "chalk";
527
528
 
528
529
  // src/lib/proposer-bond-error.ts
@@ -560,7 +561,8 @@ var STAKED_WOOD_ABI = [
560
561
  { type: "function", name: "claimUnstakeGuardian", inputs: [], outputs: [], stateMutability: "nonpayable" },
561
562
  // Views
562
563
  { type: "function", name: "guardianStake", inputs: [{ type: "address" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
563
- { type: "function", name: "isActiveGuardian", inputs: [{ type: "address" }], outputs: [{ type: "bool" }], stateMutability: "view" }
564
+ { type: "function", name: "isActiveGuardian", inputs: [{ type: "address" }], outputs: [{ type: "bool" }], stateMutability: "view" },
565
+ { type: "function", name: "minOwnerStake", inputs: [], outputs: [{ type: "uint256" }], stateMutability: "view" }
564
566
  ];
565
567
  var ERC20_ABI2 = [
566
568
  { type: "function", name: "approve", inputs: [{ type: "address" }, { type: "uint256" }], outputs: [{ type: "bool" }], stateMutability: "nonpayable" },
@@ -692,6 +694,19 @@ function registerGuardianCommands(program2) {
692
694
  "Bond WOOD as a prospective vault owner \u2014 required before `vault create`. Amount in WOOD (min = swood.minOwnerStake, default 10000)."
693
695
  ).action(async (amountStr) => {
694
696
  const amount = parseUnits(amountStr, 18);
697
+ if (isCalldataOnly()) {
698
+ const min = await getPublicClient().readContract({
699
+ address: swood(),
700
+ abi: STAKED_WOOD_ABI,
701
+ functionName: "minOwnerStake"
702
+ });
703
+ if (amount < min) {
704
+ console.error(chalk.red(`Owner stake must be at least ${formatUnits2(min, 18)} WOOD (StakedWood.minOwnerStake).`));
705
+ process.exit(1);
706
+ }
707
+ emitCalldata(encodePrepareOwnerStake({ amount }, getChain().id));
708
+ return;
709
+ }
695
710
  await ensureWoodAllowance(amount);
696
711
  const wallet = getWalletClient();
697
712
  const pc = getPublicClient();
@@ -1805,84 +1820,120 @@ function capsForCalls(calls, strategy2, maxCapital) {
1805
1820
  return seen === 1 ? share + remainder : share;
1806
1821
  });
1807
1822
  }
1808
- async function ensureProposerBondAllowance(vault, requiredCoverage, asset) {
1823
+ async function pinProposalMetadata(meta) {
1824
+ const { pinJSON } = await import("./ipfs-M56BLMCW.js");
1809
1825
  try {
1810
- const publicClient = getPublicClient();
1811
- const governor = await getGovernorOf(vault);
1812
- const govViews = [
1813
- {
1814
- type: "function",
1815
- name: "bondEscrow",
1816
- inputs: [],
1817
- outputs: [{ type: "address" }],
1818
- stateMutability: "view"
1819
- },
1820
- {
1821
- type: "function",
1822
- name: "exposureLedger",
1823
- inputs: [],
1824
- outputs: [{ type: "address" }],
1825
- stateMutability: "view"
1826
- }
1827
- ];
1828
- const escrow = await publicClient.readContract({
1826
+ const uri = await pinJSON(meta, meta.name);
1827
+ console.error(chalk3.dim(` Metadata pinned: ${uri}`));
1828
+ return uri;
1829
+ } catch {
1830
+ console.error(
1831
+ chalk3.yellow(" IPFS pin failed \u2014 using inline metadata. The app renders only ipfs:// metadata; pass --metadata-uri to override.")
1832
+ );
1833
+ const inline = JSON.stringify({ ...meta, createdBy: "@sherwoodagent/cli" });
1834
+ return `data:application/json;base64,${Buffer.from(inline).toString("base64")}`;
1835
+ }
1836
+ }
1837
+ var BOND_GOVERNOR_VIEWS = [
1838
+ { type: "function", name: "bondEscrow", inputs: [], outputs: [{ type: "address" }], stateMutability: "view" },
1839
+ { type: "function", name: "exposureLedger", inputs: [], outputs: [{ type: "address" }], stateMutability: "view" }
1840
+ ];
1841
+ var PROPOSER_BOND_WOOD_ABI = [
1842
+ {
1843
+ type: "function",
1844
+ name: "proposerBondWood",
1845
+ inputs: [
1846
+ { name: "asset", type: "address" },
1847
+ { name: "requiredCoverage", type: "uint256" }
1848
+ ],
1849
+ outputs: [{ type: "uint256" }],
1850
+ stateMutability: "view"
1851
+ }
1852
+ ];
1853
+ async function quoteProposerBond(vault, requiredCoverage, asset) {
1854
+ const publicClient = getPublicClient();
1855
+ const governor = await getGovernorOf(vault);
1856
+ const escrow = await publicClient.readContract({
1857
+ address: governor,
1858
+ abi: BOND_GOVERNOR_VIEWS,
1859
+ functionName: "bondEscrow"
1860
+ });
1861
+ if (!escrow || escrow === ZERO2) return null;
1862
+ let quotedBond = 0n;
1863
+ try {
1864
+ const ledger = await publicClient.readContract({
1829
1865
  address: governor,
1830
- abi: govViews,
1831
- functionName: "bondEscrow"
1866
+ abi: BOND_GOVERNOR_VIEWS,
1867
+ functionName: "exposureLedger"
1832
1868
  });
1833
- if (!escrow || escrow === ZERO2) return;
1834
- let quotedBond = 0n;
1835
- try {
1836
- const ledger = await publicClient.readContract({
1837
- address: governor,
1838
- abi: govViews,
1839
- functionName: "exposureLedger"
1869
+ if (ledger && ledger !== ZERO2 && requiredCoverage > 0n) {
1870
+ quotedBond = await publicClient.readContract({
1871
+ address: ledger,
1872
+ abi: PROPOSER_BOND_WOOD_ABI,
1873
+ functionName: "proposerBondWood",
1874
+ args: [asset, requiredCoverage]
1840
1875
  });
1841
- if (ledger && ledger !== ZERO2 && requiredCoverage > 0n) {
1842
- quotedBond = await publicClient.readContract({
1843
- address: ledger,
1844
- abi: [
1845
- {
1846
- type: "function",
1847
- name: "proposerBondWood",
1848
- inputs: [
1849
- { name: "asset", type: "address" },
1850
- { name: "requiredCoverage", type: "uint256" }
1851
- ],
1852
- outputs: [{ type: "uint256" }],
1853
- stateMutability: "view"
1854
- }
1855
- ],
1856
- functionName: "proposerBondWood",
1857
- args: [asset, requiredCoverage]
1858
- });
1859
- }
1860
- } catch {
1861
- }
1862
- console.log(
1863
- chalk3.dim(
1864
- ` Quoted proposer bond: ${formatUnits4(quotedBond, 18)} WOOD (via ExposureLedger.proposerBondWood; ~1% of extractable value at default proposerBondBps, WOOD-denominated and price-dependent \u2014 not a fixed WOOD amount)`
1865
- )
1866
- );
1867
- console.log(chalk3.dim(` Proposer bond escrow: ${escrow} \u2014 ensuring WOOD allowance`));
1868
- if (quotedBond > 0n) {
1869
- const wood2 = SHERWOOD().WOOD_TOKEN;
1870
- const holder = getAccount().address;
1871
- const balance = await publicClient.readContract({
1872
- address: wood2,
1873
- abi: erc20Abi3,
1874
- functionName: "balanceOf",
1875
- args: [holder]
1876
- });
1877
- if (balance < quotedBond) {
1878
- throw new InsufficientProposerBondWood(quotedBond, balance);
1879
- }
1880
1876
  }
1881
- await ensureWoodAllowance(2n ** 255n, escrow, "WOOD (proposer bond)");
1877
+ } catch {
1878
+ }
1879
+ return { escrow, quotedBond };
1880
+ }
1881
+ function bondQuoteLines(quote) {
1882
+ return [
1883
+ ` Quoted proposer bond: ${formatUnits4(quote.quotedBond, 18)} WOOD (via ExposureLedger.proposerBondWood; ~1% of extractable value at default proposerBondBps, WOOD-denominated and price-dependent \u2014 not a fixed WOOD amount)`,
1884
+ ` Proposer bond escrow: ${quote.escrow}`
1885
+ ];
1886
+ }
1887
+ async function requireBondWood(holder, quotedBond) {
1888
+ if (quotedBond === 0n) return;
1889
+ const balance = await getPublicClient().readContract({
1890
+ address: SHERWOOD().WOOD_TOKEN,
1891
+ abi: erc20Abi3,
1892
+ functionName: "balanceOf",
1893
+ args: [holder]
1894
+ });
1895
+ if (balance < quotedBond) throw new InsufficientProposerBondWood(quotedBond, balance);
1896
+ }
1897
+ async function ensureProposerBondAllowance(vault, requiredCoverage, asset) {
1898
+ try {
1899
+ const quote = await quoteProposerBond(vault, requiredCoverage, asset);
1900
+ if (!quote) return;
1901
+ for (const line of bondQuoteLines(quote)) console.log(chalk3.dim(line));
1902
+ await requireBondWood(getAccount().address, quote.quotedBond);
1903
+ await ensureWoodAllowance(2n ** 255n, quote.escrow, "WOOD (proposer bond)");
1882
1904
  } catch (err) {
1883
1905
  if (err instanceof InsufficientProposerBondWood) throw err;
1884
1906
  }
1885
1907
  }
1908
+ async function proposerBondApprovalTxs(vault, requiredCoverage, asset, proposer, chainId) {
1909
+ let quote;
1910
+ try {
1911
+ quote = await quoteProposerBond(vault, requiredCoverage, asset);
1912
+ } catch {
1913
+ return [];
1914
+ }
1915
+ if (!quote) return [];
1916
+ for (const line of bondQuoteLines(quote)) console.error(chalk3.dim(line));
1917
+ const wood2 = SHERWOOD().WOOD_TOKEN;
1918
+ if (proposer) {
1919
+ await requireBondWood(proposer, quote.quotedBond);
1920
+ const allowance = await getPublicClient().readContract({
1921
+ address: wood2,
1922
+ abi: erc20Abi3,
1923
+ functionName: "allowance",
1924
+ args: [proposer, quote.escrow]
1925
+ });
1926
+ if (allowance >= 2n ** 255n) return [];
1927
+ }
1928
+ return [
1929
+ {
1930
+ to: wood2,
1931
+ data: encodeFunctionData({ abi: erc20Abi3, functionName: "approve", args: [quote.escrow, maxUint256] }),
1932
+ value: "0x0",
1933
+ chainId
1934
+ }
1935
+ ];
1936
+ }
1886
1937
 
1887
1938
  // src/commands/strategy-propose.ts
1888
1939
  import { formatUnits as formatUnits5, isAddress as isAddress5, erc20Abi as erc20Abi4 } from "viem";
@@ -1919,7 +1970,7 @@ function registerStrategyProposeCommands(strategy2) {
1919
1970
  if (isCalldataOnly()) {
1920
1971
  const missing = [];
1921
1972
  if (!opts.proposer) missing.push("--proposer <address> (the external signer)");
1922
- if (!opts.metadataUri) missing.push("--metadata-uri <uri> (IPFS pin needs a signer)");
1973
+ if (!opts.metadataUri && !opts.name) missing.push("--name <name> (or --metadata-uri <uri>)");
1923
1974
  if (!opts.duration) missing.push("--duration <d>");
1924
1975
  if (missing.length > 0) {
1925
1976
  console.error(chalk4.red(`--calldata-only requires: ${missing.join(", ")}`));
@@ -1984,7 +2035,7 @@ function registerStrategyProposeCommands(strategy2) {
1984
2035
  }
1985
2036
  }
1986
2037
  if (templateKey === "lighter-perp") {
1987
- const { runLighterProposePreflight } = await import("./lighter-preflight-7J3IO27F.js");
2038
+ const { runLighterProposePreflight } = await import("./lighter-preflight-R7C6HY3M.js");
1988
2039
  await runLighterProposePreflight(vault, opts, maxDrawdownBps);
1989
2040
  }
1990
2041
  } catch (err) {
@@ -1992,7 +2043,7 @@ function registerStrategyProposeCommands(strategy2) {
1992
2043
  process.exit(1);
1993
2044
  }
1994
2045
  if (!opts.agent) opts.agent = proposer;
1995
- const { parseDuration: parseDuration3, getGovernorOf: getGovernorOf2 } = await import("./governor-6HCXMRIA.js");
2046
+ const { parseDuration: parseDuration3, getGovernorOf: getGovernorOf2 } = await import("./governor-53PNPDCZ.js");
1996
2047
  const strategyDuration2 = parseDuration3(opts.duration);
1997
2048
  const chainId = getChain().id;
1998
2049
  try {
@@ -2015,12 +2066,23 @@ function registerStrategyProposeCommands(strategy2) {
2015
2066
  built.assetAmount,
2016
2067
  built.extraApprovals
2017
2068
  );
2069
+ const metadataURI2 = opts.metadataUri || await pinProposalMetadata({
2070
+ name: opts.name,
2071
+ description: opts.description || "",
2072
+ proposer,
2073
+ vault,
2074
+ strategyClone: deploy.clone,
2075
+ template: def.key,
2076
+ strategyDuration: Number(strategyDuration2),
2077
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2078
+ });
2079
+ const bondTxs = await proposerBondApprovalTxs(vault, built.assetAmount, built.asset, proposer, chainId);
2018
2080
  const proposeAction = encodePropose(
2019
2081
  {
2020
2082
  vault,
2021
2083
  governor,
2022
2084
  strategy: deploy.clone,
2023
- metadataURI: opts.metadataUri,
2085
+ metadataURI: metadataURI2,
2024
2086
  strategyDuration: strategyDuration2,
2025
2087
  // Same envelope + cap shape the broadcasting path uses, so the
2026
2088
  // emitted calldata is the transaction that path would send.
@@ -2033,14 +2095,14 @@ function registerStrategyProposeCommands(strategy2) {
2033
2095
  chainId
2034
2096
  );
2035
2097
  emitCalldata({
2036
- txs: [...deploy.txs, ...proposeAction.txs],
2098
+ txs: [...bondTxs, ...deploy.txs, ...proposeAction.txs],
2037
2099
  preconditions: [...deploy.preconditions, ...proposeAction.preconditions],
2038
2100
  // Surface the deterministic clone + salt as structured fields (also
2039
2101
  // baked into tx 2's `strategy` arg + the execute/settle calls).
2040
2102
  clone: deploy.clone,
2041
2103
  salt: deploy.salt,
2042
- description: `Keyless ${def.name} proposal on ${vault}: (1) deploy clone at ${deploy.clone} (deterministic CREATE2, salt ${deploy.salt}); (2) governor.propose referencing it.`,
2043
- note: "Broadcast SEQUENTIALLY: send tx 1 (cloneAndInitDeterministic) and WAIT for it to confirm, then send tx 2 (propose). If tx 1 reverts \u2014 proposer not a registered agent, proposer != tx-1 signer, template not allowlisted, or vault unregistered \u2014 do NOT send tx 2: it would create a proposal referencing an undeployed strategy. Send tx 1 from the --proposer wallet (the factory requires proposer == msg.sender, and msg.sender must be a vault owner/agent). The clone address is pinned by (factory, template, vault, salt) and is already baked into tx 2 + the execute/settle calls. The factory binds the salt to the vault and only the vault's owner/agents may deploy there, so the predicted address can't be front-run."
2104
+ description: `Keyless ${def.name} proposal on ${vault}: ` + (bondTxs.length ? "WOOD.approve(proposer-bond escrow); " : "") + `deploy clone at ${deploy.clone} (deterministic CREATE2, salt ${deploy.salt}); governor.propose referencing it.`,
2105
+ note: "Broadcast SEQUENTIALLY from the --proposer wallet, waiting for each tx to confirm before the next. " + (bondTxs.length ? "The first tx approves WOOD to the proposer-bond escrow \u2014 propose pulls the bond from the proposer and reverts without it. " : "") + "If cloneAndInitDeterministic reverts \u2014 proposer not a registered agent, proposer != signer, template not allowlisted, or vault unregistered \u2014 do NOT send propose: it would create a proposal referencing an undeployed strategy. The factory requires proposer == msg.sender, and msg.sender must be a vault owner/agent. The clone address is pinned by (factory, template, vault, salt) and is already baked into the propose tx + the execute/settle calls. The factory binds the salt to the vault and only the vault's owner/agents may deploy there, so the predicted address can't be front-run."
2044
2106
  });
2045
2107
  } catch (err) {
2046
2108
  console.error(chalk4.red(err instanceof Error ? err.message : String(err)));
@@ -2110,7 +2172,7 @@ function registerStrategyProposeCommands(strategy2) {
2110
2172
  }
2111
2173
  }
2112
2174
  if (templateKey === "lighter-perp") {
2113
- const { runLighterProposePreflight } = await import("./lighter-preflight-7J3IO27F.js");
2175
+ const { runLighterProposePreflight } = await import("./lighter-preflight-R7C6HY3M.js");
2114
2176
  await runLighterProposePreflight(vault, opts, maxDrawdownBps);
2115
2177
  }
2116
2178
  preflightSpinner.succeed("Preflight OK");
@@ -2227,9 +2289,9 @@ function registerStrategyProposeCommands(strategy2) {
2227
2289
  console.error(chalk4.red("Missing --name or --duration. Use --write-calls to skip proposal submission."));
2228
2290
  process.exit(1);
2229
2291
  }
2230
- const { propose: propose2 } = await import("./governor-6HCXMRIA.js");
2292
+ const { propose: propose2 } = await import("./governor-53PNPDCZ.js");
2231
2293
  const { pinJSON } = await import("./ipfs-M56BLMCW.js");
2232
- const { parseDuration: parseDuration2 } = await import("./governor-6HCXMRIA.js");
2294
+ const { parseDuration: parseDuration2 } = await import("./governor-53PNPDCZ.js");
2233
2295
  const strategyDuration = parseDuration2(opts.duration);
2234
2296
  const metaSpinner = ora2("Pinning metadata to IPFS...").start();
2235
2297
  let metadataURI;
@@ -2261,7 +2323,7 @@ function registerStrategyProposeCommands(strategy2) {
2261
2323
  metadataURI = `data:application/json;base64,${Buffer.from(inline).toString("base64")}`;
2262
2324
  }
2263
2325
  try {
2264
- const { getAgentFeeBps: getAgentFeeBps2 } = await import("./vault-NLMVWYDF.js");
2326
+ const { getAgentFeeBps: getAgentFeeBps2 } = await import("./vault-3T6UHCPE.js");
2265
2327
  const feeBps = await getAgentFeeBps2(vault);
2266
2328
  console.log(
2267
2329
  chalk4.yellow(
@@ -2312,7 +2374,7 @@ function registerStrategyProposeCommands(strategy2) {
2312
2374
  console.log(chalk4.yellow(` asset.approve(${clone}, <repaymentAmount>)`));
2313
2375
  }
2314
2376
  if (templateKey === "lighter-perp") {
2315
- const { lighterNextSteps } = await import("./lighter-preflight-7J3IO27F.js");
2377
+ const { lighterNextSteps } = await import("./lighter-preflight-R7C6HY3M.js");
2316
2378
  console.log();
2317
2379
  for (const line of lighterNextSteps(clone)) console.log(chalk4.yellow(line));
2318
2380
  }
@@ -2584,7 +2646,7 @@ function registerStrategyTemplateCommands(strategy2) {
2584
2646
  let prices = [];
2585
2647
  let priceSource = null;
2586
2648
  try {
2587
- const { getTokenPricesInAsset } = await import("./price-4FGWEPKX.js");
2649
+ const { getTokenPricesInAsset } = await import("./price-PBFF6NFU.js");
2588
2650
  prices = await getTokenPricesInAsset({
2589
2651
  tokens: allocations.map((a, i) => ({
2590
2652
  token: a.token,
@@ -3001,7 +3063,7 @@ function registerVeniceCommands(program2) {
3001
3063
  console.log(chalk6.dim(` Key: ${apiKey.slice(0, 8)}...${apiKey.slice(-4)}`));
3002
3064
  console.log(chalk6.dim(" Saved to ~/.sherwood/config.json"));
3003
3065
  try {
3004
- const { createVeniceProvisionAttestation, getEasScanUrl } = await import("./eas-F6KOWI4W.js");
3066
+ const { createVeniceProvisionAttestation, getEasScanUrl } = await import("./eas-YTAAJSLU.js");
3005
3067
  const attestation = await createVeniceProvisionAttestation(account.address);
3006
3068
  if (attestation) {
3007
3069
  console.log(chalk6.dim(` Attested: ${getEasScanUrl(attestation.uid)}`));
@@ -3357,7 +3419,7 @@ ${opts.prompt}`;
3357
3419
  console.log(chalk6.dim(`Model: ${result.model} | Tokens: ${result.usage.promptTokens} in, ${result.usage.completionTokens} out, ${result.usage.totalTokens} total`));
3358
3420
  }
3359
3421
  try {
3360
- const { createVeniceInferenceAttestation, getEasScanUrl } = await import("./eas-F6KOWI4W.js");
3422
+ const { createVeniceInferenceAttestation, getEasScanUrl } = await import("./eas-YTAAJSLU.js");
3361
3423
  const { keccak256: keccak2562, toHex, isAddress: isAddr } = await import("viem");
3362
3424
  const { getChainContracts: getChainContracts2 } = await import("./config-UZ2GIXAO.js");
3363
3425
  const { getChain: getActiveChain } = await import("./network-EZ5X2BF7.js");
@@ -3389,7 +3451,7 @@ import chalk7 from "chalk";
3389
3451
  import ora5 from "ora";
3390
3452
 
3391
3453
  // src/strategies/allowance-disburse.ts
3392
- import { encodeFunctionData, parseUnits as parseUnits7 } from "viem";
3454
+ import { encodeFunctionData as encodeFunctionData2, parseUnits as parseUnits7 } from "viem";
3393
3455
  function buildDisburseBatch(config, vaultAddress, agents, assetAddress, assetDecimals2, minUsdc, swapPath) {
3394
3456
  const assetAmount = parseUnits7(config.amount, assetDecimals2);
3395
3457
  const isUsdc = assetAddress.toLowerCase() === TOKENS().USDC.toLowerCase();
@@ -3398,7 +3460,7 @@ function buildDisburseBatch(config, vaultAddress, agents, assetAddress, assetDec
3398
3460
  if (!isUsdc) {
3399
3461
  calls.push({
3400
3462
  target: assetAddress,
3401
- data: encodeFunctionData({
3463
+ data: encodeFunctionData2({
3402
3464
  abi: ERC20_ABI,
3403
3465
  functionName: "approve",
3404
3466
  args: [UNISWAP().SWAP_ROUTER, assetAmount]
@@ -3408,7 +3470,7 @@ function buildDisburseBatch(config, vaultAddress, agents, assetAddress, assetDec
3408
3470
  if (isWeth) {
3409
3471
  calls.push({
3410
3472
  target: UNISWAP().SWAP_ROUTER,
3411
- data: encodeFunctionData({
3473
+ data: encodeFunctionData2({
3412
3474
  abi: SWAP_ROUTER_EXACT_INPUT_SINGLE_ABI,
3413
3475
  functionName: "exactInputSingle",
3414
3476
  args: [
@@ -3428,7 +3490,7 @@ function buildDisburseBatch(config, vaultAddress, agents, assetAddress, assetDec
3428
3490
  } else {
3429
3491
  calls.push({
3430
3492
  target: UNISWAP().SWAP_ROUTER,
3431
- data: encodeFunctionData({
3493
+ data: encodeFunctionData2({
3432
3494
  abi: SWAP_ROUTER_ABI,
3433
3495
  functionName: "exactInput",
3434
3496
  args: [
@@ -3448,7 +3510,7 @@ function buildDisburseBatch(config, vaultAddress, agents, assetAddress, assetDec
3448
3510
  for (const agent of agents) {
3449
3511
  calls.push({
3450
3512
  target: TOKENS().USDC,
3451
- data: encodeFunctionData({
3513
+ data: encodeFunctionData2({
3452
3514
  abi: ERC20_ABI,
3453
3515
  functionName: "transfer",
3454
3516
  args: [agent, perAgent]
@@ -3658,13 +3720,13 @@ function registerAllowanceCommands(program2) {
3658
3720
  }
3659
3721
 
3660
3722
  // src/commands/proposal.ts
3661
- import { isAddress as isAddress9, encodeFunctionData as encodeFunctionData3, formatUnits as formatUnits9, parseUnits as parseUnits9 } from "viem";
3723
+ import { isAddress as isAddress9, encodeFunctionData as encodeFunctionData4, formatUnits as formatUnits9, parseUnits as parseUnits9 } from "viem";
3662
3724
  import chalk9 from "chalk";
3663
3725
  import ora6 from "ora";
3664
3726
  import { readFileSync as readFileSync2 } from "fs";
3665
3727
 
3666
3728
  // src/lib/simulate.ts
3667
- import { decodeFunctionData as decodeFunctionData2, encodeFunctionData as encodeFunctionData2 } from "viem";
3729
+ import { decodeFunctionData as decodeFunctionData2, encodeFunctionData as encodeFunctionData3 } from "viem";
3668
3730
  import chalk8 from "chalk";
3669
3731
 
3670
3732
  // src/lib/risk.ts
@@ -4224,7 +4286,7 @@ async function simulateViaEthCall(vault, calls) {
4224
4286
  const governor = await getGovernorOf(vault);
4225
4287
  const client = getPublicClient();
4226
4288
  const uncapped = calls.map(() => MAX_UINT256);
4227
- const input2 = encodeFunctionData2({
4289
+ const input2 = encodeFunctionData3({
4228
4290
  abi: SYNDICATE_VAULT_ABI,
4229
4291
  functionName: "executeGovernorBatch",
4230
4292
  args: [
@@ -4356,8 +4418,8 @@ function printSimulationResult(result, callType) {
4356
4418
  }
4357
4419
  async function sendSimulationAlert(subdomain, proposalId, vault, execResult, settleResult) {
4358
4420
  try {
4359
- const xmtp = await import("./xmtp-H5GTOJ43.js");
4360
- const { getAccount: getAccount2 } = await import("./client-DA434B67.js");
4421
+ const xmtp = await import("./xmtp-EOMKXRTB.js");
4422
+ const { getAccount: getAccount2 } = await import("./client-3BFK7TNL.js");
4361
4423
  const group = await xmtp.getGroup("", subdomain);
4362
4424
  const allRisks = [...execResult.risks, ...settleResult?.risks ?? []];
4363
4425
  const criticals = allRisks.filter((r) => r.level === "critical");
@@ -4605,7 +4667,7 @@ function registerProposalCommands(program2) {
4605
4667
  ).option(
4606
4668
  "--settle-caps <list>",
4607
4669
  "Comma-separated per-settlement-call outflow caps in asset units. Default: 0 for every call, because a settlement returns assets to the vault rather than sending them."
4608
- ).option("--metadata-uri <uri>", "Override \u2014 skip IPFS upload and use this URI directly").option("--simulate", "Simulate calls via Tenderly before submitting").option("--force", "Submit even if simulation fails (requires --simulate)").action(async (opts) => {
4670
+ ).option("--metadata-uri <uri>", "Override \u2014 skip IPFS upload and use this URI directly").option("--proposer <address>", "With --calldata-only: the wallet that will send propose (recorded in metadata; enables the proposer-bond balance/allowance preflight)").option("--simulate", "Simulate calls via Tenderly before submitting").option("--force", "Submit even if simulation fails (requires --simulate)").action(async (opts) => {
4609
4671
  try {
4610
4672
  const vault = opts.vault;
4611
4673
  if (!isAddress9(vault)) {
@@ -4625,46 +4687,72 @@ function registerProposalCommands(program2) {
4625
4687
  strategy2 = opts.strategy;
4626
4688
  }
4627
4689
  if (isCalldataOnly()) {
4628
- if (!opts.metadataUri) {
4629
- console.error(
4630
- chalk9.red(
4631
- "--calldata-only requires --metadata-uri (IPFS metadata upload needs a signing wallet)"
4632
- )
4633
- );
4634
- process.exit(1);
4690
+ let proposer;
4691
+ if (opts.proposer) {
4692
+ if (!isAddress9(opts.proposer)) {
4693
+ console.error(chalk9.red(`Invalid --proposer address: ${opts.proposer}`));
4694
+ process.exit(1);
4695
+ }
4696
+ proposer = opts.proposer;
4635
4697
  }
4636
4698
  const calldataEnvelope = await resolveRiskEnvelope(opts, vault);
4637
4699
  const capDecimals2 = await assetDecimals(vault);
4700
+ const chainId = getChain().id;
4638
4701
  try {
4639
- emitCalldata(
4640
- encodePropose(
4641
- {
4642
- vault,
4643
- governor: await getGovernorOf(vault),
4644
- strategy: strategy2,
4645
- metadataURI: opts.metadataUri,
4646
- strategyDuration,
4647
- envelope: calldataEnvelope,
4648
- executeCalls,
4649
- executeCallCaps: resolveCaps(
4650
- opts.executeCaps,
4651
- executeCalls.length,
4652
- calldataEnvelope.maxCapital,
4653
- "--execute-caps",
4654
- capDecimals2
4655
- ),
4656
- settlementCalls: settleCalls,
4657
- settlementCallCaps: resolveCaps(
4658
- opts.settleCaps,
4659
- settleCalls.length,
4660
- 0n,
4661
- "--settle-caps",
4662
- capDecimals2
4663
- )
4664
- },
4665
- getChain().id
4666
- )
4702
+ const metadataURI2 = opts.metadataUri || await pinProposalMetadata({
4703
+ schema: "sherwood/proposal/v1",
4704
+ name: opts.name,
4705
+ description: opts.description,
4706
+ chain: getNetwork(),
4707
+ strategies: [],
4708
+ terms: {},
4709
+ links: {},
4710
+ ...proposer ? { proposer } : {},
4711
+ vault,
4712
+ strategyDuration: Number(strategyDuration),
4713
+ createdAt: Math.floor(Date.now() / 1e3)
4714
+ });
4715
+ const bondTxs = await proposerBondApprovalTxs(
4716
+ vault,
4717
+ calldataEnvelope.maxCapital,
4718
+ await getAssetAddress(vault),
4719
+ proposer,
4720
+ chainId
4721
+ );
4722
+ const proposeAction = encodePropose(
4723
+ {
4724
+ vault,
4725
+ governor: await getGovernorOf(vault),
4726
+ strategy: strategy2,
4727
+ metadataURI: metadataURI2,
4728
+ strategyDuration,
4729
+ envelope: calldataEnvelope,
4730
+ executeCalls,
4731
+ executeCallCaps: resolveCaps(
4732
+ opts.executeCaps,
4733
+ executeCalls.length,
4734
+ calldataEnvelope.maxCapital,
4735
+ "--execute-caps",
4736
+ capDecimals2
4737
+ ),
4738
+ settlementCalls: settleCalls,
4739
+ settlementCallCaps: resolveCaps(
4740
+ opts.settleCaps,
4741
+ settleCalls.length,
4742
+ 0n,
4743
+ "--settle-caps",
4744
+ capDecimals2
4745
+ )
4746
+ },
4747
+ chainId
4667
4748
  );
4749
+ emitCalldata({
4750
+ ...proposeAction,
4751
+ txs: [...bondTxs, ...proposeAction.txs],
4752
+ ...bondTxs.length ? {
4753
+ note: "Send in order from the proposing agent's wallet: the WOOD approval to the proposer-bond escrow first \u2014 propose pulls the bond and reverts without it." + (proposeAction.note ? ` ${proposeAction.note}` : "")
4754
+ } : {}
4755
+ });
4668
4756
  } catch (err) {
4669
4757
  console.error(chalk9.red(err instanceof Error ? err.message : String(err)));
4670
4758
  process.exit(1);
@@ -5301,7 +5389,7 @@ function registerProposalCommands(program2) {
5301
5389
  process.exit(1);
5302
5390
  }
5303
5391
  const asset = await getAssetAddress(vault);
5304
- const noopCalldata = encodeFunctionData3({
5392
+ const noopCalldata = encodeFunctionData4({
5305
5393
  abi: ERC20_ABI,
5306
5394
  functionName: "balanceOf",
5307
5395
  args: [vault]
@@ -5679,7 +5767,7 @@ function registerProposalCommands(program2) {
5679
5767
  txs: [
5680
5768
  {
5681
5769
  to: governor,
5682
- data: encodeFunctionData3({
5770
+ data: encodeFunctionData4({
5683
5771
  abi: SYNDICATE_GOVERNOR_ABI,
5684
5772
  functionName: "reclaimProposerBond",
5685
5773
  args: [proposalId]
@@ -5858,6 +5946,20 @@ var VAULT_QUEUE_ABI = [
5858
5946
  inputs: [],
5859
5947
  outputs: [{ name: "", type: "address" }]
5860
5948
  },
5949
+ {
5950
+ name: "redemptionsLocked",
5951
+ type: "function",
5952
+ stateMutability: "view",
5953
+ inputs: [],
5954
+ outputs: [{ name: "", type: "bool" }]
5955
+ },
5956
+ {
5957
+ name: "depositsLocked",
5958
+ type: "function",
5959
+ stateMutability: "view",
5960
+ inputs: [],
5961
+ outputs: [{ name: "", type: "bool" }]
5962
+ },
5861
5963
  {
5862
5964
  type: "event",
5863
5965
  name: "DepositRequested",
@@ -5938,6 +6040,103 @@ var QUEUE_ABI = [
5938
6040
  }
5939
6041
  ];
5940
6042
  var KIND_LABELS = ["Redeem", "Deposit"];
6043
+ var KIND_REDEEM = 0;
6044
+ var NEVER_STAMPS = /* @__PURE__ */ new Set([
6045
+ PROPOSAL_STATE.Rejected,
6046
+ PROPOSAL_STATE.Expired,
6047
+ PROPOSAL_STATE.Cancelled,
6048
+ PROPOSAL_STATE.Settled
6049
+ ]);
6050
+ function classifyRequest(args) {
6051
+ const { request: r, stamped, proposalState } = args;
6052
+ if (r.claimed) return { kind: "claimed" };
6053
+ if (r.cancelled) return { kind: "cancelled" };
6054
+ if (r.kind === KIND_REDEEM) {
6055
+ if (!stamped) {
6056
+ if (proposalState !== void 0 && NEVER_STAMPS.has(proposalState)) {
6057
+ return { kind: "stranded", pid: r.pid, state: PROPOSAL_STATES[proposalState] ?? `state ${proposalState}` };
6058
+ }
6059
+ return { kind: "waiting", pid: r.pid };
6060
+ }
6061
+ return args.redemptionsLocked ? { kind: "locked" } : { kind: "claimable" };
6062
+ }
6063
+ return args.depositsLocked ? { kind: "locked" } : { kind: "claimable" };
6064
+ }
6065
+ function formatRequestStatus(status) {
6066
+ switch (status.kind) {
6067
+ case "claimed":
6068
+ return "claimed";
6069
+ case "cancelled":
6070
+ return "cancelled";
6071
+ case "claimable":
6072
+ return "claimable";
6073
+ case "waiting":
6074
+ return `waiting for pid ${status.pid} to settle`;
6075
+ case "stranded":
6076
+ return `stranded (pid ${status.pid} ended ${status.state} \u2014 cancel it)`;
6077
+ case "locked":
6078
+ return "waiting for vault to unlock";
6079
+ }
6080
+ }
6081
+ function claimRefusal(status, vault, id) {
6082
+ const cancelCmd = `sherwood queue cancel --vault ${vault} --id ${id}`;
6083
+ switch (status.kind) {
6084
+ case "claimable":
6085
+ return void 0;
6086
+ case "claimed":
6087
+ return `Request #${id} has already been claimed.`;
6088
+ case "cancelled":
6089
+ return `Request #${id} was cancelled \u2014 its escrow was already returned.`;
6090
+ case "stranded":
6091
+ return `Request #${id} is stranded: pid ${status.pid} ended ${status.state} without settling, so no claim price will ever be stamped (claim would revert NotSettled).
6092
+ Get the shares back with:
6093
+ ${cancelCmd}
6094
+ then redeem directly.`;
6095
+ case "waiting":
6096
+ return `Request #${id} is waiting for pid ${status.pid} to settle \u2014 no claim price is stamped yet (claim would revert NotSettled). Retry after it settles.
6097
+ If pid ${status.pid} ends Rejected/Expired/Cancelled instead, run:
6098
+ ${cancelCmd}`;
6099
+ case "locked":
6100
+ return `Request #${id} cannot be claimed while the vault is locked by an open proposal (claim would revert VaultLocked). Retry once it settles.`;
6101
+ }
6102
+ }
6103
+ async function loadRequestStatuses(vault, queue, requests) {
6104
+ const client = getPublicClient();
6105
+ const [redemptionsLocked, depositsLocked] = await Promise.all([
6106
+ client.readContract({ address: vault, abi: VAULT_QUEUE_ABI, functionName: "redemptionsLocked" }),
6107
+ client.readContract({ address: vault, abi: VAULT_QUEUE_ABI, functionName: "depositsLocked" })
6108
+ ]);
6109
+ const pids = [
6110
+ ...new Set(
6111
+ requests.filter((r) => r.kind === KIND_REDEEM && !r.claimed && !r.cancelled).map((r) => r.pid)
6112
+ )
6113
+ ];
6114
+ const stampedByPid = /* @__PURE__ */ new Map();
6115
+ const stateByPid = /* @__PURE__ */ new Map();
6116
+ await Promise.all(
6117
+ pids.map(async (pid) => {
6118
+ const sp = await client.readContract({
6119
+ address: queue,
6120
+ abi: QUEUE_ABI,
6121
+ functionName: "getSettlePrice",
6122
+ args: [pid]
6123
+ });
6124
+ stampedByPid.set(pid, sp.stamped);
6125
+ if (!sp.stamped) {
6126
+ stateByPid.set(pid, await getProposalState(vault, pid).catch(() => void 0));
6127
+ }
6128
+ })
6129
+ );
6130
+ return requests.map(
6131
+ (request) => classifyRequest({
6132
+ request,
6133
+ stamped: stampedByPid.get(request.pid) ?? false,
6134
+ proposalState: stateByPid.get(request.pid),
6135
+ redemptionsLocked,
6136
+ depositsLocked
6137
+ })
6138
+ );
6139
+ }
5941
6140
  function resolveVaultAddress(opt) {
5942
6141
  if (!opt || !isAddress10(opt)) {
5943
6142
  console.error(chalk10.red(`Invalid or missing --vault address: ${opt ?? "(none)"}`));
@@ -6097,9 +6296,17 @@ function registerQueueCommands(program2) {
6097
6296
  const vault = resolveVaultAddress(opts.vault);
6098
6297
  const account = getAccount();
6099
6298
  const id = BigInt(opts.id);
6100
- const spinner = ora7(`Claiming request #${id}...`).start();
6299
+ const spinner = ora7(`Checking request #${id}...`).start();
6101
6300
  try {
6102
6301
  const queueAddr = await getQueueAddress(vault);
6302
+ const [status] = await loadRequestStatuses(vault, queueAddr, [await readRequest(queueAddr, id)]);
6303
+ const refusal = claimRefusal(status, vault, id);
6304
+ if (refusal) {
6305
+ spinner.fail(`Not claimable: ${formatRequestStatus(status)}`);
6306
+ console.error(chalk10.yellow(refusal));
6307
+ process.exit(1);
6308
+ }
6309
+ spinner.text = `Claiming request #${id}...`;
6103
6310
  const hash = await writeContractWithRetry({
6104
6311
  account,
6105
6312
  chain: getChain(),
@@ -6164,20 +6371,21 @@ function registerQueueCommands(program2) {
6164
6371
  return;
6165
6372
  }
6166
6373
  const requests = await Promise.all(ids.map((id) => readRequest(queueAddr, id)));
6374
+ const statuses = await loadRequestStatuses(vault, queueAddr, requests);
6167
6375
  spinner.stop();
6168
6376
  console.log();
6169
6377
  console.log(chalk10.bold(`Queue requests (${ids.length}) \u2014 ${account.address}`));
6170
- console.log(chalk10.dim("\u2500".repeat(72)));
6378
+ console.log(chalk10.dim("\u2500".repeat(100)));
6171
6379
  console.log(
6172
6380
  chalk10.dim(
6173
- ` ${"id".padEnd(6)}${"kind".padEnd(9)}${"amount".padEnd(26)}${"pid".padEnd(6)}${"claimed".padEnd(9)}cancelled`
6381
+ ` ${"id".padEnd(6)}${"kind".padEnd(9)}${"amount".padEnd(26)}${"pid".padEnd(6)}${"claimed".padEnd(9)}${"cancelled".padEnd(11)}status`
6174
6382
  )
6175
6383
  );
6176
6384
  ids.forEach((id, i) => {
6177
6385
  const r = requests[i];
6178
6386
  const label = KIND_LABELS[r.kind] ?? `kind ${r.kind}`;
6179
6387
  console.log(
6180
- ` ${String(id).padEnd(6)}${label.padEnd(9)}${String(r.amount).padEnd(26)}${String(r.pid).padEnd(6)}${String(r.claimed).padEnd(9)}${r.cancelled}`
6388
+ ` ${String(id).padEnd(6)}${label.padEnd(9)}${String(r.amount).padEnd(26)}${String(r.pid).padEnd(6)}${String(r.claimed).padEnd(9)}${String(r.cancelled).padEnd(11)}${formatRequestStatus(statuses[i])}`
6181
6389
  );
6182
6390
  });
6183
6391
  console.log();
@@ -9969,9 +10177,9 @@ try {
9969
10177
  } catch {
9970
10178
  }
9971
10179
  var require2 = createRequire(import.meta.url);
9972
- var CLI_VERSION = "0.89.2";
10180
+ var CLI_VERSION = "0.90.0";
9973
10181
  async function loadXmtp() {
9974
- return import("./xmtp-H5GTOJ43.js");
10182
+ return import("./xmtp-EOMKXRTB.js");
9975
10183
  }
9976
10184
  async function loadCron() {
9977
10185
  return import("./cron-E3UAZF5B.js");
@@ -10026,6 +10234,10 @@ program.name("sherwood").description("CLI for agent-managed onchain vaults").ver
10026
10234
  ).hook("preAction", (thisCommand) => {
10027
10235
  const opts = thisCommand.optsWithGlobals();
10028
10236
  setCalldataOnly(Boolean(opts.calldataOnly));
10237
+ if (opts.calldataOnly) {
10238
+ console.log = console.error;
10239
+ console.info = console.error;
10240
+ }
10029
10241
  setNetwork(opts.chain);
10030
10242
  });
10031
10243
  var LEGACY_GROUP_ALIASES = ["fund", "syndicate"];
@@ -10167,20 +10379,22 @@ vaultCmd.command("create").description("Launch a new vault via the factory (inte
10167
10379
  }
10168
10380
  }
10169
10381
  if (isCalldataOnly()) {
10170
- emitCalldata(
10171
- encodeCreateFund(
10172
- {
10173
- creatorAgentId: agentIdStr,
10174
- metadataURI,
10175
- asset,
10176
- name,
10177
- symbol,
10178
- openDeposits,
10179
- subdomain
10180
- },
10181
- getChain().id
10182
- )
10382
+ const action = encodeCreateFund(
10383
+ {
10384
+ creatorAgentId: agentIdStr,
10385
+ metadataURI,
10386
+ asset,
10387
+ name,
10388
+ symbol,
10389
+ openDeposits,
10390
+ subdomain
10391
+ },
10392
+ getChain().id
10183
10393
  );
10394
+ emitCalldata({
10395
+ ...action,
10396
+ note: `${action.note} Bond first with \`sherwood --calldata-only guardian prepare-owner-stake <amount>\`. After this tx confirms, register the creator as an agent (the factory does not, and proposing requires it): read the vault with \`sherwood vault info ${subdomain}\`, then send \`sherwood --calldata-only vault add --vault <vault> --wallet <creator> --agent-id ${agentIdStr}\`.`
10397
+ });
10184
10398
  return;
10185
10399
  }
10186
10400
  const existingId = await subdomainExists(subdomain);
@@ -10642,7 +10856,7 @@ vaultCmd.command("add").description("Register an agent on a vault (creator only)
10642
10856
  agentId = BigInt(opts.agentId);
10643
10857
  } else {
10644
10858
  spinner.text = "Looking up ERC-8004 identity from wallet...";
10645
- const { lookupAgentIdByWallet } = await import("./identity-2JEZE7DV.js");
10859
+ const { lookupAgentIdByWallet } = await import("./identity-M6UIVBNX.js");
10646
10860
  const resolved = await lookupAgentIdByWallet(agentWallet);
10647
10861
  if (resolved === null) {
10648
10862
  spinner.fail("Agent wallet does not own an ERC-8004 identity NFT");
@@ -11345,7 +11559,7 @@ vaultCmd.command("redeem").description("Redeem vault shares for the underlying a
11345
11559
  var strategy = program.command("strategy").description("Strategy templates \u2014 list, clone, propose");
11346
11560
  registerStrategyTemplateCommands(strategy);
11347
11561
  program.command("providers").description("List available providers (research + swap routing)").action(async () => {
11348
- const { listProviders } = await import("./providers-XEEFKITN.js");
11562
+ const { listProviders } = await import("./providers-22A42FKP.js");
11349
11563
  for (const info2 of listProviders()) {
11350
11564
  console.log(`
11351
11565
  ${info2.name} (${info2.type})`);
@@ -11355,7 +11569,7 @@ ${info2.name} (${info2.type})`);
11355
11569
  }
11356
11570
  });
11357
11571
  try {
11358
- const { registerChatCommands } = await import("./chat-QVDOCCJN.js");
11572
+ const { registerChatCommands } = await import("./chat-PFXIL6WT.js");
11359
11573
  registerChatCommands(program);
11360
11574
  } catch {
11361
11575
  program.command("chat <name> [action] [actionArgs...]").description("Vault chat (XMTP) \u2014 requires @xmtp/cli").action(() => {
@@ -11365,7 +11579,7 @@ try {
11365
11579
  process.exit(1);
11366
11580
  });
11367
11581
  }
11368
- var { registerSessionCommands } = await import("./session-775NKMGH.js");
11582
+ var { registerSessionCommands } = await import("./session-GSWEE4M3.js");
11369
11583
  registerSessionCommands(program);
11370
11584
  registerVeniceCommands(program);
11371
11585
  registerAllowanceCommands(program);
@@ -11374,15 +11588,15 @@ registerProposalCommands(program);
11374
11588
  registerQueueCommands(program);
11375
11589
  registerGovernorCommands(program);
11376
11590
  registerGuardianCommands(program);
11377
- var { registerResearchCommands } = await import("./research-CJOSSD4U.js");
11591
+ var { registerResearchCommands } = await import("./research-ZH34O7YL.js");
11378
11592
  registerResearchCommands(program);
11379
- var { registerAgentCommands } = await import("./agent-K5ZKUS47.js");
11593
+ var { registerAgentCommands } = await import("./agent-QU2OSWS4.js");
11380
11594
  registerAgentCommands(program);
11381
- var { registerTradeCommands } = await import("./trade-PCK3LONN.js");
11595
+ var { registerTradeCommands } = await import("./trade-SHK7GKSI.js");
11382
11596
  registerTradeCommands(program);
11383
11597
  registerGridCommand(program);
11384
11598
  registerLighterCommands(program);
11385
- var { registerLaunchpadCommands } = await import("./launchpad-XM4546BO.js");
11599
+ var { registerLaunchpadCommands } = await import("./launchpad-LHMBN4TV.js");
11386
11600
  registerLaunchpadCommands(program);
11387
11601
  var configCmd = program.command("config");
11388
11602
  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 vault 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").option("--anthropic-api-key <key>", "Anthropic Claude API key (for LLM judge)").option("--xmtp-group <subdomain:groupId>", "Cache an XMTP group ID for a vault (e.g. my-vault:abc123)").action((opts) => {