@sherwoodagent/cli 0.24.0 → 0.25.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/{chat-3SAX4VRO.js → chat-MYRUIM54.js} +7 -7
  2. package/dist/{chunk-5NN424VN.js → chunk-2SNPCWB4.js} +4 -4
  3. package/dist/{chunk-LMDHM36X.js → chunk-2UP63CYI.js} +4 -4
  4. package/dist/{chunk-NGET2RWT.js → chunk-66VFSLPS.js} +2 -2
  5. package/dist/{chunk-ZIH3SCFJ.js → chunk-G6LOQCM3.js} +84 -5
  6. package/dist/chunk-G6LOQCM3.js.map +1 -0
  7. package/dist/{chunk-35536GNW.js → chunk-GWIW6DDG.js} +2 -2
  8. package/dist/{chunk-PEJPTKLK.js → chunk-L5RWZSET.js} +5 -5
  9. package/dist/{chunk-R75DM3VL.js → chunk-O7QIRMJO.js} +35 -7
  10. package/dist/chunk-O7QIRMJO.js.map +1 -0
  11. package/dist/{chunk-5S2TBADL.js → chunk-RGOTBJIN.js} +25 -2
  12. package/dist/chunk-RGOTBJIN.js.map +1 -0
  13. package/dist/{chunk-R7Q2L6VV.js → chunk-SBVHGUQJ.js} +3 -3
  14. package/dist/{client-XG6LL6TS.js → client-CWQFYZ5Z.js} +4 -4
  15. package/dist/{config-EHCEKT2B.js → config-YXZKPSZB.js} +8 -2
  16. package/dist/{eas-ZMTIOZH6.js → eas-DGLNPNWX.js} +6 -6
  17. package/dist/{governor-JIXAG6WU.js → governor-3PINJ2X2.js} +6 -6
  18. package/dist/index.js +221 -35
  19. package/dist/index.js.map +1 -1
  20. package/dist/{network-UVMYTKQX.js → network-QEMP3DSP.js} +3 -3
  21. package/dist/{price-AGCWXLZ6.js → price-76BEXJDI.js} +5 -5
  22. package/dist/{research-KUIZM6AK.js → research-FIX3U4LG.js} +9 -9
  23. package/dist/research-ZX7T2ZIJ.js +14 -0
  24. package/dist/{session-2FIR4LJP.js → session-6DJ4A2Q3.js} +10 -10
  25. package/dist/{trade-UTM3DSO3.js → trade-MDVM4XFF.js} +16 -16
  26. package/dist/{xmtp-B2XFVPIV.js → xmtp-64CYLVLO.js} +6 -6
  27. package/package.json +1 -1
  28. package/dist/chunk-5S2TBADL.js.map +0 -1
  29. package/dist/chunk-R75DM3VL.js.map +0 -1
  30. package/dist/chunk-ZIH3SCFJ.js.map +0 -1
  31. package/dist/research-2I6AA3BI.js +0 -14
  32. /package/dist/{chat-3SAX4VRO.js.map → chat-MYRUIM54.js.map} +0 -0
  33. /package/dist/{chunk-5NN424VN.js.map → chunk-2SNPCWB4.js.map} +0 -0
  34. /package/dist/{chunk-LMDHM36X.js.map → chunk-2UP63CYI.js.map} +0 -0
  35. /package/dist/{chunk-NGET2RWT.js.map → chunk-66VFSLPS.js.map} +0 -0
  36. /package/dist/{chunk-35536GNW.js.map → chunk-GWIW6DDG.js.map} +0 -0
  37. /package/dist/{chunk-PEJPTKLK.js.map → chunk-L5RWZSET.js.map} +0 -0
  38. /package/dist/{chunk-R7Q2L6VV.js.map → chunk-SBVHGUQJ.js.map} +0 -0
  39. /package/dist/{client-XG6LL6TS.js.map → client-CWQFYZ5Z.js.map} +0 -0
  40. /package/dist/{config-EHCEKT2B.js.map → config-YXZKPSZB.js.map} +0 -0
  41. /package/dist/{eas-ZMTIOZH6.js.map → eas-DGLNPNWX.js.map} +0 -0
  42. /package/dist/{governor-JIXAG6WU.js.map → governor-3PINJ2X2.js.map} +0 -0
  43. /package/dist/{network-UVMYTKQX.js.map → network-QEMP3DSP.js.map} +0 -0
  44. /package/dist/{price-AGCWXLZ6.js.map → price-76BEXJDI.js.map} +0 -0
  45. /package/dist/{research-KUIZM6AK.js.map → research-FIX3U4LG.js.map} +0 -0
  46. /package/dist/{research-2I6AA3BI.js.map → research-ZX7T2ZIJ.js.map} +0 -0
  47. /package/dist/{session-2FIR4LJP.js.map → session-6DJ4A2Q3.js.map} +0 -0
  48. /package/dist/{trade-UTM3DSO3.js.map → trade-MDVM4XFF.js.map} +0 -0
  49. /package/dist/{xmtp-B2XFVPIV.js.map → xmtp-64CYLVLO.js.map} +0 -0
package/dist/index.js CHANGED
@@ -6,17 +6,20 @@ import {
6
6
  executeBatch,
7
7
  getAssetDecimals,
8
8
  getBalance,
9
+ getShareBalance,
9
10
  getVaultAddress,
10
11
  getVaultInfo,
11
12
  isAgent,
12
13
  preflightDeposit,
14
+ preflightRedeem,
15
+ redeem,
13
16
  registerAgent,
14
17
  removeDepositor,
15
18
  resolveSyndicate,
16
19
  resolveVaultSyndicate,
17
20
  setTextRecord,
18
21
  setVaultAddress
19
- } from "./chunk-ZIH3SCFJ.js";
22
+ } from "./chunk-G6LOQCM3.js";
20
23
  import {
21
24
  UniswapProvider,
22
25
  applySlippage,
@@ -27,7 +30,7 @@ import {
27
30
  getQuote,
28
31
  listModels,
29
32
  provisionApiKey
30
- } from "./chunk-PEJPTKLK.js";
33
+ } from "./chunk-L5RWZSET.js";
31
34
  import {
32
35
  PROPOSAL_STATE,
33
36
  PROPOSAL_STATES,
@@ -58,7 +61,7 @@ import {
58
61
  setVotingPeriod,
59
62
  settleProposal,
60
63
  vote
61
- } from "./chunk-LMDHM36X.js";
64
+ } from "./chunk-2UP63CYI.js";
62
65
  import {
63
66
  fetchMetadata,
64
67
  uploadMetadata
@@ -72,7 +75,7 @@ import {
72
75
  queryJoinRequests,
73
76
  revokeAttestation,
74
77
  stripReferrerPrefix
75
- } from "./chunk-5NN424VN.js";
78
+ } from "./chunk-2SNPCWB4.js";
76
79
  import {
77
80
  AERODROME,
78
81
  AGENT_REGISTRY,
@@ -93,7 +96,7 @@ import {
93
96
  UNISWAP,
94
97
  VENICE,
95
98
  VENICE_STAKING_ABI
96
- } from "./chunk-R75DM3VL.js";
99
+ } from "./chunk-O7QIRMJO.js";
97
100
  import {
98
101
  formatContractError,
99
102
  getAccount,
@@ -101,8 +104,9 @@ import {
101
104
  sendTxWithRetry,
102
105
  waitForReceipt,
103
106
  writeContractWithRetry
104
- } from "./chunk-R7Q2L6VV.js";
107
+ } from "./chunk-SBVHGUQJ.js";
105
108
  import {
109
+ CHAIN_REGISTRY,
106
110
  VALID_NETWORKS,
107
111
  getChain,
108
112
  getExplorerUrl,
@@ -110,11 +114,13 @@ import {
110
114
  getRpcUrl,
111
115
  isTestnet,
112
116
  setNetwork
113
- } from "./chunk-35536GNW.js";
117
+ } from "./chunk-GWIW6DDG.js";
114
118
  import {
115
119
  addSyndicate,
116
120
  cacheGroupId,
121
+ cacheSwapRoute,
117
122
  getAgentId,
123
+ getCachedSwapRoute,
118
124
  getChainContracts,
119
125
  getNotifyTo,
120
126
  getPrimarySyndicate,
@@ -130,13 +136,13 @@ import {
130
136
  setPrivateKey,
131
137
  setUniswapApiKey,
132
138
  setVeniceApiKey
133
- } from "./chunk-5S2TBADL.js";
139
+ } from "./chunk-RGOTBJIN.js";
134
140
 
135
141
  // src/index.ts
136
142
  import { config as loadDotenv } from "dotenv";
137
143
  import { createRequire } from "module";
138
144
  import { Command, Option } from "commander";
139
- import { parseUnits as parseUnits5, isAddress as isAddress5 } from "viem";
145
+ import { parseUnits as parseUnits5, formatUnits as formatUnits5, isAddress as isAddress5 } from "viem";
140
146
  import chalk8 from "chalk";
141
147
  import ora7 from "ora";
142
148
  import { input, confirm, select } from "@inquirer/prompts";
@@ -550,7 +556,125 @@ function buildSettleCalls6(clone) {
550
556
  }
551
557
 
552
558
  // src/commands/strategy-template.ts
559
+ import { concat as concat2, numberToHex } from "viem";
553
560
  var ZERO = "0x0000000000000000000000000000000000000000";
561
+ function encodeV3Path(tokens, fees) {
562
+ if (tokens.length !== fees.length + 1) throw new Error("tokens.length must be fees.length + 1");
563
+ const parts = [];
564
+ for (let i = 0; i < tokens.length; i++) {
565
+ parts.push(tokens[i].toLowerCase());
566
+ if (i < fees.length) {
567
+ parts.push(numberToHex(fees[i], { size: 3 }));
568
+ }
569
+ }
570
+ return concat2(parts);
571
+ }
572
+ function buildSwapExtraData(network, tokenIn, tokenOut, feeTier, hop) {
573
+ if (network === "robinhood-testnet") {
574
+ return encodeAbiParameters7([{ type: "uint24" }], [feeTier]);
575
+ }
576
+ if (hop) {
577
+ const path = encodeV3Path([tokenIn, hop.via, tokenOut], [hop.feeIn, hop.feeOut]);
578
+ const encoded2 = encodeAbiParameters7([{ type: "bytes" }], [path]);
579
+ return `0x01${encoded2.slice(2)}`;
580
+ }
581
+ const encoded = encodeAbiParameters7([{ type: "uint24" }], [feeTier]);
582
+ return `0x00${encoded.slice(2)}`;
583
+ }
584
+ async function detectSwapRoute(asset, token, preferredFeeTier) {
585
+ const network = getNetwork();
586
+ if (network === "robinhood-testnet") {
587
+ return {
588
+ extraData: buildSwapExtraData(network, asset, token, preferredFeeTier),
589
+ routeDesc: `direct (fee ${preferredFeeTier})`
590
+ };
591
+ }
592
+ const chainId = CHAIN_REGISTRY[network].chain.id;
593
+ const cached = getCachedSwapRoute(chainId, asset, token);
594
+ if (cached) {
595
+ const extraData = cached.hop ? buildSwapExtraData(network, asset, token, 0, {
596
+ via: cached.hop.via,
597
+ feeIn: cached.hop.feeIn,
598
+ feeOut: cached.hop.feeOut
599
+ }) : buildSwapExtraData(network, asset, token, cached.feeTier);
600
+ const desc = cached.hop ? `cached multi-hop: ${cached.hop.feeIn}\u2192WETH\u2192${cached.hop.feeOut}` : `cached direct (fee ${cached.feeTier})`;
601
+ return { extraData, routeDesc: desc };
602
+ }
603
+ const publicClient = getPublicClient();
604
+ const quoterAddr = UNISWAP().QUOTER_V2;
605
+ const quoterAbi = [{
606
+ type: "function",
607
+ name: "quoteExactInputSingle",
608
+ inputs: [{ type: "tuple", components: [
609
+ { type: "address", name: "tokenIn" },
610
+ { type: "address", name: "tokenOut" },
611
+ { type: "uint256", name: "amountIn" },
612
+ { type: "uint24", name: "fee" },
613
+ { type: "uint160", name: "sqrtPriceLimitX96" }
614
+ ] }],
615
+ outputs: [
616
+ { type: "uint256" },
617
+ { type: "uint160" },
618
+ { type: "uint32" },
619
+ { type: "uint256" }
620
+ ],
621
+ stateMutability: "nonpayable"
622
+ }];
623
+ const testAmount = asset.toLowerCase() === TOKENS().USDC.toLowerCase() ? 1000000n : 1000000000000000n;
624
+ const feeTiers = [preferredFeeTier, 1e4, 3e3, 500].filter((v, i, a) => a.indexOf(v) === i);
625
+ for (const fee of feeTiers) {
626
+ try {
627
+ await publicClient.simulateContract({
628
+ address: quoterAddr,
629
+ abi: quoterAbi,
630
+ functionName: "quoteExactInputSingle",
631
+ args: [{ tokenIn: asset, tokenOut: token, amountIn: testAmount, fee, sqrtPriceLimitX96: 0n }]
632
+ });
633
+ cacheSwapRoute(chainId, asset, token, { mode: "direct", feeTier: fee, detectedAt: Math.floor(Date.now() / 1e3) });
634
+ return {
635
+ extraData: buildSwapExtraData(network, asset, token, fee),
636
+ routeDesc: `direct (fee ${fee})`
637
+ };
638
+ } catch {
639
+ }
640
+ }
641
+ const weth = TOKENS().WETH;
642
+ if (asset.toLowerCase() === weth.toLowerCase()) return null;
643
+ let bestAssetToWethFee = null;
644
+ for (const fee of [500, 3e3, 1e4]) {
645
+ try {
646
+ await publicClient.simulateContract({
647
+ address: quoterAddr,
648
+ abi: quoterAbi,
649
+ functionName: "quoteExactInputSingle",
650
+ args: [{ tokenIn: asset, tokenOut: weth, amountIn: testAmount, fee, sqrtPriceLimitX96: 0n }]
651
+ });
652
+ bestAssetToWethFee = fee;
653
+ break;
654
+ } catch {
655
+ }
656
+ }
657
+ if (!bestAssetToWethFee) return null;
658
+ const wethTestAmount = 1000000000000000n;
659
+ for (const fee of feeTiers) {
660
+ try {
661
+ await publicClient.simulateContract({
662
+ address: quoterAddr,
663
+ abi: quoterAbi,
664
+ functionName: "quoteExactInputSingle",
665
+ args: [{ tokenIn: weth, tokenOut: token, amountIn: wethTestAmount, fee, sqrtPriceLimitX96: 0n }]
666
+ });
667
+ const hop = { via: weth, feeIn: bestAssetToWethFee, feeOut: fee };
668
+ cacheSwapRoute(chainId, asset, token, { mode: "multi-hop", feeTier: 0, hop, detectedAt: Math.floor(Date.now() / 1e3) });
669
+ return {
670
+ extraData: buildSwapExtraData(network, asset, token, 0, hop),
671
+ routeDesc: `multi-hop: ${bestAssetToWethFee}\u2192WETH\u2192${fee}`
672
+ };
673
+ } catch {
674
+ }
675
+ }
676
+ return null;
677
+ }
554
678
  var TEMPLATES = [
555
679
  {
556
680
  name: "Moonwell Supply",
@@ -793,8 +917,10 @@ async function buildInitDataForTemplate(templateKey, opts, vault) {
793
917
  process.exit(1);
794
918
  }
795
919
  const tokens = TOKENS();
796
- const asset = resolveToken(opts.asset || "WETH");
797
- const decimals = opts.asset?.toUpperCase() === "USDC" ? 6 : 18;
920
+ const defaultAsset = getNetwork() === "robinhood-testnet" ? "WETH" : "USDC";
921
+ const assetSymbol = opts.asset || defaultAsset;
922
+ const asset = resolveToken(assetSymbol);
923
+ const decimals = assetSymbol.toUpperCase() === "USDC" ? 6 : 18;
798
924
  const totalAmount = parseUnits(opts.amount, decimals);
799
925
  const maxSlippageBps = Number(opts.maxSlippage || "500");
800
926
  const feeTier = opts.feeTier || "3000";
@@ -818,11 +944,22 @@ async function buildInitDataForTemplate(templateKey, opts, vault) {
818
944
  }
819
945
  const swapAdapter = opts.swapAdapter || resolveSwapAdapter();
820
946
  const chainlinkVerifier = CHAINLINK().VERIFIER_PROXY;
821
- const allocations = tokenAddrs.map((token, i) => ({
822
- token,
823
- weightBps: weightsBps[i],
824
- swapExtraData: encodeAbiParameters7([{ type: "uint24" }], [Number(feeTier)])
825
- }));
947
+ console.log(chalk.dim(" Detecting swap routes..."));
948
+ const allocations = [];
949
+ for (let i = 0; i < tokenAddrs.length; i++) {
950
+ const token = tokenAddrs[i];
951
+ const route = await detectSwapRoute(asset, token, Number(feeTier));
952
+ if (!route) {
953
+ console.error(chalk.red(`No swap route found for ${token}. No Uniswap V3 pool (direct or via WETH).`));
954
+ process.exit(1);
955
+ }
956
+ console.log(chalk.dim(` ${token.slice(0, 10)}... \u2192 ${route.routeDesc}`));
957
+ allocations.push({
958
+ token,
959
+ weightBps: weightsBps[i],
960
+ swapExtraData: route.extraData
961
+ });
962
+ }
826
963
  return {
827
964
  initData: buildInitData6(asset, swapAdapter, chainlinkVerifier, allocations, totalAmount, maxSlippageBps),
828
965
  asset,
@@ -881,10 +1018,8 @@ function resolveSwapAdapter() {
881
1018
  }
882
1019
  return "0xdae81cDCfcB14c56fCeB788A147Fcd6CbEdfEeca";
883
1020
  }
884
- if (UNISWAP().SWAP_ROUTER === ZERO) {
885
- console.error(chalk.red("No swap adapter available"));
886
- process.exit(1);
887
- }
1021
+ const adapterAddr = UNISWAP().SWAP_ADAPTER;
1022
+ if (adapterAddr !== ZERO) return adapterAddr;
888
1023
  console.error(chalk.red("UniswapSwapAdapter not deployed yet. Use --swap-adapter to specify manually."));
889
1024
  process.exit(1);
890
1025
  }
@@ -1161,9 +1296,9 @@ function registerStrategyTemplateCommands(strategy2) {
1161
1296
  console.error(chalk.red("Missing --name, --performance-fee, or --duration. Use --write-calls to skip proposal submission."));
1162
1297
  process.exit(1);
1163
1298
  }
1164
- const { propose: propose2 } = await import("./governor-JIXAG6WU.js");
1299
+ const { propose: propose2 } = await import("./governor-3PINJ2X2.js");
1165
1300
  const { pinJSON } = await import("./ipfs-22YLNQ2C.js");
1166
- const { parseDuration: parseDuration2 } = await import("./governor-JIXAG6WU.js");
1301
+ const { parseDuration: parseDuration2 } = await import("./governor-3PINJ2X2.js");
1167
1302
  const performanceFeeBps = BigInt(opts.performanceFee);
1168
1303
  if (performanceFeeBps < 0n || performanceFeeBps > 10000n) {
1169
1304
  console.error(chalk.red("--performance-fee must be 0-10000 (basis points)"));
@@ -1271,7 +1406,7 @@ function registerStrategyTemplateCommands(strategy2) {
1271
1406
  let prices = [];
1272
1407
  let priceSource = null;
1273
1408
  try {
1274
- const { getTokenPricesInAsset } = await import("./price-AGCWXLZ6.js");
1409
+ const { getTokenPricesInAsset } = await import("./price-76BEXJDI.js");
1275
1410
  prices = await getTokenPricesInAsset({
1276
1411
  tokens: allocations.map((a, i) => ({
1277
1412
  token: a.token,
@@ -1818,7 +1953,7 @@ function registerVeniceCommands(program2) {
1818
1953
  console.log(chalk2.dim(` Key: ${apiKey.slice(0, 8)}...${apiKey.slice(-4)}`));
1819
1954
  console.log(chalk2.dim(" Saved to ~/.sherwood/config.json"));
1820
1955
  try {
1821
- const { createVeniceProvisionAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-ZMTIOZH6.js");
1956
+ const { createVeniceProvisionAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-DGLNPNWX.js");
1822
1957
  const { uid } = await createVeniceProvisionAttestation(account.address);
1823
1958
  if (uid !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
1824
1959
  console.log(chalk2.dim(` Attested: ${getEasScanUrl2(uid)}`));
@@ -1969,10 +2104,10 @@ ${opts.prompt}`;
1969
2104
  console.log(chalk2.dim(`Model: ${result.model} | Tokens: ${result.usage.promptTokens} in, ${result.usage.completionTokens} out, ${result.usage.totalTokens} total`));
1970
2105
  }
1971
2106
  try {
1972
- const { createVeniceInferenceAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-ZMTIOZH6.js");
2107
+ const { createVeniceInferenceAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-DGLNPNWX.js");
1973
2108
  const { keccak256, toHex, isAddress: isAddr } = await import("viem");
1974
- const { getChainContracts: getChainContracts2 } = await import("./config-EHCEKT2B.js");
1975
- const { getChain: getActiveChain } = await import("./network-UVMYTKQX.js");
2109
+ const { getChainContracts: getChainContracts2 } = await import("./config-YXZKPSZB.js");
2110
+ const { getChain: getActiveChain } = await import("./network-QEMP3DSP.js");
1976
2111
  const vaultRecipient = opts.vault && isAddr(opts.vault) ? opts.vault : getChainContracts2(getActiveChain().id).vault;
1977
2112
  const promptHash = keccak256(toHex(userContent)).slice(0, 18);
1978
2113
  const { uid } = await createVeniceInferenceAttestation(
@@ -3101,8 +3236,8 @@ function printSimulationResult(result, callType) {
3101
3236
  }
3102
3237
  async function sendSimulationAlert(subdomain, proposalId, vault, execResult, settleResult) {
3103
3238
  try {
3104
- const xmtp = await import("./xmtp-B2XFVPIV.js");
3105
- const { getAccount: getAccount2 } = await import("./client-XG6LL6TS.js");
3239
+ const xmtp = await import("./xmtp-64CYLVLO.js");
3240
+ const { getAccount: getAccount2 } = await import("./client-CWQFYZ5Z.js");
3106
3241
  const group = await xmtp.getGroup("", subdomain);
3107
3242
  const allRisks = [...execResult.risks, ...settleResult?.risks ?? []];
3108
3243
  const criticals = allRisks.filter((r) => r.level === "critical");
@@ -3794,7 +3929,7 @@ try {
3794
3929
  var require2 = createRequire(import.meta.url);
3795
3930
  var { version: CLI_VERSION } = require2("../package.json");
3796
3931
  async function loadXmtp() {
3797
- return import("./xmtp-B2XFVPIV.js");
3932
+ return import("./xmtp-64CYLVLO.js");
3798
3933
  }
3799
3934
  async function loadCron() {
3800
3935
  return import("./cron-EOULYO3R.js");
@@ -4754,10 +4889,61 @@ vaultCmd.command("balance").description("Show LP share balance and asset value")
4754
4889
  process.exit(1);
4755
4890
  }
4756
4891
  });
4892
+ vaultCmd.command("redeem").description("Redeem vault shares for the underlying asset (ERC-4626)").option("--vault <address>", "Vault address (default: from config)").option("--shares <amount>", "Shares to redeem in whole-share units (default: all)").option("--receiver <address>", "Receiver of the underlying asset (default: your wallet)").action(async (opts) => {
4893
+ resolveVault(opts);
4894
+ const assetDecimals = await getAssetDecimals();
4895
+ const shareDecimals = assetDecimals * 2;
4896
+ let shares;
4897
+ try {
4898
+ if (opts.shares) {
4899
+ shares = parseUnits5(opts.shares, shareDecimals);
4900
+ } else {
4901
+ shares = await getShareBalance();
4902
+ if (shares === 0n) {
4903
+ console.error(chalk8.red("\n \u2716 No shares to redeem.\n"));
4904
+ process.exit(1);
4905
+ }
4906
+ }
4907
+ } catch (err) {
4908
+ console.error(chalk8.red(`
4909
+ \u2716 ${err instanceof Error ? err.message : String(err)}
4910
+ `));
4911
+ process.exit(1);
4912
+ }
4913
+ if (opts.receiver && !isAddress5(opts.receiver)) {
4914
+ console.error(chalk8.red(`
4915
+ \u2716 Invalid receiver address: ${opts.receiver}
4916
+ `));
4917
+ process.exit(1);
4918
+ }
4919
+ try {
4920
+ await preflightRedeem(shares);
4921
+ } catch (err) {
4922
+ console.error(chalk8.red(`
4923
+ \u2716 ${err instanceof Error ? err.message : String(err)}
4924
+ `));
4925
+ process.exit(1);
4926
+ }
4927
+ const sharesDisplay = formatUnits5(shares, shareDecimals);
4928
+ const spinner = ora7(`Redeeming ${sharesDisplay} shares...`).start();
4929
+ try {
4930
+ const { hash, assetsOut } = await redeem(
4931
+ shares,
4932
+ opts.receiver
4933
+ );
4934
+ spinner.succeed(`Redeemed: ${hash}`);
4935
+ console.log(chalk8.dim(` ${getExplorerUrl(hash)}`));
4936
+ console.log(` Assets received: ${formatUnits5(assetsOut, assetDecimals)}`);
4937
+ } catch (err) {
4938
+ spinner.fail("Redeem failed");
4939
+ console.error(chalk8.red(formatContractError(err)));
4940
+ process.exit(1);
4941
+ }
4942
+ });
4757
4943
  var strategy = program.command("strategy").description("Strategy templates \u2014 list, clone, propose");
4758
4944
  registerStrategyTemplateCommands(strategy);
4759
4945
  program.command("providers").description("List available DeFi providers").action(async () => {
4760
- const { MessariProvider, NansenProvider } = await import("./research-2I6AA3BI.js");
4946
+ const { MessariProvider, NansenProvider } = await import("./research-ZX7T2ZIJ.js");
4761
4947
  const providers = [new MoonwellProvider(), new UniswapProvider(), new MessariProvider(), new NansenProvider()];
4762
4948
  for (const p of providers) {
4763
4949
  const info = p.info();
@@ -4768,7 +4954,7 @@ ${info.name} (${info.type})`);
4768
4954
  }
4769
4955
  });
4770
4956
  try {
4771
- const { registerChatCommands } = await import("./chat-3SAX4VRO.js");
4957
+ const { registerChatCommands } = await import("./chat-MYRUIM54.js");
4772
4958
  registerChatCommands(program);
4773
4959
  } catch {
4774
4960
  program.command("chat <name> [action] [actionArgs...]").description("Syndicate chat (XMTP) \u2014 requires @xmtp/cli").action(() => {
@@ -4778,16 +4964,16 @@ try {
4778
4964
  process.exit(1);
4779
4965
  });
4780
4966
  }
4781
- var { registerSessionCommands } = await import("./session-2FIR4LJP.js");
4967
+ var { registerSessionCommands } = await import("./session-6DJ4A2Q3.js");
4782
4968
  registerSessionCommands(program);
4783
4969
  registerVeniceCommands(program);
4784
4970
  registerAllowanceCommands(program);
4785
4971
  registerIdentityCommands(program);
4786
4972
  registerProposalCommands(program);
4787
4973
  registerGovernorCommands(program);
4788
- var { registerResearchCommands } = await import("./research-KUIZM6AK.js");
4974
+ var { registerResearchCommands } = await import("./research-FIX3U4LG.js");
4789
4975
  registerResearchCommands(program);
4790
- var { registerTradeCommands } = await import("./trade-UTM3DSO3.js");
4976
+ var { registerTradeCommands } = await import("./trade-MDVM4XFF.js");
4791
4977
  registerTradeCommands(program);
4792
4978
  var configCmd = program.command("config");
4793
4979
  configCmd.command("set").description("Save settings to ~/.sherwood/config.json (persists across sessions)").option("--private-key <key>", "Wallet private key (0x-prefixed)").option("--vault <address>", "Default SyndicateVault address").option("--rpc <url>", "Custom RPC URL for the active --chain network").option("--notify-to <id>", "Destination for cron summaries (Telegram chat ID, phone, etc.)").option("--uniswap-api-key <key>", "Uniswap Trading API key (from developers.uniswap.org)").option("--venice-api-key <key>", "Venice AI inference API key").action((opts) => {