@sherwoodagent/cli 0.47.1 → 0.51.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{abis-RGCO5GHC.js → abis-Z6M6SRPX.js} +2 -2
- package/dist/{agent-4HKHLQEJ.js → agent-E5GHURKJ.js} +49 -155
- package/dist/agent-E5GHURKJ.js.map +1 -0
- package/dist/{chat-MRTPUZ3X.js → chat-TD6V5LFD.js} +5 -5
- package/dist/{chunk-WMZEOR5W.js → chunk-2HNUJ7XH.js} +2 -2
- package/dist/{chunk-R7X33D2S.js → chunk-4OF4IZ6M.js} +2 -2
- package/dist/{chunk-D77JGXIV.js → chunk-J2FOCXF2.js} +1 -8
- package/dist/{chunk-D77JGXIV.js.map → chunk-J2FOCXF2.js.map} +1 -1
- package/dist/{chunk-E6LH2FJ6.js → chunk-ME6MUKGZ.js} +2 -2
- package/dist/{chunk-R2P4C2I7.js → chunk-OMW47WOO.js} +142 -2
- package/dist/chunk-OMW47WOO.js.map +1 -0
- package/dist/{chunk-KJ35EF7N.js → chunk-T464VNRC.js} +2 -2
- package/dist/{chunk-IS2CLLUT.js → chunk-UYL6Q7BS.js} +2 -2
- package/dist/{eas-PEEWGSAO.js → eas-LHJF2UCG.js} +3 -3
- package/dist/{governor-MG5VVSU4.js → governor-JD4KZXDA.js} +3 -3
- package/dist/index.js +593 -194
- package/dist/index.js.map +1 -1
- package/dist/{price-WCCXWPDM.js → price-2PNKWY4D.js} +2 -2
- package/dist/{research-QBKISW2M.js → research-WBZU4QNU.js} +3 -3
- package/dist/{session-FPCPCVQX.js → session-IYT3N7MG.js} +5 -5
- package/dist/{trade-NO6CNWCA.js → trade-PTYLNJBU.js} +6 -6
- package/dist/{xmtp-ACB2W573.js → xmtp-ZACWHUI5.js} +3 -3
- package/package.json +1 -1
- package/dist/agent-4HKHLQEJ.js.map +0 -1
- package/dist/chunk-R2P4C2I7.js.map +0 -1
- /package/dist/{abis-RGCO5GHC.js.map → abis-Z6M6SRPX.js.map} +0 -0
- /package/dist/{chat-MRTPUZ3X.js.map → chat-TD6V5LFD.js.map} +0 -0
- /package/dist/{chunk-WMZEOR5W.js.map → chunk-2HNUJ7XH.js.map} +0 -0
- /package/dist/{chunk-R7X33D2S.js.map → chunk-4OF4IZ6M.js.map} +0 -0
- /package/dist/{chunk-E6LH2FJ6.js.map → chunk-ME6MUKGZ.js.map} +0 -0
- /package/dist/{chunk-KJ35EF7N.js.map → chunk-T464VNRC.js.map} +0 -0
- /package/dist/{chunk-IS2CLLUT.js.map → chunk-UYL6Q7BS.js.map} +0 -0
- /package/dist/{eas-PEEWGSAO.js.map → eas-LHJF2UCG.js.map} +0 -0
- /package/dist/{governor-MG5VVSU4.js.map → governor-JD4KZXDA.js.map} +0 -0
- /package/dist/{price-WCCXWPDM.js.map → price-2PNKWY4D.js.map} +0 -0
- /package/dist/{research-QBKISW2M.js.map → research-WBZU4QNU.js.map} +0 -0
- /package/dist/{session-FPCPCVQX.js.map → session-IYT3N7MG.js.map} +0 -0
- /package/dist/{trade-NO6CNWCA.js.map → trade-PTYLNJBU.js.map} +0 -0
- /package/dist/{xmtp-ACB2W573.js.map → xmtp-ZACWHUI5.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
HyperliquidProvider,
|
|
4
|
-
getLatestSignals
|
|
5
|
-
|
|
4
|
+
getLatestSignals,
|
|
5
|
+
hlCancelAllOrders,
|
|
6
|
+
hlGetMeta,
|
|
7
|
+
hlPlaceLimitOrder,
|
|
8
|
+
resolveHLCoin
|
|
9
|
+
} from "./chunk-OMW47WOO.js";
|
|
6
10
|
import {
|
|
7
11
|
UniswapProvider,
|
|
8
12
|
applySlippage,
|
|
9
13
|
encodeSwapPath,
|
|
10
14
|
getMultiHopQuote,
|
|
11
15
|
getQuote
|
|
12
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-2HNUJ7XH.js";
|
|
13
17
|
import {
|
|
14
18
|
chatCompletion,
|
|
15
19
|
checkApiKeyValid,
|
|
@@ -26,14 +30,14 @@ import {
|
|
|
26
30
|
queryJoinRequests,
|
|
27
31
|
revokeJoinRequest,
|
|
28
32
|
stripReferrerPrefix
|
|
29
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-ME6MUKGZ.js";
|
|
30
34
|
import {
|
|
31
35
|
createSyndicate,
|
|
32
36
|
getActiveSyndicates,
|
|
33
37
|
getSyndicate,
|
|
34
38
|
subdomainExists,
|
|
35
39
|
updateMetadata
|
|
36
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-UYL6Q7BS.js";
|
|
37
41
|
import {
|
|
38
42
|
approveDepositor,
|
|
39
43
|
deposit,
|
|
@@ -57,7 +61,7 @@ import {
|
|
|
57
61
|
searchSyndicateAcrossChains,
|
|
58
62
|
setTextRecord,
|
|
59
63
|
setVaultAddress
|
|
60
|
-
} from "./chunk-
|
|
64
|
+
} from "./chunk-4OF4IZ6M.js";
|
|
61
65
|
import {
|
|
62
66
|
PROPOSAL_STATE,
|
|
63
67
|
PROPOSAL_STATES,
|
|
@@ -89,7 +93,7 @@ import {
|
|
|
89
93
|
setVotingPeriod,
|
|
90
94
|
settleProposal,
|
|
91
95
|
vote
|
|
92
|
-
} from "./chunk-
|
|
96
|
+
} from "./chunk-T464VNRC.js";
|
|
93
97
|
import {
|
|
94
98
|
AERODROME,
|
|
95
99
|
AGENT_REGISTRY,
|
|
@@ -154,7 +158,7 @@ import {
|
|
|
154
158
|
SWAP_ROUTER_EXACT_INPUT_SINGLE_ABI,
|
|
155
159
|
SYNDICATE_VAULT_ABI,
|
|
156
160
|
VENICE_STAKING_ABI
|
|
157
|
-
} from "./chunk-
|
|
161
|
+
} from "./chunk-J2FOCXF2.js";
|
|
158
162
|
import {
|
|
159
163
|
fetchMetadata,
|
|
160
164
|
uploadMetadata
|
|
@@ -164,8 +168,8 @@ import {
|
|
|
164
168
|
import { config as loadDotenv } from "dotenv";
|
|
165
169
|
import { createRequire } from "module";
|
|
166
170
|
import { Command, Option } from "commander";
|
|
167
|
-
import { parseUnits as
|
|
168
|
-
import
|
|
171
|
+
import { parseUnits as parseUnits7, formatUnits as formatUnits5, isAddress as isAddress6 } from "viem";
|
|
172
|
+
import chalk15 from "chalk";
|
|
169
173
|
import ora8 from "ora";
|
|
170
174
|
import { input, confirm, select } from "@inquirer/prompts";
|
|
171
175
|
|
|
@@ -589,18 +593,17 @@ function buildSettleCalls6(clone) {
|
|
|
589
593
|
|
|
590
594
|
// src/strategies/hyperliquid-perp-template.ts
|
|
591
595
|
import { encodeAbiParameters as encodeAbiParameters7, encodeFunctionData as encodeFunctionData7, maxUint256 as maxUint2562 } from "viem";
|
|
592
|
-
function buildInitData7(asset, depositAmount,
|
|
596
|
+
function buildInitData7(asset, depositAmount, perpAssetIndex, leverage, maxPositionSize, maxTradesPerDay) {
|
|
593
597
|
return encodeAbiParameters7(
|
|
594
598
|
[
|
|
595
599
|
{ type: "address" },
|
|
596
600
|
{ type: "uint256" },
|
|
597
|
-
{ type: "uint256" },
|
|
598
601
|
{ type: "uint32" },
|
|
599
602
|
{ type: "uint32" },
|
|
600
603
|
{ type: "uint256" },
|
|
601
604
|
{ type: "uint32" }
|
|
602
605
|
],
|
|
603
|
-
[asset, depositAmount,
|
|
606
|
+
[asset, depositAmount, perpAssetIndex, leverage, maxPositionSize, maxTradesPerDay]
|
|
604
607
|
);
|
|
605
608
|
}
|
|
606
609
|
function buildExecuteCalls7(clone, asset, amount) {
|
|
@@ -1066,19 +1069,12 @@ async function buildInitDataForTemplate(templateKey, opts, vault) {
|
|
|
1066
1069
|
const asset = resolveToken(token);
|
|
1067
1070
|
const decimals = token.toUpperCase() === "USDC" ? 6 : 18;
|
|
1068
1071
|
const depositAmount = opts.amount ? parseUnits(opts.amount, decimals) : 0n;
|
|
1069
|
-
if (depositAmount === 0n && !opts.minReturn) {
|
|
1070
|
-
console.error(chalk.red(
|
|
1071
|
-
"--min-return is required when --amount is omitted (dynamic-all mode).\n The settlement floor can't be derived without a reference deposit \u2014 set it explicitly to the minimum USDC you'll accept back from HyperCore."
|
|
1072
|
-
));
|
|
1073
|
-
process.exit(1);
|
|
1074
|
-
}
|
|
1075
|
-
const minReturn = opts.minReturn ? parseUnits(opts.minReturn, decimals) : opts.amount ? parseUnits(opts.amount, decimals) : 0n;
|
|
1076
1072
|
const leverage = Number(opts.leverage || "10");
|
|
1077
1073
|
const assetIndex = Number(opts.assetIndex || "0");
|
|
1078
1074
|
const maxPosition = parseUnits(opts.maxPosition || "100000", decimals);
|
|
1079
1075
|
const maxTradesDay = Number(opts.maxTradesPerDay || "50");
|
|
1080
1076
|
return {
|
|
1081
|
-
initData: buildInitData7(asset, depositAmount,
|
|
1077
|
+
initData: buildInitData7(asset, depositAmount, assetIndex, leverage, maxPosition, maxTradesDay),
|
|
1082
1078
|
asset,
|
|
1083
1079
|
assetAmount: depositAmount
|
|
1084
1080
|
};
|
|
@@ -1224,7 +1220,7 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
1224
1220
|
console.log(chalk.dim("Full proposal: sherwood strategy propose <template> --vault <addr> ..."));
|
|
1225
1221
|
console.log();
|
|
1226
1222
|
});
|
|
1227
|
-
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").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("--leverage <number>", "Leverage multiplier (Hyperliquid Perp, default: 10)").option("--asset-index <number>", "Perp asset index (Hyperliquid Perp, default: 0 for BTC)").option("--
|
|
1223
|
+
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").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("--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)").action(async (templateKey, opts) => {
|
|
1228
1224
|
const vault = opts.vault;
|
|
1229
1225
|
if (!isAddress(vault)) {
|
|
1230
1226
|
console.error(chalk.red("Invalid vault address"));
|
|
@@ -1272,7 +1268,7 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
1272
1268
|
console.log(chalk.dim("Use this address in your proposal batch calls."));
|
|
1273
1269
|
console.log();
|
|
1274
1270
|
});
|
|
1275
|
-
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").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("--leverage <number>", "Leverage multiplier (Hyperliquid Perp, default: 10)").option("--asset-index <number>", "Perp asset index (Hyperliquid Perp, default: 0 for BTC)").option("--
|
|
1271
|
+
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").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("--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)").action(async (templateKey, opts) => {
|
|
1276
1272
|
const clone = opts.clone;
|
|
1277
1273
|
const vault = opts.vault;
|
|
1278
1274
|
if (!isAddress(clone)) {
|
|
@@ -1328,7 +1324,7 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
1328
1324
|
console.log(` Proposer: ${chalk.green(getAccount().address)}`);
|
|
1329
1325
|
console.log();
|
|
1330
1326
|
});
|
|
1331
|
-
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").requiredOption("--vault <address>", "Vault address").option("--write-calls <dir>", "Write execute/settle JSON to directory (skip proposal submission)").option("--name <name>", "Proposal name").option("--description <text>", "Proposal description").option("--performance-fee <bps>", "Agent fee in bps").option("--duration <duration>", "Strategy duration (7d, 24h, etc.)").option("--amount <n>", "Asset amount to deploy").option("--min-redeem <n>", "Min asset on settlement (Moonwell)").option("--token <symbol>", "Asset token symbol (default: USDC)").option("--asset <symbol>", "Asset token (USDC, VVV, or address)").option("--agent <address>", "Agent wallet (Venice, default: your wallet)").option("--min-vvv <n>", "Min VVV from swap (Venice)").option("--single-hop", "Single-hop Aerodrome swap (Venice)").option("--token-a <address>", "Token A (Aerodrome)").option("--token-b <address>", "Token B (Aerodrome)").option("--amount-a <n>", "Token A amount (Aerodrome)").option("--amount-b <n>", "Token B amount (Aerodrome)").option("--stable", "Stable pool (Aerodrome)").option("--gauge <address>", "Gauge address (Aerodrome)").option("--lp-token <address>", "LP token address (Aerodrome)").option("--min-a-out <n>", "Min token A on settle (Aerodrome)").option("--min-b-out <n>", "Min token B on settle (Aerodrome)").option("--slippage <bps>", "Slippage tolerance in bps (wstETH, default: 500 = 5%)").option("--mamo-factory <address>", "Mamo StrategyFactory address (Mamo)").option("--tokens <list>", "Comma-separated token addresses or symbols (Portfolio)").option("--weights <list>", "Comma-separated weights in bps, must sum to 10000 (Portfolio)").option("--max-slippage <bps>", "Max slippage bps (Portfolio, default: 500)").option("--fee-tier <n>", "Pool fee tier (Portfolio, default: 3000)").option("--swap-adapter <address>", "Swap adapter address (Portfolio)").option("--leverage <number>", "Leverage multiplier (Hyperliquid Perp, default: 10)").option("--asset-index <number>", "Perp asset index (Hyperliquid Perp, default: 0 for BTC)").option("--
|
|
1327
|
+
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").requiredOption("--vault <address>", "Vault address").option("--write-calls <dir>", "Write execute/settle JSON to directory (skip proposal submission)").option("--name <name>", "Proposal name").option("--description <text>", "Proposal description").option("--performance-fee <bps>", "Agent fee in bps").option("--duration <duration>", "Strategy duration (7d, 24h, etc.)").option("--amount <n>", "Asset amount to deploy").option("--min-redeem <n>", "Min asset on settlement (Moonwell)").option("--token <symbol>", "Asset token symbol (default: USDC)").option("--asset <symbol>", "Asset token (USDC, VVV, or address)").option("--agent <address>", "Agent wallet (Venice, default: your wallet)").option("--min-vvv <n>", "Min VVV from swap (Venice)").option("--single-hop", "Single-hop Aerodrome swap (Venice)").option("--token-a <address>", "Token A (Aerodrome)").option("--token-b <address>", "Token B (Aerodrome)").option("--amount-a <n>", "Token A amount (Aerodrome)").option("--amount-b <n>", "Token B amount (Aerodrome)").option("--stable", "Stable pool (Aerodrome)").option("--gauge <address>", "Gauge address (Aerodrome)").option("--lp-token <address>", "LP token address (Aerodrome)").option("--min-a-out <n>", "Min token A on settle (Aerodrome)").option("--min-b-out <n>", "Min token B on settle (Aerodrome)").option("--slippage <bps>", "Slippage tolerance in bps (wstETH, default: 500 = 5%)").option("--mamo-factory <address>", "Mamo StrategyFactory address (Mamo)").option("--tokens <list>", "Comma-separated token addresses or symbols (Portfolio)").option("--weights <list>", "Comma-separated weights in bps, must sum to 10000 (Portfolio)").option("--max-slippage <bps>", "Max slippage bps (Portfolio, default: 500)").option("--fee-tier <n>", "Pool fee tier (Portfolio, default: 3000)").option("--swap-adapter <address>", "Swap adapter address (Portfolio)").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)").action(async (templateKey, opts) => {
|
|
1332
1328
|
const vault = opts.vault;
|
|
1333
1329
|
if (!isAddress(vault)) {
|
|
1334
1330
|
console.error(chalk.red("Invalid vault address"));
|
|
@@ -1454,9 +1450,9 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
1454
1450
|
console.error(chalk.red("Missing --name, --performance-fee, or --duration. Use --write-calls to skip proposal submission."));
|
|
1455
1451
|
process.exit(1);
|
|
1456
1452
|
}
|
|
1457
|
-
const { propose: propose2 } = await import("./governor-
|
|
1453
|
+
const { propose: propose2 } = await import("./governor-JD4KZXDA.js");
|
|
1458
1454
|
const { pinJSON } = await import("./ipfs-IGXLLJCF.js");
|
|
1459
|
-
const { parseDuration: parseDuration2 } = await import("./governor-
|
|
1455
|
+
const { parseDuration: parseDuration2 } = await import("./governor-JD4KZXDA.js");
|
|
1460
1456
|
const performanceFeeBps = BigInt(opts.performanceFee);
|
|
1461
1457
|
if (performanceFeeBps < 0n || performanceFeeBps > 10000n) {
|
|
1462
1458
|
console.error(chalk.red("--performance-fee must be 0-10000 (basis points)"));
|
|
@@ -1563,7 +1559,7 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
1563
1559
|
let prices = [];
|
|
1564
1560
|
let priceSource = null;
|
|
1565
1561
|
try {
|
|
1566
|
-
const { getTokenPricesInAsset } = await import("./price-
|
|
1562
|
+
const { getTokenPricesInAsset } = await import("./price-2PNKWY4D.js");
|
|
1567
1563
|
prices = await getTokenPricesInAsset({
|
|
1568
1564
|
tokens: allocations.map((a, i) => ({
|
|
1569
1565
|
token: a.token,
|
|
@@ -1993,7 +1989,7 @@ function registerVeniceCommands(program2) {
|
|
|
1993
1989
|
console.log(chalk2.dim(` Key: ${apiKey.slice(0, 8)}...${apiKey.slice(-4)}`));
|
|
1994
1990
|
console.log(chalk2.dim(" Saved to ~/.sherwood/config.json"));
|
|
1995
1991
|
try {
|
|
1996
|
-
const { createVeniceProvisionAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-
|
|
1992
|
+
const { createVeniceProvisionAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-LHJF2UCG.js");
|
|
1997
1993
|
const { uid } = await createVeniceProvisionAttestation(account.address);
|
|
1998
1994
|
if (uid !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
|
|
1999
1995
|
console.log(chalk2.dim(` Attested: ${getEasScanUrl2(uid)}`));
|
|
@@ -2349,12 +2345,12 @@ ${opts.prompt}`;
|
|
|
2349
2345
|
console.log(chalk2.dim(`Model: ${result.model} | Tokens: ${result.usage.promptTokens} in, ${result.usage.completionTokens} out, ${result.usage.totalTokens} total`));
|
|
2350
2346
|
}
|
|
2351
2347
|
try {
|
|
2352
|
-
const { createVeniceInferenceAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-
|
|
2353
|
-
const { keccak256, toHex, isAddress: isAddr } = await import("viem");
|
|
2348
|
+
const { createVeniceInferenceAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-LHJF2UCG.js");
|
|
2349
|
+
const { keccak256: keccak2562, toHex, isAddress: isAddr } = await import("viem");
|
|
2354
2350
|
const { getChainContracts: getChainContracts2 } = await import("./config-REASKDIK.js");
|
|
2355
2351
|
const { getChain: getActiveChain } = await import("./network-3ZU7UBDU.js");
|
|
2356
2352
|
const vaultRecipient = opts.vault && isAddr(opts.vault) ? opts.vault : getChainContracts2(getActiveChain().id).vault;
|
|
2357
|
-
const promptHash =
|
|
2353
|
+
const promptHash = keccak2562(toHex(userContent)).slice(0, 18);
|
|
2358
2354
|
const { uid } = await createVeniceInferenceAttestation(
|
|
2359
2355
|
result.model,
|
|
2360
2356
|
result.usage.promptTokens,
|
|
@@ -3525,7 +3521,7 @@ function printSimulationResult(result, callType) {
|
|
|
3525
3521
|
}
|
|
3526
3522
|
async function sendSimulationAlert(subdomain, proposalId, vault, execResult, settleResult) {
|
|
3527
3523
|
try {
|
|
3528
|
-
const xmtp = await import("./xmtp-
|
|
3524
|
+
const xmtp = await import("./xmtp-ZACWHUI5.js");
|
|
3529
3525
|
const { getAccount: getAccount2 } = await import("./client-UHHCB6EZ.js");
|
|
3530
3526
|
const group = await xmtp.getGroup("", subdomain);
|
|
3531
3527
|
const allRisks = [...execResult.risks, ...settleResult?.risks ?? []];
|
|
@@ -4618,13 +4614,13 @@ function registerGuardianCommands(program2) {
|
|
|
4618
4614
|
}
|
|
4619
4615
|
|
|
4620
4616
|
// src/commands/grid.ts
|
|
4621
|
-
import
|
|
4617
|
+
import chalk14 from "chalk";
|
|
4622
4618
|
|
|
4623
4619
|
// src/grid/loop.ts
|
|
4624
|
-
import
|
|
4625
|
-
import { appendFile, mkdir as
|
|
4626
|
-
import { join as
|
|
4627
|
-
import { homedir as
|
|
4620
|
+
import chalk13 from "chalk";
|
|
4621
|
+
import { appendFile, mkdir as mkdir4 } from "fs/promises";
|
|
4622
|
+
import { join as join4 } from "path";
|
|
4623
|
+
import { homedir as homedir4 } from "os";
|
|
4628
4624
|
|
|
4629
4625
|
// src/grid/manager.ts
|
|
4630
4626
|
import chalk9 from "chalk";
|
|
@@ -4964,6 +4960,45 @@ var GridManager = class {
|
|
|
4964
4960
|
};
|
|
4965
4961
|
}).filter((e) => e.fillCount > 0);
|
|
4966
4962
|
}
|
|
4963
|
+
/**
|
|
4964
|
+
* Compute the orders that should be placed for the current grid state,
|
|
4965
|
+
* without simulating fills. Used by the live executor.
|
|
4966
|
+
*
|
|
4967
|
+
* Returns:
|
|
4968
|
+
* - ordersToPlace: all current grid levels that haven't been filled
|
|
4969
|
+
* - assetsToCancel: tokens whose grid was rebalanced (need cancel-and-place)
|
|
4970
|
+
* - needsRebalance: whether any grid was rebuilt this tick
|
|
4971
|
+
*/
|
|
4972
|
+
computeOrders(prices) {
|
|
4973
|
+
const state = this.portfolio.getState();
|
|
4974
|
+
if (!state || state.paused || !this.config.enabled) {
|
|
4975
|
+
return { ordersToPlace: [], assetsToCancel: [], needsRebalance: false };
|
|
4976
|
+
}
|
|
4977
|
+
const ordersToPlace = [];
|
|
4978
|
+
const assetsToCancel = [];
|
|
4979
|
+
let needsRebalance = false;
|
|
4980
|
+
for (const grid of state.grids) {
|
|
4981
|
+
const price = prices[grid.token];
|
|
4982
|
+
if (!price || price <= 0) continue;
|
|
4983
|
+
const wasEmpty = grid.levels.length === 0;
|
|
4984
|
+
const fullRebuild = wasEmpty || this.needsFullRebuild(grid);
|
|
4985
|
+
const shift = !fullRebuild && grid.centerPrice > 0 && this.needsShift(grid, price);
|
|
4986
|
+
if (fullRebuild || shift) {
|
|
4987
|
+
needsRebalance = true;
|
|
4988
|
+
if (!wasEmpty) assetsToCancel.push(grid.token);
|
|
4989
|
+
}
|
|
4990
|
+
for (const level of grid.levels) {
|
|
4991
|
+
if (level.filled) continue;
|
|
4992
|
+
ordersToPlace.push({
|
|
4993
|
+
token: grid.token,
|
|
4994
|
+
isBuy: level.side === "buy",
|
|
4995
|
+
price: level.price,
|
|
4996
|
+
quantity: level.quantity
|
|
4997
|
+
});
|
|
4998
|
+
}
|
|
4999
|
+
}
|
|
5000
|
+
return { ordersToPlace, assetsToCancel, needsRebalance };
|
|
5001
|
+
}
|
|
4967
5002
|
/** Get aggregate stats for display. */
|
|
4968
5003
|
getStats() {
|
|
4969
5004
|
const state = this.portfolio.getState();
|
|
@@ -5033,8 +5068,8 @@ var GridHedgeManager = class {
|
|
|
5033
5068
|
await mkdir2(dirname2(HEDGE_STATE_PATH), { recursive: true });
|
|
5034
5069
|
const tmp = `${HEDGE_STATE_PATH}.tmp.${process.pid}`;
|
|
5035
5070
|
await writeFile2(tmp, JSON.stringify(this.state, null, 2), "utf-8");
|
|
5036
|
-
const { rename:
|
|
5037
|
-
await
|
|
5071
|
+
const { rename: rename3 } = await import("fs/promises");
|
|
5072
|
+
await rename3(tmp, HEDGE_STATE_PATH);
|
|
5038
5073
|
}
|
|
5039
5074
|
/**
|
|
5040
5075
|
* Run one hedge tick. Call after grid tick with current prices and open fill data.
|
|
@@ -5152,14 +5187,330 @@ var GridHedgeManager = class {
|
|
|
5152
5187
|
}
|
|
5153
5188
|
};
|
|
5154
5189
|
|
|
5190
|
+
// src/grid/executor.ts
|
|
5191
|
+
import chalk11 from "chalk";
|
|
5192
|
+
var GridExecutor = class {
|
|
5193
|
+
cfg;
|
|
5194
|
+
constructor(cfg) {
|
|
5195
|
+
this.cfg = cfg;
|
|
5196
|
+
}
|
|
5197
|
+
/**
|
|
5198
|
+
* Execute the order plan against Hyperliquid.
|
|
5199
|
+
* Cancels stale orders for rebalanced tokens, then places new orders.
|
|
5200
|
+
*/
|
|
5201
|
+
async execute(plan) {
|
|
5202
|
+
const errors = [];
|
|
5203
|
+
let cancelled = 0;
|
|
5204
|
+
let placed = 0;
|
|
5205
|
+
for (const token of plan.assetsToCancel) {
|
|
5206
|
+
const coin = resolveHLCoin(token);
|
|
5207
|
+
if (!coin) {
|
|
5208
|
+
errors.push(`No HL ticker for ${token}`);
|
|
5209
|
+
continue;
|
|
5210
|
+
}
|
|
5211
|
+
try {
|
|
5212
|
+
await hlCancelAllOrders(coin);
|
|
5213
|
+
cancelled++;
|
|
5214
|
+
console.error(chalk11.dim(` [grid-exec] Cancelled all orders for ${coin}`));
|
|
5215
|
+
} catch (e) {
|
|
5216
|
+
errors.push(`Cancel ${coin} failed: ${e.message}`);
|
|
5217
|
+
}
|
|
5218
|
+
}
|
|
5219
|
+
for (const order of plan.ordersToPlace) {
|
|
5220
|
+
const coin = resolveHLCoin(order.token);
|
|
5221
|
+
if (!coin) {
|
|
5222
|
+
errors.push(`No HL ticker for ${order.token}`);
|
|
5223
|
+
continue;
|
|
5224
|
+
}
|
|
5225
|
+
try {
|
|
5226
|
+
const res = await hlPlaceLimitOrder(coin, order.isBuy, order.quantity, order.price);
|
|
5227
|
+
if (res.success) {
|
|
5228
|
+
placed++;
|
|
5229
|
+
} else {
|
|
5230
|
+
errors.push(`Place ${coin} ${order.isBuy ? "buy" : "sell"} @${order.price}: ${res.error}`);
|
|
5231
|
+
}
|
|
5232
|
+
} catch (e) {
|
|
5233
|
+
errors.push(`Place ${coin} threw: ${e.message}`);
|
|
5234
|
+
}
|
|
5235
|
+
}
|
|
5236
|
+
return { placed, cancelled, errors };
|
|
5237
|
+
}
|
|
5238
|
+
};
|
|
5239
|
+
|
|
5240
|
+
// src/grid/onchain-executor.ts
|
|
5241
|
+
import chalk12 from "chalk";
|
|
5242
|
+
import { mkdir as mkdir3, readFile as readFile3, rename as rename2, writeFile as writeFile3 } from "fs/promises";
|
|
5243
|
+
import { dirname as dirname3, join as join3 } from "path";
|
|
5244
|
+
import { homedir as homedir3 } from "os";
|
|
5245
|
+
import { encodeAbiParameters as encodeAbiParameters10, parseUnits as parseUnits6 } from "viem";
|
|
5246
|
+
|
|
5247
|
+
// src/grid/cloid.ts
|
|
5248
|
+
import { keccak256, encodeAbiParameters as encodeAbiParameters9 } from "viem";
|
|
5249
|
+
function gridCloid(strategy2, assetIndex, isBuy, levelIndex, nonce) {
|
|
5250
|
+
const hash = keccak256(
|
|
5251
|
+
encodeAbiParameters9(
|
|
5252
|
+
[
|
|
5253
|
+
{ type: "address" },
|
|
5254
|
+
{ type: "uint32" },
|
|
5255
|
+
{ type: "bool" },
|
|
5256
|
+
{ type: "uint32" },
|
|
5257
|
+
{ type: "uint32" }
|
|
5258
|
+
],
|
|
5259
|
+
[strategy2, assetIndex, isBuy, levelIndex, nonce]
|
|
5260
|
+
)
|
|
5261
|
+
);
|
|
5262
|
+
const top16 = hash.slice(0, 2 + 32);
|
|
5263
|
+
return BigInt(top16);
|
|
5264
|
+
}
|
|
5265
|
+
|
|
5266
|
+
// src/grid/strategy-abi.ts
|
|
5267
|
+
var HYPERLIQUID_GRID_STRATEGY_ABI = [
|
|
5268
|
+
{
|
|
5269
|
+
type: "function",
|
|
5270
|
+
name: "updateParams",
|
|
5271
|
+
inputs: [{ name: "data", type: "bytes" }],
|
|
5272
|
+
outputs: [],
|
|
5273
|
+
stateMutability: "nonpayable"
|
|
5274
|
+
}
|
|
5275
|
+
];
|
|
5276
|
+
|
|
5277
|
+
// src/grid/onchain-executor.ts
|
|
5278
|
+
var ONCHAIN_STATE_PATH = join3(homedir3(), ".sherwood", "grid", "onchain-state.json");
|
|
5279
|
+
var UINT64_MAX = 18446744073709551615n;
|
|
5280
|
+
var ACTION_PLACE_GRID = 1;
|
|
5281
|
+
var ACTION_CANCEL_ALL = 2;
|
|
5282
|
+
var ACTION_CANCEL_AND_PLACE = 3;
|
|
5283
|
+
var GRID_ORDER_COMPONENTS = [
|
|
5284
|
+
{ name: "assetIndex", type: "uint32" },
|
|
5285
|
+
{ name: "isBuy", type: "bool" },
|
|
5286
|
+
{ name: "limitPx", type: "uint64" },
|
|
5287
|
+
{ name: "sz", type: "uint64" },
|
|
5288
|
+
{ name: "cloid", type: "uint128" }
|
|
5289
|
+
];
|
|
5290
|
+
var OnchainGridExecutor = class {
|
|
5291
|
+
cfg;
|
|
5292
|
+
nonces = /* @__PURE__ */ new Map();
|
|
5293
|
+
placedCloids = /* @__PURE__ */ new Map();
|
|
5294
|
+
meta = null;
|
|
5295
|
+
busy = false;
|
|
5296
|
+
constructor(cfg) {
|
|
5297
|
+
this.cfg = cfg;
|
|
5298
|
+
}
|
|
5299
|
+
/** Load persisted state. Call once before the first execute(). */
|
|
5300
|
+
async load() {
|
|
5301
|
+
try {
|
|
5302
|
+
const raw = await readFile3(ONCHAIN_STATE_PATH, "utf-8");
|
|
5303
|
+
const state = JSON.parse(raw);
|
|
5304
|
+
for (const [tok, n] of Object.entries(state.nonces)) this.nonces.set(tok, n);
|
|
5305
|
+
for (const [tok, cloids] of Object.entries(state.placedCloids)) {
|
|
5306
|
+
this.placedCloids.set(tok, cloids.map((s) => BigInt(s)));
|
|
5307
|
+
}
|
|
5308
|
+
console.error(chalk12.dim(` [grid-onchain] Loaded state: ${this.nonces.size} tokens`));
|
|
5309
|
+
} catch (e) {
|
|
5310
|
+
const code = e.code;
|
|
5311
|
+
if (code === "ENOENT") {
|
|
5312
|
+
} else {
|
|
5313
|
+
console.error(
|
|
5314
|
+
chalk12.yellow(
|
|
5315
|
+
` [grid-onchain] Failed to load state (${e.message}) \u2014 starting fresh. Existing on-chain orders may be orphaned.`
|
|
5316
|
+
)
|
|
5317
|
+
);
|
|
5318
|
+
}
|
|
5319
|
+
}
|
|
5320
|
+
if (!this.meta) this.meta = await hlGetMeta();
|
|
5321
|
+
}
|
|
5322
|
+
/**
|
|
5323
|
+
* Atomic save: write to .tmp, then POSIX-rename. A crash mid-write leaves
|
|
5324
|
+
* either the old file intact or the new file fully written — never a
|
|
5325
|
+
* partial JSON that would crash load() on restart.
|
|
5326
|
+
*/
|
|
5327
|
+
async save() {
|
|
5328
|
+
const state = {
|
|
5329
|
+
nonces: Object.fromEntries(this.nonces),
|
|
5330
|
+
placedCloids: Object.fromEntries(
|
|
5331
|
+
[...this.placedCloids].map(([tok, cloids]) => [tok, cloids.map((c) => c.toString())])
|
|
5332
|
+
)
|
|
5333
|
+
};
|
|
5334
|
+
await mkdir3(dirname3(ONCHAIN_STATE_PATH), { recursive: true });
|
|
5335
|
+
const tmp = ONCHAIN_STATE_PATH + ".tmp";
|
|
5336
|
+
await writeFile3(tmp, JSON.stringify(state, null, 2));
|
|
5337
|
+
await rename2(tmp, ONCHAIN_STATE_PATH);
|
|
5338
|
+
}
|
|
5339
|
+
/**
|
|
5340
|
+
* Execute the order plan: cancel stale orders for rebalanced tokens, then
|
|
5341
|
+
* place new orders. Each (token) is one tx. Persists state after each tx
|
|
5342
|
+
* so a crash mid-execution still leaves a recoverable on-disk record.
|
|
5343
|
+
*
|
|
5344
|
+
* Concurrency-guarded: a second concurrent call returns immediately rather
|
|
5345
|
+
* than racing tx submission against another in-flight execute().
|
|
5346
|
+
*/
|
|
5347
|
+
async execute(plan) {
|
|
5348
|
+
if (this.busy) {
|
|
5349
|
+
return { placed: 0, cancelled: 0, txs: [], errors: ["execute() already in flight \u2014 skipping"] };
|
|
5350
|
+
}
|
|
5351
|
+
this.busy = true;
|
|
5352
|
+
try {
|
|
5353
|
+
if (!this.meta) await this.load();
|
|
5354
|
+
const errors = [];
|
|
5355
|
+
const txs = [];
|
|
5356
|
+
let placed = 0;
|
|
5357
|
+
let cancelled = 0;
|
|
5358
|
+
const ordersByToken = /* @__PURE__ */ new Map();
|
|
5359
|
+
for (const o of plan.ordersToPlace) {
|
|
5360
|
+
if (!ordersByToken.has(o.token)) ordersByToken.set(o.token, []);
|
|
5361
|
+
ordersByToken.get(o.token).push(o);
|
|
5362
|
+
}
|
|
5363
|
+
const cancelSet = new Set(plan.assetsToCancel);
|
|
5364
|
+
const allTokens = /* @__PURE__ */ new Set([...ordersByToken.keys(), ...cancelSet]);
|
|
5365
|
+
for (const token of allTokens) {
|
|
5366
|
+
const assetIndex = this.cfg.assetIndices[token];
|
|
5367
|
+
if (assetIndex === void 0) {
|
|
5368
|
+
errors.push(`No asset index for ${token}`);
|
|
5369
|
+
continue;
|
|
5370
|
+
}
|
|
5371
|
+
const coin = resolveHLCoin(token);
|
|
5372
|
+
if (!coin) {
|
|
5373
|
+
errors.push(`No HL ticker for ${token}`);
|
|
5374
|
+
continue;
|
|
5375
|
+
}
|
|
5376
|
+
const meta = this.meta.get(coin);
|
|
5377
|
+
if (!meta) {
|
|
5378
|
+
errors.push(`No HL meta for ${coin} (run \`sherwood grid status\` to refresh)`);
|
|
5379
|
+
continue;
|
|
5380
|
+
}
|
|
5381
|
+
const wantCancel = cancelSet.has(token);
|
|
5382
|
+
const orders = ordersByToken.get(token) ?? [];
|
|
5383
|
+
const wantPlace = orders.length > 0;
|
|
5384
|
+
try {
|
|
5385
|
+
if (wantCancel && wantPlace) {
|
|
5386
|
+
const tx = await this.cancelAndPlace(token, assetIndex, orders, meta);
|
|
5387
|
+
txs.push(tx);
|
|
5388
|
+
cancelled += 1;
|
|
5389
|
+
placed += orders.length;
|
|
5390
|
+
} else if (wantCancel) {
|
|
5391
|
+
const tx = await this.cancelAll(token, assetIndex);
|
|
5392
|
+
txs.push(tx);
|
|
5393
|
+
cancelled += 1;
|
|
5394
|
+
} else if (wantPlace) {
|
|
5395
|
+
const tx = await this.placeGrid(token, assetIndex, orders, meta);
|
|
5396
|
+
txs.push(tx);
|
|
5397
|
+
placed += orders.length;
|
|
5398
|
+
}
|
|
5399
|
+
await this.save();
|
|
5400
|
+
} catch (e) {
|
|
5401
|
+
errors.push(`${token}: ${e.message}`);
|
|
5402
|
+
}
|
|
5403
|
+
}
|
|
5404
|
+
return { placed, cancelled, txs, errors };
|
|
5405
|
+
} finally {
|
|
5406
|
+
this.busy = false;
|
|
5407
|
+
}
|
|
5408
|
+
}
|
|
5409
|
+
currentNonce(token) {
|
|
5410
|
+
return this.nonces.get(token) ?? 0;
|
|
5411
|
+
}
|
|
5412
|
+
bumpNonce(token) {
|
|
5413
|
+
const next = this.currentNonce(token) + 1;
|
|
5414
|
+
this.nonces.set(token, next);
|
|
5415
|
+
return next;
|
|
5416
|
+
}
|
|
5417
|
+
/**
|
|
5418
|
+
* Pure encoding — does NOT mutate placedCloids. Caller commits CLOIDs
|
|
5419
|
+
* only after the on-chain tx confirms successfully.
|
|
5420
|
+
*
|
|
5421
|
+
* Uses viem's `parseUnits` for decimal scaling instead of float math so
|
|
5422
|
+
* high-szDecimals tokens (BTC=5, ETH=4) keep full precision on small sizes.
|
|
5423
|
+
*/
|
|
5424
|
+
encodeOrders(assetIndex, orders, meta, nonce) {
|
|
5425
|
+
const cloids = [];
|
|
5426
|
+
const encoded = orders.map((o, i) => {
|
|
5427
|
+
const cloid = gridCloid(this.cfg.strategyAddress, assetIndex, o.isBuy, i, nonce);
|
|
5428
|
+
cloids.push(cloid);
|
|
5429
|
+
const limitPx = parseUnits6(o.price.toFixed(meta.pxDecimals), meta.pxDecimals);
|
|
5430
|
+
const sz = parseUnits6(o.quantity.toFixed(meta.szDecimals), meta.szDecimals);
|
|
5431
|
+
if (limitPx > UINT64_MAX || sz > UINT64_MAX) {
|
|
5432
|
+
throw new Error(
|
|
5433
|
+
`Order exceeds uint64: token=${o.token} px=${limitPx} sz=${sz}`
|
|
5434
|
+
);
|
|
5435
|
+
}
|
|
5436
|
+
return { assetIndex, isBuy: o.isBuy, limitPx, sz, cloid };
|
|
5437
|
+
});
|
|
5438
|
+
return { encoded, cloids };
|
|
5439
|
+
}
|
|
5440
|
+
async placeGrid(token, assetIndex, orders, meta) {
|
|
5441
|
+
const nonce = this.bumpNonce(token);
|
|
5442
|
+
const { encoded, cloids } = this.encodeOrders(assetIndex, orders, meta, nonce);
|
|
5443
|
+
const data = encodeAbiParameters10(
|
|
5444
|
+
[{ type: "uint8" }, { type: "tuple[]", components: [...GRID_ORDER_COMPONENTS] }],
|
|
5445
|
+
[ACTION_PLACE_GRID, encoded]
|
|
5446
|
+
);
|
|
5447
|
+
const tx = await this.send(data);
|
|
5448
|
+
this.placedCloids.set(token, cloids);
|
|
5449
|
+
return tx;
|
|
5450
|
+
}
|
|
5451
|
+
async cancelAll(token, assetIndex) {
|
|
5452
|
+
const cloids = this.placedCloids.get(token) ?? [];
|
|
5453
|
+
if (cloids.length === 0) {
|
|
5454
|
+
this.bumpNonce(token);
|
|
5455
|
+
return "0x";
|
|
5456
|
+
}
|
|
5457
|
+
const data = encodeAbiParameters10(
|
|
5458
|
+
[{ type: "uint8" }, { type: "uint32" }, { type: "uint128[]" }],
|
|
5459
|
+
[ACTION_CANCEL_ALL, assetIndex, cloids]
|
|
5460
|
+
);
|
|
5461
|
+
const tx = await this.send(data);
|
|
5462
|
+
this.placedCloids.set(token, []);
|
|
5463
|
+
this.bumpNonce(token);
|
|
5464
|
+
return tx;
|
|
5465
|
+
}
|
|
5466
|
+
async cancelAndPlace(token, assetIndex, orders, meta) {
|
|
5467
|
+
const oldCloids = this.placedCloids.get(token) ?? [];
|
|
5468
|
+
const newNonce = this.bumpNonce(token);
|
|
5469
|
+
const { encoded, cloids: newCloids } = this.encodeOrders(assetIndex, orders, meta, newNonce);
|
|
5470
|
+
const data = encodeAbiParameters10(
|
|
5471
|
+
[
|
|
5472
|
+
{ type: "uint8" },
|
|
5473
|
+
{ type: "uint32" },
|
|
5474
|
+
{ type: "uint128[]" },
|
|
5475
|
+
{ type: "tuple[]", components: [...GRID_ORDER_COMPONENTS] }
|
|
5476
|
+
],
|
|
5477
|
+
[ACTION_CANCEL_AND_PLACE, assetIndex, oldCloids, encoded]
|
|
5478
|
+
);
|
|
5479
|
+
const tx = await this.send(data);
|
|
5480
|
+
this.placedCloids.set(token, newCloids);
|
|
5481
|
+
return tx;
|
|
5482
|
+
}
|
|
5483
|
+
/**
|
|
5484
|
+
* Submit and wait for receipt. Throwing here surfaces to the per-token
|
|
5485
|
+
* try/catch in execute(), recording the error and skipping the save() so
|
|
5486
|
+
* placedCloids never reflects orders that didn't land.
|
|
5487
|
+
*/
|
|
5488
|
+
async send(data) {
|
|
5489
|
+
const tx = await writeContractWithRetry({
|
|
5490
|
+
address: this.cfg.strategyAddress,
|
|
5491
|
+
abi: HYPERLIQUID_GRID_STRATEGY_ABI,
|
|
5492
|
+
functionName: "updateParams",
|
|
5493
|
+
args: [data]
|
|
5494
|
+
});
|
|
5495
|
+
const pub = getPublicClient();
|
|
5496
|
+
const receipt = await pub.waitForTransactionReceipt({ hash: tx, timeout: 3e4 });
|
|
5497
|
+
if (receipt.status !== "success") {
|
|
5498
|
+
throw new Error(`Tx ${tx} reverted on-chain`);
|
|
5499
|
+
}
|
|
5500
|
+
console.error(chalk12.dim(` [grid-onchain] tx ${tx.slice(0, 10)}... confirmed`));
|
|
5501
|
+
return tx;
|
|
5502
|
+
}
|
|
5503
|
+
};
|
|
5504
|
+
|
|
5155
5505
|
// src/grid/loop.ts
|
|
5156
|
-
var GRID_CYCLES_PATH =
|
|
5506
|
+
var GRID_CYCLES_PATH = join4(homedir4(), ".sherwood", "grid", "cycles.jsonl");
|
|
5157
5507
|
var GridLoop = class {
|
|
5158
5508
|
cfg;
|
|
5159
5509
|
gridConfig;
|
|
5160
5510
|
manager;
|
|
5161
5511
|
hedge;
|
|
5162
5512
|
hl;
|
|
5513
|
+
executor = null;
|
|
5163
5514
|
running = false;
|
|
5164
5515
|
cycleCount = 0;
|
|
5165
5516
|
timer = null;
|
|
@@ -5169,12 +5520,25 @@ var GridLoop = class {
|
|
|
5169
5520
|
this.manager = new GridManager(this.gridConfig);
|
|
5170
5521
|
this.hedge = new GridHedgeManager();
|
|
5171
5522
|
this.hl = new HyperliquidProvider();
|
|
5523
|
+
if (cfg.live) {
|
|
5524
|
+
if (!cfg.assetIndices) {
|
|
5525
|
+
throw new Error("assetIndices required when live=true");
|
|
5526
|
+
}
|
|
5527
|
+
if (cfg.strategyAddress) {
|
|
5528
|
+
this.executor = new OnchainGridExecutor({
|
|
5529
|
+
strategyAddress: cfg.strategyAddress,
|
|
5530
|
+
assetIndices: cfg.assetIndices
|
|
5531
|
+
});
|
|
5532
|
+
} else {
|
|
5533
|
+
this.executor = new GridExecutor({ assetIndices: cfg.assetIndices });
|
|
5534
|
+
}
|
|
5535
|
+
}
|
|
5172
5536
|
}
|
|
5173
5537
|
/** Start the grid loop. Resolves when shut down via SIGINT/SIGTERM. */
|
|
5174
5538
|
async start() {
|
|
5175
5539
|
this.running = true;
|
|
5176
5540
|
const shutdown = () => {
|
|
5177
|
-
console.error(
|
|
5541
|
+
console.error(chalk13.yellow("\n [grid-loop] Shutting down\u2026"));
|
|
5178
5542
|
this.running = false;
|
|
5179
5543
|
if (this.timer) {
|
|
5180
5544
|
clearTimeout(this.timer);
|
|
@@ -5184,7 +5548,10 @@ var GridLoop = class {
|
|
|
5184
5548
|
process.on("SIGINT", shutdown);
|
|
5185
5549
|
process.on("SIGTERM", shutdown);
|
|
5186
5550
|
await this.manager.init(this.cfg.capital);
|
|
5187
|
-
|
|
5551
|
+
if (this.executor instanceof OnchainGridExecutor) {
|
|
5552
|
+
await this.executor.load();
|
|
5553
|
+
}
|
|
5554
|
+
console.error(chalk13.cyan(
|
|
5188
5555
|
`
|
|
5189
5556
|
[grid-loop] Started \u2014 capital=$${this.cfg.capital.toFixed(0)} cycle=${(this.cfg.cycle / 1e3).toFixed(0)}s tokens=[${this.gridConfig.tokens.join(", ")}] leverage=${this.gridConfig.leverage}x levels=${this.gridConfig.levelsPerSide}/side
|
|
5190
5557
|
`
|
|
@@ -5193,7 +5560,7 @@ var GridLoop = class {
|
|
|
5193
5560
|
try {
|
|
5194
5561
|
await this.tick();
|
|
5195
5562
|
} catch (err) {
|
|
5196
|
-
console.error(
|
|
5563
|
+
console.error(chalk13.red(` [grid-loop] Tick error: ${err.message}`));
|
|
5197
5564
|
}
|
|
5198
5565
|
if (this.running) {
|
|
5199
5566
|
await new Promise((resolve2) => {
|
|
@@ -5203,7 +5570,7 @@ var GridLoop = class {
|
|
|
5203
5570
|
}
|
|
5204
5571
|
process.off("SIGINT", shutdown);
|
|
5205
5572
|
process.off("SIGTERM", shutdown);
|
|
5206
|
-
console.error(
|
|
5573
|
+
console.error(chalk13.yellow(" [grid-loop] Stopped."));
|
|
5207
5574
|
}
|
|
5208
5575
|
/** Execute one grid cycle. */
|
|
5209
5576
|
async tick() {
|
|
@@ -5217,25 +5584,37 @@ var GridLoop = class {
|
|
|
5217
5584
|
}
|
|
5218
5585
|
}
|
|
5219
5586
|
if (Object.keys(prices).length === 0) {
|
|
5220
|
-
console.error(
|
|
5587
|
+
console.error(chalk13.dim(` [grid-loop] #${this.cycleCount} \u2014 no prices, skipping`));
|
|
5221
5588
|
return;
|
|
5222
5589
|
}
|
|
5223
5590
|
const result = await this.manager.tick(prices);
|
|
5224
5591
|
const elapsed = Date.now() - start;
|
|
5592
|
+
if (this.executor) {
|
|
5593
|
+
const plan = this.manager.computeOrders(prices);
|
|
5594
|
+
if (plan.ordersToPlace.length > 0 || plan.assetsToCancel.length > 0) {
|
|
5595
|
+
const res = await this.executor.execute(plan);
|
|
5596
|
+
if (res.errors.length > 0) {
|
|
5597
|
+
console.error(chalk13.yellow(` [grid-loop] Executor errors: ${res.errors.join("; ")}`));
|
|
5598
|
+
}
|
|
5599
|
+
if (res.placed > 0 || res.cancelled > 0) {
|
|
5600
|
+
console.error(chalk13.cyan(` [grid-loop] Live: placed=${res.placed} cancelled=${res.cancelled}`));
|
|
5601
|
+
}
|
|
5602
|
+
}
|
|
5603
|
+
}
|
|
5225
5604
|
if (result.roundTrips > 0) {
|
|
5226
|
-
console.error(
|
|
5605
|
+
console.error(chalk13.green(
|
|
5227
5606
|
` [grid-loop] #${this.cycleCount} \u2014 ${result.roundTrips} RT(s), +$${result.pnlUsd.toFixed(2)} PnL, ${result.fills} fill(s) [${elapsed}ms]`
|
|
5228
5607
|
));
|
|
5229
5608
|
}
|
|
5230
5609
|
if (result.fills > 0 && result.roundTrips === 0) {
|
|
5231
|
-
console.error(
|
|
5610
|
+
console.error(chalk13.dim(
|
|
5232
5611
|
` [grid-loop] #${this.cycleCount} \u2014 ${result.fills} fill(s), 0 RTs [${elapsed}ms]`
|
|
5233
5612
|
));
|
|
5234
5613
|
}
|
|
5235
5614
|
const openExposure = this.manager.getOpenFillExposure();
|
|
5236
5615
|
const hedgeResult = await this.hedge.tick(openExposure, prices);
|
|
5237
5616
|
if (hedgeResult.adjustments > 0) {
|
|
5238
|
-
console.error(
|
|
5617
|
+
console.error(chalk13.magenta(
|
|
5239
5618
|
` [hedge] ${hedgeResult.adjustments} adjustment(s), unrealized: $${hedgeResult.unrealizedPnl.toFixed(2)}, total realized: $${hedgeResult.totalRealizedPnl.toFixed(2)}`
|
|
5240
5619
|
));
|
|
5241
5620
|
}
|
|
@@ -5255,7 +5634,7 @@ var GridLoop = class {
|
|
|
5255
5634
|
hedgeTotalRealizedPnl: hedgeResult.totalRealizedPnl
|
|
5256
5635
|
};
|
|
5257
5636
|
try {
|
|
5258
|
-
await
|
|
5637
|
+
await mkdir4(join4(homedir4(), ".sherwood", "grid"), { recursive: true });
|
|
5259
5638
|
await appendFile(GRID_CYCLES_PATH, JSON.stringify(cycleEntry) + "\n");
|
|
5260
5639
|
} catch {
|
|
5261
5640
|
}
|
|
@@ -5264,7 +5643,7 @@ var GridLoop = class {
|
|
|
5264
5643
|
if (stats2) {
|
|
5265
5644
|
const hedgeStatus = this.hedge.getStatus();
|
|
5266
5645
|
const hedgeInfo = hedgeStatus && hedgeStatus.positions.length > 0 ? ` hedge=$${hedgeResult.unrealizedPnl.toFixed(2)}unr/$${hedgeStatus.totalRealizedPnl.toFixed(2)}real` : "";
|
|
5267
|
-
console.error(
|
|
5646
|
+
console.error(chalk13.cyan(
|
|
5268
5647
|
` [grid-loop] Status #${this.cycleCount} \u2014 totalPnL=$${stats2.totalPnlUsd.toFixed(2)} todayPnL=$${stats2.todayPnlUsd.toFixed(2)} RTs=${stats2.totalRoundTrips} alloc=$${stats2.allocation.toFixed(0)}${hedgeInfo}${stats2.paused ? " (PAUSED)" : ""}`
|
|
5269
5648
|
));
|
|
5270
5649
|
}
|
|
@@ -5273,19 +5652,36 @@ var GridLoop = class {
|
|
|
5273
5652
|
};
|
|
5274
5653
|
|
|
5275
5654
|
// src/commands/grid.ts
|
|
5276
|
-
var DIM5 =
|
|
5277
|
-
var G5 =
|
|
5278
|
-
var BOLD5 =
|
|
5279
|
-
var W5 =
|
|
5655
|
+
var DIM5 = chalk14.gray;
|
|
5656
|
+
var G5 = chalk14.green;
|
|
5657
|
+
var BOLD5 = chalk14.white.bold;
|
|
5658
|
+
var W5 = chalk14.white;
|
|
5280
5659
|
var SEP5 = () => console.log(DIM5("\u2500".repeat(60)));
|
|
5281
5660
|
function registerGridCommand(program2) {
|
|
5282
5661
|
const grid = program2.command("grid").description("Grid trading strategy \u2014 ATR-based grid on BTC/ETH/SOL");
|
|
5283
|
-
grid.command("start").description("Start the grid trading loop").option("--capital <usd>", "Starting capital in USD", "5000").option("--cycle <seconds>", "Cycle interval in seconds", "60").option("--tokens <list>", "Comma-separated token list", "bitcoin,ethereum,solana").option("--leverage <n>", "Leverage multiplier", "5").option("--levels <n>", "Grid levels per side", "15").action(async (opts) => {
|
|
5662
|
+
grid.command("start").description("Start the grid trading loop").option("--capital <usd>", "Starting capital in USD", "5000").option("--cycle <seconds>", "Cycle interval in seconds", "60").option("--tokens <list>", "Comma-separated token list", "bitcoin,ethereum,solana").option("--leverage <n>", "Leverage multiplier", "5").option("--levels <n>", "Grid levels per side", "15").option("--live", "enable live execution (places real orders on Hyperliquid)").option("--asset-indices <pairs>", "comma-separated token=index pairs (e.g. bitcoin=3,ethereum=4,solana=5)").option("--strategy <address>", "on-chain strategy contract address (enables on-chain executor)").action(async (opts) => {
|
|
5284
5663
|
const capital = parseFloat(opts.capital);
|
|
5285
5664
|
const cycleMs = parseInt(opts.cycle, 10) * 1e3;
|
|
5286
5665
|
const tokens = opts.tokens.split(",").map((t) => t.trim());
|
|
5287
5666
|
const leverage = parseFloat(opts.leverage);
|
|
5288
5667
|
const levels = parseInt(opts.levels, 10);
|
|
5668
|
+
const live = !!opts.live;
|
|
5669
|
+
let assetIndices;
|
|
5670
|
+
if (live) {
|
|
5671
|
+
if (!opts.assetIndices) {
|
|
5672
|
+
throw new Error("--asset-indices required when --live (e.g. --asset-indices bitcoin=3,ethereum=4,solana=5)");
|
|
5673
|
+
}
|
|
5674
|
+
assetIndices = {};
|
|
5675
|
+
for (const pair of opts.assetIndices.split(",")) {
|
|
5676
|
+
const [tok, idx] = pair.split("=");
|
|
5677
|
+
if (!tok || !idx) throw new Error(`Bad asset-indices pair: ${pair}`);
|
|
5678
|
+
assetIndices[tok.trim()] = Number(idx);
|
|
5679
|
+
}
|
|
5680
|
+
}
|
|
5681
|
+
const strategyAddress = opts.strategy;
|
|
5682
|
+
if (strategyAddress && !live) {
|
|
5683
|
+
throw new Error("--strategy requires --live");
|
|
5684
|
+
}
|
|
5289
5685
|
const weight = 1 / tokens.length;
|
|
5290
5686
|
const tokenSplit = {};
|
|
5291
5687
|
for (const t of tokens) {
|
|
@@ -5303,6 +5699,9 @@ function registerGridCommand(program2) {
|
|
|
5303
5699
|
const loop = new GridLoop({
|
|
5304
5700
|
capital,
|
|
5305
5701
|
cycle: cycleMs,
|
|
5702
|
+
live,
|
|
5703
|
+
assetIndices,
|
|
5704
|
+
strategyAddress,
|
|
5306
5705
|
config: {
|
|
5307
5706
|
...DEFAULT_GRID_CONFIG,
|
|
5308
5707
|
tokens,
|
|
@@ -5326,7 +5725,7 @@ function registerGridCommand(program2) {
|
|
|
5326
5725
|
console.log(G5.bold(" Grid Portfolio"));
|
|
5327
5726
|
SEP5();
|
|
5328
5727
|
console.log(W5(` Allocation: $${state.totalAllocation.toLocaleString()}`));
|
|
5329
|
-
console.log(W5(` Status: ${state.paused ?
|
|
5728
|
+
console.log(W5(` Status: ${state.paused ? chalk14.red("PAUSED \u2014 " + state.pauseReason) : G5("Active")}`));
|
|
5330
5729
|
console.log(W5(` Initialized: ${new Date(state.initializedAt).toLocaleString()}`));
|
|
5331
5730
|
SEP5();
|
|
5332
5731
|
console.log();
|
|
@@ -5339,13 +5738,13 @@ function registerGridCommand(program2) {
|
|
|
5339
5738
|
const pnl = `$${g.stats.totalPnlUsd.toFixed(2)}`.padStart(10);
|
|
5340
5739
|
const todayPnl = `$${g.stats.todayPnlUsd.toFixed(2)}`.padStart(10);
|
|
5341
5740
|
const fills = String(g.stats.totalFills).padStart(6);
|
|
5342
|
-
const pnlColor = g.stats.totalPnlUsd >= 0 ? G5 :
|
|
5343
|
-
const todayColor = g.stats.todayPnlUsd >= 0 ? G5 :
|
|
5741
|
+
const pnlColor = g.stats.totalPnlUsd >= 0 ? G5 : chalk14.red;
|
|
5742
|
+
const todayColor = g.stats.todayPnlUsd >= 0 ? G5 : chalk14.red;
|
|
5344
5743
|
console.log(` ${W5(name)} ${alloc} ${rts} ${pnlColor(pnl)} ${todayColor(todayPnl)} ${fills}`);
|
|
5345
5744
|
}
|
|
5346
5745
|
console.log(DIM5(" " + "\u2500".repeat(72)));
|
|
5347
|
-
const totalPnlColor = agg.totalPnlUsd >= 0 ? G5 :
|
|
5348
|
-
const todayTotalColor = agg.todayPnlUsd >= 0 ? G5 :
|
|
5746
|
+
const totalPnlColor = agg.totalPnlUsd >= 0 ? G5 : chalk14.red;
|
|
5747
|
+
const todayTotalColor = agg.todayPnlUsd >= 0 ? G5 : chalk14.red;
|
|
5349
5748
|
console.log(` ${BOLD5("TOTAL".padEnd(13))} ${`$${state.totalAllocation.toFixed(0)}`.padStart(8)} ${String(agg.totalRoundTrips).padStart(6)} ${totalPnlColor(`$${agg.totalPnlUsd.toFixed(2)}`.padStart(10))} ${todayTotalColor(`$${agg.todayPnlUsd.toFixed(2)}`.padStart(10))} ${String(agg.todayFills).padStart(6)}`);
|
|
5350
5749
|
console.log();
|
|
5351
5750
|
});
|
|
@@ -5359,20 +5758,20 @@ try {
|
|
|
5359
5758
|
var require2 = createRequire(import.meta.url);
|
|
5360
5759
|
var { version: CLI_VERSION } = require2("../package.json");
|
|
5361
5760
|
async function loadXmtp() {
|
|
5362
|
-
return import("./xmtp-
|
|
5761
|
+
return import("./xmtp-ZACWHUI5.js");
|
|
5363
5762
|
}
|
|
5364
5763
|
async function loadCron() {
|
|
5365
5764
|
return import("./cron-EOULYO3R.js");
|
|
5366
5765
|
}
|
|
5367
|
-
var G6 =
|
|
5368
|
-
var W6 =
|
|
5369
|
-
var DIM6 =
|
|
5370
|
-
var BOLD6 =
|
|
5371
|
-
var LABEL5 =
|
|
5766
|
+
var G6 = chalk15.green;
|
|
5767
|
+
var W6 = chalk15.white;
|
|
5768
|
+
var DIM6 = chalk15.gray;
|
|
5769
|
+
var BOLD6 = chalk15.white.bold;
|
|
5770
|
+
var LABEL5 = chalk15.green.bold;
|
|
5372
5771
|
var SEP6 = () => console.log(DIM6("\u2500".repeat(60)));
|
|
5373
5772
|
function validateAddress(value, name) {
|
|
5374
5773
|
if (!isAddress6(value)) {
|
|
5375
|
-
console.error(
|
|
5774
|
+
console.error(chalk15.red(`Invalid ${name} address: ${value}`));
|
|
5376
5775
|
process.exit(1);
|
|
5377
5776
|
}
|
|
5378
5777
|
return value;
|
|
@@ -5392,7 +5791,7 @@ program.name("sherwood").description("CLI for agent-managed investment syndicate
|
|
|
5392
5791
|
process.env.ENABLE_TESTNET = "true";
|
|
5393
5792
|
if (network !== "base") {
|
|
5394
5793
|
console.warn(
|
|
5395
|
-
|
|
5794
|
+
chalk15.yellow("[warn] --testnet ignored, --chain takes precedence")
|
|
5396
5795
|
);
|
|
5397
5796
|
} else {
|
|
5398
5797
|
network = "base-sepolia";
|
|
@@ -5400,7 +5799,7 @@ program.name("sherwood").description("CLI for agent-managed investment syndicate
|
|
|
5400
5799
|
}
|
|
5401
5800
|
setNetwork(network);
|
|
5402
5801
|
if (getNetwork() !== "base") {
|
|
5403
|
-
console.log(
|
|
5802
|
+
console.log(chalk15.yellow(`[${getNetwork()}]`));
|
|
5404
5803
|
}
|
|
5405
5804
|
});
|
|
5406
5805
|
var syndicate = program.command("syndicate");
|
|
@@ -5448,7 +5847,7 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
5448
5847
|
asset = opts.asset;
|
|
5449
5848
|
} else {
|
|
5450
5849
|
const supported = Object.keys(ASSET_SYMBOLS).join(", ");
|
|
5451
|
-
console.error(
|
|
5850
|
+
console.error(chalk15.red(` Unknown asset "${opts.asset}". Use a symbol (${supported}) or a 0x address.`));
|
|
5452
5851
|
process.exit(1);
|
|
5453
5852
|
}
|
|
5454
5853
|
} else if (nonInteractive) {
|
|
@@ -5478,7 +5877,7 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
5478
5877
|
console.log(W6(` Agent ID: #${agentIdStr}`));
|
|
5479
5878
|
console.log(W6(` Asset: ${assetSymbol} (${asset.slice(0, 10)}...)`));
|
|
5480
5879
|
console.log(W6(` Share token: ${symbol}`));
|
|
5481
|
-
console.log(W6(` Open deposits: ${openDeposits ? G6("yes") :
|
|
5880
|
+
console.log(W6(` Open deposits: ${openDeposits ? G6("yes") : chalk15.red("no (whitelist)")}`));
|
|
5482
5881
|
SEP6();
|
|
5483
5882
|
if (!nonInteractive) {
|
|
5484
5883
|
const go = await confirm({ message: G6("Deploy syndicate?"), default: true });
|
|
@@ -5505,7 +5904,7 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
5505
5904
|
metadataURI = await uploadMetadata(metadata);
|
|
5506
5905
|
spinner2.succeed(G6(`Metadata pinned: ${DIM6(metadataURI)}`));
|
|
5507
5906
|
} catch (err) {
|
|
5508
|
-
spinner2.warn(
|
|
5907
|
+
spinner2.warn(chalk15.yellow(`IPFS upload failed \u2014 using inline metadata`));
|
|
5509
5908
|
const json = JSON.stringify({ name, description, subdomain, asset: assetSymbol, openDeposits, createdBy: "@sherwoodagent/cli" });
|
|
5510
5909
|
metadataURI = `data:application/json;base64,${Buffer.from(json).toString("base64")}`;
|
|
5511
5910
|
}
|
|
@@ -5515,12 +5914,12 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
5515
5914
|
const existingInfo = await getSyndicate(existingId);
|
|
5516
5915
|
const callerAddress = getAccount().address.toLowerCase();
|
|
5517
5916
|
if (existingInfo.creator.toLowerCase() !== callerAddress) {
|
|
5518
|
-
console.error(
|
|
5917
|
+
console.error(chalk15.red(`
|
|
5519
5918
|
\u2716 Subdomain '${subdomain}' is already taken by another syndicate.
|
|
5520
5919
|
`));
|
|
5521
5920
|
process.exit(1);
|
|
5522
5921
|
}
|
|
5523
|
-
console.log(
|
|
5922
|
+
console.log(chalk15.yellow(`
|
|
5524
5923
|
\u26A0 Syndicate '${subdomain}' already exists \u2014 resuming setup...
|
|
5525
5924
|
`));
|
|
5526
5925
|
const spinner2 = ora8({ text: W6("Resuming setup..."), color: "green" }).start();
|
|
@@ -5551,7 +5950,7 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
5551
5950
|
cacheGroupId(subdomain, groupId);
|
|
5552
5951
|
}
|
|
5553
5952
|
} catch {
|
|
5554
|
-
console.warn(
|
|
5953
|
+
console.warn(chalk15.yellow("\n \u26A0 Could not set up XMTP chat group"));
|
|
5555
5954
|
}
|
|
5556
5955
|
try {
|
|
5557
5956
|
const cron = await loadCron();
|
|
@@ -5598,7 +5997,7 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
5598
5997
|
// agentAddress = creator EOA (direct execution)
|
|
5599
5998
|
);
|
|
5600
5999
|
} catch (regErr) {
|
|
5601
|
-
console.warn(
|
|
6000
|
+
console.warn(chalk15.yellow("\n \u26A0 Could not auto-register creator as agent \u2014 register manually with `syndicate add`"));
|
|
5602
6001
|
}
|
|
5603
6002
|
spinner.text = W6("Setting up chat...");
|
|
5604
6003
|
try {
|
|
@@ -5611,8 +6010,8 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
5611
6010
|
} catch {
|
|
5612
6011
|
}
|
|
5613
6012
|
} catch {
|
|
5614
|
-
console.warn(
|
|
5615
|
-
console.warn(
|
|
6013
|
+
console.warn(chalk15.yellow("\n \u26A0 Could not create XMTP chat group"));
|
|
6014
|
+
console.warn(chalk15.dim(` Recover later with: sherwood chat ${subdomain} init`));
|
|
5616
6015
|
}
|
|
5617
6016
|
try {
|
|
5618
6017
|
const cron = await loadCron();
|
|
@@ -5640,7 +6039,7 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
5640
6039
|
console.log(G6(" \u2713 Syndicate saved to ~/.sherwood/config.json (set as primary)"));
|
|
5641
6040
|
console.log();
|
|
5642
6041
|
} catch (err) {
|
|
5643
|
-
console.error(
|
|
6042
|
+
console.error(chalk15.red(`
|
|
5644
6043
|
\u2716 ${formatContractError(err)}`));
|
|
5645
6044
|
process.exit(1);
|
|
5646
6045
|
}
|
|
@@ -5690,38 +6089,38 @@ syndicate.command("list").description("List active syndicates (queries subgraph,
|
|
|
5690
6089
|
}
|
|
5691
6090
|
spinner.stop();
|
|
5692
6091
|
if (syndicates.length === 0) {
|
|
5693
|
-
console.log(
|
|
6092
|
+
console.log(chalk15.dim("No active syndicates found."));
|
|
5694
6093
|
return;
|
|
5695
6094
|
}
|
|
5696
6095
|
console.log();
|
|
5697
|
-
console.log(
|
|
6096
|
+
console.log(chalk15.bold(`Active Syndicates (${syndicates.length})`));
|
|
5698
6097
|
if (!process.env.SUBGRAPH_URL && !opts.allChains) {
|
|
5699
|
-
console.log(
|
|
6098
|
+
console.log(chalk15.dim(" (Set SUBGRAPH_URL for faster indexed queries)"));
|
|
5700
6099
|
}
|
|
5701
6100
|
if (opts.allChains) {
|
|
5702
|
-
console.log(
|
|
6101
|
+
console.log(chalk15.dim(" (Aggregated across chains via on-chain factory calls; SUBGRAPH_URL is single-chain only)"));
|
|
5703
6102
|
}
|
|
5704
|
-
console.log(
|
|
6103
|
+
console.log(chalk15.dim("\u2500".repeat(70)));
|
|
5705
6104
|
for (const s of syndicates) {
|
|
5706
6105
|
const ts = typeof s.createdAt === "string" ? Number(s.createdAt) : Number(s.createdAt);
|
|
5707
6106
|
const date = new Date(ts * 1e3).toLocaleDateString();
|
|
5708
6107
|
const ensName = s.subdomain ? `${s.subdomain}.sherwoodagent.eth` : "";
|
|
5709
|
-
const chainTag = s.chain ?
|
|
5710
|
-
console.log(` ${chainTag}#${s.id} ${
|
|
5711
|
-
if (ensName) console.log(` Vault: ${
|
|
6108
|
+
const chainTag = s.chain ? chalk15.dim(`[${s.chain}] `) : "";
|
|
6109
|
+
console.log(` ${chainTag}#${s.id} ${chalk15.bold(ensName || String(s.vault))}`);
|
|
6110
|
+
if (ensName) console.log(` Vault: ${chalk15.cyan(String(s.vault))}`);
|
|
5712
6111
|
console.log(` Creator: ${s.creator}`);
|
|
5713
6112
|
console.log(` Created: ${date}`);
|
|
5714
6113
|
if (s.totalDeposits) {
|
|
5715
6114
|
console.log(` Deposits: ${s.totalDeposits} USDC`);
|
|
5716
6115
|
}
|
|
5717
6116
|
if (s.metadataURI) {
|
|
5718
|
-
console.log(` Metadata: ${
|
|
6117
|
+
console.log(` Metadata: ${chalk15.dim(s.metadataURI)}`);
|
|
5719
6118
|
}
|
|
5720
6119
|
console.log();
|
|
5721
6120
|
}
|
|
5722
6121
|
} catch (err) {
|
|
5723
6122
|
spinner.fail("Failed to load syndicates");
|
|
5724
|
-
console.error(
|
|
6123
|
+
console.error(chalk15.red(formatContractError(err)));
|
|
5725
6124
|
process.exit(1);
|
|
5726
6125
|
}
|
|
5727
6126
|
});
|
|
@@ -5761,38 +6160,38 @@ syndicate.command("info").description("Display syndicate details by ID or subdom
|
|
|
5761
6160
|
spinner.stop();
|
|
5762
6161
|
if (crossChainHint) {
|
|
5763
6162
|
const others = crossChainHint.others.length > 0 ? ` (also on: ${crossChainHint.others.join(", ")})` : "";
|
|
5764
|
-
console.log(
|
|
5765
|
-
console.log(
|
|
6163
|
+
console.log(chalk15.yellow(` Found on chain: ${chalk15.bold(crossChainHint.network)}${others}`));
|
|
6164
|
+
console.log(chalk15.dim(` Re-run with --chain ${crossChainHint.network} to interact with this syndicate.`));
|
|
5766
6165
|
console.log();
|
|
5767
6166
|
}
|
|
5768
6167
|
if (!info.vault || info.vault === "0x0000000000000000000000000000000000000000") {
|
|
5769
|
-
console.log(
|
|
6168
|
+
console.log(chalk15.red(`Syndicate #${id} not found.`));
|
|
5770
6169
|
process.exit(1);
|
|
5771
6170
|
}
|
|
5772
6171
|
const date = new Date(Number(info.createdAt) * 1e3).toLocaleDateString();
|
|
5773
6172
|
console.log();
|
|
5774
|
-
console.log(
|
|
5775
|
-
console.log(
|
|
6173
|
+
console.log(chalk15.bold(`Syndicate #${info.id}`));
|
|
6174
|
+
console.log(chalk15.dim("\u2500".repeat(40)));
|
|
5776
6175
|
if (info.subdomain) {
|
|
5777
|
-
console.log(` ENS: ${
|
|
6176
|
+
console.log(` ENS: ${chalk15.bold(`${info.subdomain}.sherwoodagent.eth`)}`);
|
|
5778
6177
|
}
|
|
5779
|
-
console.log(` Vault: ${
|
|
6178
|
+
console.log(` Vault: ${chalk15.cyan(info.vault)}`);
|
|
5780
6179
|
console.log(` Creator: ${info.creator}`);
|
|
5781
6180
|
console.log(` Created: ${date}`);
|
|
5782
|
-
console.log(` Active: ${info.active ?
|
|
6181
|
+
console.log(` Active: ${info.active ? chalk15.green("yes") : chalk15.red("no")}`);
|
|
5783
6182
|
if (info.metadataURI) {
|
|
5784
|
-
console.log(` Metadata: ${
|
|
6183
|
+
console.log(` Metadata: ${chalk15.dim(info.metadataURI)}`);
|
|
5785
6184
|
}
|
|
5786
6185
|
if (info.subdomain) {
|
|
5787
6186
|
const xmtpGroupId = getCachedGroupId(info.subdomain);
|
|
5788
6187
|
if (xmtpGroupId) {
|
|
5789
|
-
console.log(` XMTP Group: ${
|
|
6188
|
+
console.log(` XMTP Group: ${chalk15.cyan(xmtpGroupId)}`);
|
|
5790
6189
|
}
|
|
5791
6190
|
}
|
|
5792
6191
|
setVaultAddress(info.vault);
|
|
5793
6192
|
const vaultInfo = crossChainHint ? await withNetwork(crossChainHint.network, () => getVaultInfo()) : await getVaultInfo();
|
|
5794
6193
|
console.log();
|
|
5795
|
-
console.log(
|
|
6194
|
+
console.log(chalk15.bold(" Vault Stats"));
|
|
5796
6195
|
console.log(` Total Assets: ${vaultInfo.totalAssets}`);
|
|
5797
6196
|
console.log(` Agent Count: ${vaultInfo.agentCount}`);
|
|
5798
6197
|
console.log(` Redemptions Locked: ${vaultInfo.redemptionsLocked}`);
|
|
@@ -5800,7 +6199,7 @@ syndicate.command("info").description("Display syndicate details by ID or subdom
|
|
|
5800
6199
|
console.log();
|
|
5801
6200
|
} catch (err) {
|
|
5802
6201
|
spinner.fail("Failed to load syndicate info");
|
|
5803
|
-
console.error(
|
|
6202
|
+
console.error(chalk15.red(formatContractError(err)));
|
|
5804
6203
|
process.exit(1);
|
|
5805
6204
|
}
|
|
5806
6205
|
});
|
|
@@ -5836,7 +6235,7 @@ syndicate.command("update-metadata").description("Update syndicate metadata (cre
|
|
|
5836
6235
|
console.log(DIM6(` ${getExplorerUrl(hash)}`));
|
|
5837
6236
|
} catch (err) {
|
|
5838
6237
|
spinner.fail("Metadata update failed");
|
|
5839
|
-
console.error(
|
|
6238
|
+
console.error(chalk15.red(formatContractError(err)));
|
|
5840
6239
|
process.exit(1);
|
|
5841
6240
|
}
|
|
5842
6241
|
});
|
|
@@ -5847,10 +6246,10 @@ syndicate.command("approve-depositor").description("Approve an address to deposi
|
|
|
5847
6246
|
const depositor = validateAddress(opts.depositor, "depositor");
|
|
5848
6247
|
const hash = await approveDepositor(depositor);
|
|
5849
6248
|
spinner.succeed(`Depositor approved: ${hash}`);
|
|
5850
|
-
console.log(
|
|
6249
|
+
console.log(chalk15.dim(` ${getExplorerUrl(hash)}`));
|
|
5851
6250
|
} catch (err) {
|
|
5852
6251
|
spinner.fail("Approval failed");
|
|
5853
|
-
console.error(
|
|
6252
|
+
console.error(chalk15.red(formatContractError(err)));
|
|
5854
6253
|
process.exit(1);
|
|
5855
6254
|
}
|
|
5856
6255
|
});
|
|
@@ -5861,10 +6260,10 @@ syndicate.command("remove-depositor").description("Remove an address from the de
|
|
|
5861
6260
|
const depositor = validateAddress(opts.depositor, "depositor");
|
|
5862
6261
|
const hash = await removeDepositor(depositor);
|
|
5863
6262
|
spinner.succeed(`Depositor removed: ${hash}`);
|
|
5864
|
-
console.log(
|
|
6263
|
+
console.log(chalk15.dim(` ${getExplorerUrl(hash)}`));
|
|
5865
6264
|
} catch (err) {
|
|
5866
6265
|
spinner.fail("Removal failed");
|
|
5867
|
-
console.error(
|
|
6266
|
+
console.error(chalk15.red(formatContractError(err)));
|
|
5868
6267
|
process.exit(1);
|
|
5869
6268
|
}
|
|
5870
6269
|
});
|
|
@@ -5905,7 +6304,7 @@ syndicate.command("add").description("Register an agent on a syndicate vault (cr
|
|
|
5905
6304
|
});
|
|
5906
6305
|
if (balance === 0n) {
|
|
5907
6306
|
spinner.fail("Agent wallet does not own an ERC-8004 identity NFT");
|
|
5908
|
-
console.error(
|
|
6307
|
+
console.error(chalk15.dim(" Mint one first: sherwood identity mint --name <name>"));
|
|
5909
6308
|
process.exit(1);
|
|
5910
6309
|
}
|
|
5911
6310
|
const tokenId = await client.readContract({
|
|
@@ -5921,13 +6320,13 @@ syndicate.command("add").description("Register an agent on a syndicate vault (cr
|
|
|
5921
6320
|
args: [agentWallet, 0n]
|
|
5922
6321
|
});
|
|
5923
6322
|
agentId = tokenId;
|
|
5924
|
-
console.log(
|
|
6323
|
+
console.log(chalk15.dim(` Resolved agent ID: #${agentId}`));
|
|
5925
6324
|
}
|
|
5926
6325
|
}
|
|
5927
6326
|
spinner.text = "Registering agent...";
|
|
5928
6327
|
const hash = await registerAgent(agentId, agentWallet);
|
|
5929
6328
|
spinner.succeed(`Agent registered: ${hash}`);
|
|
5930
|
-
console.log(
|
|
6329
|
+
console.log(chalk15.dim(` ${getExplorerUrl(hash)}`));
|
|
5931
6330
|
try {
|
|
5932
6331
|
const xmtp = await loadXmtp();
|
|
5933
6332
|
const xmtpClient = await xmtp.getXmtpClient();
|
|
@@ -5939,14 +6338,14 @@ syndicate.command("add").description("Register an agent on a syndicate vault (cr
|
|
|
5939
6338
|
syndicate: subdomain,
|
|
5940
6339
|
timestamp: Math.floor(Date.now() / 1e3)
|
|
5941
6340
|
});
|
|
5942
|
-
console.log(
|
|
6341
|
+
console.log(chalk15.dim(` Added to chat: ${subdomain}`));
|
|
5943
6342
|
} catch {
|
|
5944
|
-
console.warn(
|
|
5945
|
-
console.warn(
|
|
6343
|
+
console.warn(chalk15.yellow(" \u26A0 Could not add agent to chat group"));
|
|
6344
|
+
console.warn(chalk15.dim(` If no group exists, run: sherwood chat ${subdomain} init`));
|
|
5946
6345
|
}
|
|
5947
6346
|
} catch (err) {
|
|
5948
6347
|
spinner.fail("Registration failed");
|
|
5949
|
-
console.error(
|
|
6348
|
+
console.error(chalk15.red(formatContractError(err)));
|
|
5950
6349
|
process.exit(1);
|
|
5951
6350
|
}
|
|
5952
6351
|
});
|
|
@@ -5954,7 +6353,7 @@ syndicate.command("share").description("Print the shareable dashboard URL for yo
|
|
|
5954
6353
|
try {
|
|
5955
6354
|
const subdomain = opts.subdomain || getPrimarySyndicate(getChain().id)?.subdomain;
|
|
5956
6355
|
if (!subdomain) {
|
|
5957
|
-
console.error(
|
|
6356
|
+
console.error(chalk15.red(" No syndicate configured. Pass --subdomain <name> or run 'syndicate create/join' first."));
|
|
5958
6357
|
process.exit(1);
|
|
5959
6358
|
}
|
|
5960
6359
|
const syndicateInfo = await resolveSyndicate(subdomain);
|
|
@@ -5981,7 +6380,7 @@ syndicate.command("share").description("Print the shareable dashboard URL for yo
|
|
|
5981
6380
|
}
|
|
5982
6381
|
console.log();
|
|
5983
6382
|
} catch (err) {
|
|
5984
|
-
console.error(
|
|
6383
|
+
console.error(chalk15.red(` \u2716 ${formatContractError(err)}`));
|
|
5985
6384
|
process.exit(1);
|
|
5986
6385
|
}
|
|
5987
6386
|
});
|
|
@@ -6006,14 +6405,14 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
|
|
|
6006
6405
|
const hits = await searchSyndicateAcrossChains(subdomain);
|
|
6007
6406
|
spinner.fail(`Syndicate "${subdomain}" not found on ${getNetwork()}`);
|
|
6008
6407
|
if (hits.length === 0) {
|
|
6009
|
-
console.error(
|
|
6408
|
+
console.error(chalk15.red(` Not found on any supported chain.`));
|
|
6010
6409
|
} else if (hits.length === 1) {
|
|
6011
|
-
console.error(
|
|
6012
|
-
console.error(
|
|
6410
|
+
console.error(chalk15.yellow(` Found on chain: ${chalk15.bold(hits[0].network)}`));
|
|
6411
|
+
console.error(chalk15.dim(` Re-run: sherwood --chain ${hits[0].network} syndicate join --subdomain ${subdomain}`));
|
|
6013
6412
|
} else {
|
|
6014
6413
|
const chains = hits.map((h) => h.network).join(", ");
|
|
6015
|
-
console.error(
|
|
6016
|
-
console.error(
|
|
6414
|
+
console.error(chalk15.yellow(` Found on chains: ${chalk15.bold(chains)}`));
|
|
6415
|
+
console.error(chalk15.dim(` Specify which chain to join with --chain <network>.`));
|
|
6017
6416
|
}
|
|
6018
6417
|
process.exit(1);
|
|
6019
6418
|
}
|
|
@@ -6026,17 +6425,17 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
|
|
|
6026
6425
|
try {
|
|
6027
6426
|
const xmtp = await loadXmtp();
|
|
6028
6427
|
await xmtp.getXmtpClient();
|
|
6029
|
-
console.log(
|
|
6428
|
+
console.log(chalk15.dim(" XMTP identity ready"));
|
|
6030
6429
|
} catch {
|
|
6031
|
-
console.warn(
|
|
6430
|
+
console.warn(chalk15.yellow(" \u26A0 Could not initialize XMTP identity"));
|
|
6032
6431
|
}
|
|
6033
6432
|
try {
|
|
6034
6433
|
const cron = await loadCron();
|
|
6035
6434
|
const cronResult = cron.registerSyndicateCrons(subdomain, isTestnet(), getNotifyTo());
|
|
6036
6435
|
if (cronResult.isOpenClaw && cronResult.registered) {
|
|
6037
|
-
console.log(
|
|
6436
|
+
console.log(chalk15.green(" \u2713 Participation crons registered"));
|
|
6038
6437
|
} else if (!cronResult.isOpenClaw) {
|
|
6039
|
-
console.log(
|
|
6438
|
+
console.log(chalk15.dim(" Tip: Set up a scheduled process to run `sherwood session check " + subdomain + "` periodically"));
|
|
6040
6439
|
}
|
|
6041
6440
|
} catch {
|
|
6042
6441
|
}
|
|
@@ -6049,14 +6448,14 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
|
|
|
6049
6448
|
);
|
|
6050
6449
|
if (existingRequest) {
|
|
6051
6450
|
spinner.succeed("You already have a pending join request for this syndicate");
|
|
6052
|
-
console.log(
|
|
6053
|
-
console.log(
|
|
6451
|
+
console.log(chalk15.dim(` Attestation: ${existingRequest.uid}`));
|
|
6452
|
+
console.log(chalk15.dim(` Submitted: ${new Date(existingRequest.time * 1e3).toLocaleString()}`));
|
|
6054
6453
|
try {
|
|
6055
6454
|
const xmtp = await loadXmtp();
|
|
6056
6455
|
await xmtp.getXmtpClient();
|
|
6057
|
-
console.log(
|
|
6456
|
+
console.log(chalk15.dim(" XMTP identity ready"));
|
|
6058
6457
|
} catch {
|
|
6059
|
-
console.warn(
|
|
6458
|
+
console.warn(chalk15.yellow(" \u26A0 Could not initialize XMTP identity"));
|
|
6060
6459
|
}
|
|
6061
6460
|
return;
|
|
6062
6461
|
}
|
|
@@ -6077,7 +6476,7 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
|
|
|
6077
6476
|
spinner.succeed("Join request created (XMTP identity ready)");
|
|
6078
6477
|
} catch {
|
|
6079
6478
|
spinner.succeed("Join request created");
|
|
6080
|
-
console.warn(
|
|
6479
|
+
console.warn(chalk15.yellow(" \u26A0 Could not initialize XMTP identity \u2014 creator may not be able to auto-add you to chat"));
|
|
6081
6480
|
}
|
|
6082
6481
|
addSyndicate(getChain().id, { subdomain, vault: syndicate2.vault, role: "agent" });
|
|
6083
6482
|
setPrimarySyndicate(getChain().id, subdomain);
|
|
@@ -6107,7 +6506,7 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
|
|
|
6107
6506
|
console.log();
|
|
6108
6507
|
} catch (err) {
|
|
6109
6508
|
spinner.fail("Join request failed");
|
|
6110
|
-
console.error(
|
|
6509
|
+
console.error(chalk15.red(formatContractError(err)));
|
|
6111
6510
|
process.exit(1);
|
|
6112
6511
|
}
|
|
6113
6512
|
});
|
|
@@ -6175,7 +6574,7 @@ syndicate.command("requests").description("View pending join requests for a synd
|
|
|
6175
6574
|
console.log();
|
|
6176
6575
|
} catch (err) {
|
|
6177
6576
|
spinner.fail("Failed to load requests");
|
|
6178
|
-
console.error(
|
|
6577
|
+
console.error(chalk15.red(formatContractError(err)));
|
|
6179
6578
|
process.exit(1);
|
|
6180
6579
|
}
|
|
6181
6580
|
});
|
|
@@ -6250,8 +6649,8 @@ syndicate.command("approve").description("Approve an agent join request (registe
|
|
|
6250
6649
|
});
|
|
6251
6650
|
console.log(DIM6(` Added to chat: ${subdomain}`));
|
|
6252
6651
|
} catch {
|
|
6253
|
-
console.warn(
|
|
6254
|
-
console.warn(
|
|
6652
|
+
console.warn(chalk15.yellow(" \u26A0 Could not add agent to chat group"));
|
|
6653
|
+
console.warn(chalk15.dim(` If no group exists, run: sherwood chat ${subdomain} init`));
|
|
6255
6654
|
}
|
|
6256
6655
|
spinner.succeed("Agent approved and registered");
|
|
6257
6656
|
console.log();
|
|
@@ -6264,7 +6663,7 @@ syndicate.command("approve").description("Approve an agent join request (registe
|
|
|
6264
6663
|
SEP6();
|
|
6265
6664
|
} catch (err) {
|
|
6266
6665
|
spinner.fail("Approval failed");
|
|
6267
|
-
console.error(
|
|
6666
|
+
console.error(chalk15.red(formatContractError(err)));
|
|
6268
6667
|
process.exit(1);
|
|
6269
6668
|
}
|
|
6270
6669
|
});
|
|
@@ -6276,14 +6675,14 @@ syndicate.command("reject").description("Reject a join request by revoking its a
|
|
|
6276
6675
|
console.log(DIM6(` ${getEasExplorerUrl(hash)}`));
|
|
6277
6676
|
} catch (err) {
|
|
6278
6677
|
spinner.fail("Rejection failed");
|
|
6279
|
-
console.error(
|
|
6678
|
+
console.error(chalk15.red(formatContractError(err)));
|
|
6280
6679
|
process.exit(1);
|
|
6281
6680
|
}
|
|
6282
6681
|
});
|
|
6283
6682
|
syndicate.command("leave").description("Leave a syndicate \u2014 removes participation crons and session state").option("--subdomain <name>", "Syndicate subdomain to leave").action(async (opts) => {
|
|
6284
6683
|
const subdomain = opts.subdomain || getPrimarySyndicate(getChain().id)?.subdomain;
|
|
6285
6684
|
if (!subdomain) {
|
|
6286
|
-
console.error(
|
|
6685
|
+
console.error(chalk15.red(" No syndicate configured. Pass --subdomain <name>."));
|
|
6287
6686
|
process.exit(1);
|
|
6288
6687
|
}
|
|
6289
6688
|
const spinner = ora8("Cleaning up...").start();
|
|
@@ -6303,13 +6702,13 @@ syndicate.command("leave").description("Leave a syndicate \u2014 removes partici
|
|
|
6303
6702
|
}
|
|
6304
6703
|
console.log(G6(" \u2713 Session state cleared"));
|
|
6305
6704
|
console.log();
|
|
6306
|
-
console.log(
|
|
6307
|
-
console.log(
|
|
6308
|
-
console.log(
|
|
6705
|
+
console.log(chalk15.dim(" Note: This does not remove you on-chain. To exit your position:"));
|
|
6706
|
+
console.log(chalk15.dim(" sherwood vault balance \u2014 check your LP share balance"));
|
|
6707
|
+
console.log(chalk15.dim(" Redeem shares via the vault contract or dashboard"));
|
|
6309
6708
|
console.log();
|
|
6310
6709
|
} catch (err) {
|
|
6311
6710
|
spinner.fail("Leave failed");
|
|
6312
|
-
console.error(
|
|
6711
|
+
console.error(chalk15.red(err instanceof Error ? err.message : String(err)));
|
|
6313
6712
|
process.exit(1);
|
|
6314
6713
|
}
|
|
6315
6714
|
});
|
|
@@ -6360,7 +6759,7 @@ syndicate.command("set-primary").description("Set the active syndicate for CLI c
|
|
|
6360
6759
|
}
|
|
6361
6760
|
}
|
|
6362
6761
|
if (!target) {
|
|
6363
|
-
console.error(
|
|
6762
|
+
console.error(chalk15.red(" Could not resolve syndicate. Pass --subdomain or --vault."));
|
|
6364
6763
|
process.exit(1);
|
|
6365
6764
|
}
|
|
6366
6765
|
setPrimarySyndicate(chainId, target);
|
|
@@ -6368,7 +6767,7 @@ syndicate.command("set-primary").description("Set the active syndicate for CLI c
|
|
|
6368
6767
|
\u2713 Primary syndicate set to ${target}.sherwoodagent.eth
|
|
6369
6768
|
`));
|
|
6370
6769
|
} catch (err) {
|
|
6371
|
-
console.error(
|
|
6770
|
+
console.error(chalk15.red(formatContractError(err)));
|
|
6372
6771
|
process.exit(1);
|
|
6373
6772
|
}
|
|
6374
6773
|
});
|
|
@@ -6376,13 +6775,13 @@ var vaultCmd = program.command("vault");
|
|
|
6376
6775
|
vaultCmd.command("deposit").description("Deposit into a vault").option("--vault <address>", "Vault address (default: from config)").requiredOption("--amount <amount>", "Amount to deposit (in asset units)").option("--use-eth", "Auto-wrap ETH \u2192 WETH before depositing (for WETH vaults)").action(async (opts) => {
|
|
6377
6776
|
resolveVault(opts);
|
|
6378
6777
|
const decimals = await getAssetDecimals();
|
|
6379
|
-
const amount =
|
|
6778
|
+
const amount = parseUnits7(opts.amount, decimals);
|
|
6380
6779
|
try {
|
|
6381
6780
|
if (!opts.useEth) {
|
|
6382
6781
|
await preflightDeposit(amount);
|
|
6383
6782
|
}
|
|
6384
6783
|
} catch (err) {
|
|
6385
|
-
console.error(
|
|
6784
|
+
console.error(chalk15.red(`
|
|
6386
6785
|
\u2716 ${err instanceof Error ? err.message : String(err)}
|
|
6387
6786
|
`));
|
|
6388
6787
|
process.exit(1);
|
|
@@ -6397,10 +6796,10 @@ vaultCmd.command("deposit").description("Deposit into a vault").option("--vault
|
|
|
6397
6796
|
hash = await deposit(amount);
|
|
6398
6797
|
}
|
|
6399
6798
|
spinner.succeed(`Deposited: ${hash}`);
|
|
6400
|
-
console.log(
|
|
6799
|
+
console.log(chalk15.dim(` ${getExplorerUrl(hash)}`));
|
|
6401
6800
|
} catch (err) {
|
|
6402
6801
|
spinner.fail("Deposit failed");
|
|
6403
|
-
console.error(
|
|
6802
|
+
console.error(chalk15.red(formatContractError(err)));
|
|
6404
6803
|
process.exit(1);
|
|
6405
6804
|
}
|
|
6406
6805
|
});
|
|
@@ -6411,8 +6810,8 @@ vaultCmd.command("info").description("Display vault state").option("--vault <add
|
|
|
6411
6810
|
const info = await getVaultInfo();
|
|
6412
6811
|
spinner.stop();
|
|
6413
6812
|
console.log();
|
|
6414
|
-
console.log(
|
|
6415
|
-
console.log(
|
|
6813
|
+
console.log(chalk15.bold("Vault Info"));
|
|
6814
|
+
console.log(chalk15.dim("\u2500".repeat(40)));
|
|
6416
6815
|
console.log(` Address: ${info.address}`);
|
|
6417
6816
|
console.log(` Total Assets: ${info.totalAssets}`);
|
|
6418
6817
|
console.log(` Agent Count: ${info.agentCount}`);
|
|
@@ -6421,7 +6820,7 @@ vaultCmd.command("info").description("Display vault state").option("--vault <add
|
|
|
6421
6820
|
console.log();
|
|
6422
6821
|
} catch (err) {
|
|
6423
6822
|
spinner.fail("Failed to load vault info");
|
|
6424
|
-
console.error(
|
|
6823
|
+
console.error(chalk15.red(formatContractError(err)));
|
|
6425
6824
|
process.exit(1);
|
|
6426
6825
|
}
|
|
6427
6826
|
});
|
|
@@ -6432,15 +6831,15 @@ vaultCmd.command("balance").description("Show LP share balance and asset value")
|
|
|
6432
6831
|
const balance = await getBalance(opts.address);
|
|
6433
6832
|
spinner.stop();
|
|
6434
6833
|
console.log();
|
|
6435
|
-
console.log(
|
|
6436
|
-
console.log(
|
|
6834
|
+
console.log(chalk15.bold("LP Position"));
|
|
6835
|
+
console.log(chalk15.dim("\u2500".repeat(40)));
|
|
6437
6836
|
console.log(` Shares: ${balance.shares.toString()}`);
|
|
6438
6837
|
console.log(` Asset Value: ${balance.assetsValue}`);
|
|
6439
6838
|
console.log(` % of Vault: ${balance.percentOfVault}`);
|
|
6440
6839
|
console.log();
|
|
6441
6840
|
} catch (err) {
|
|
6442
6841
|
spinner.fail("Failed to load balance");
|
|
6443
|
-
console.error(
|
|
6842
|
+
console.error(chalk15.red(formatContractError(err)));
|
|
6444
6843
|
process.exit(1);
|
|
6445
6844
|
}
|
|
6446
6845
|
});
|
|
@@ -6451,22 +6850,22 @@ vaultCmd.command("redeem").description("Redeem vault shares for the underlying a
|
|
|
6451
6850
|
let shares;
|
|
6452
6851
|
try {
|
|
6453
6852
|
if (opts.shares) {
|
|
6454
|
-
shares =
|
|
6853
|
+
shares = parseUnits7(opts.shares, shareDecimals);
|
|
6455
6854
|
} else {
|
|
6456
6855
|
shares = await getShareBalance();
|
|
6457
6856
|
if (shares === 0n) {
|
|
6458
|
-
console.error(
|
|
6857
|
+
console.error(chalk15.red("\n \u2716 No shares to redeem.\n"));
|
|
6459
6858
|
process.exit(1);
|
|
6460
6859
|
}
|
|
6461
6860
|
}
|
|
6462
6861
|
} catch (err) {
|
|
6463
|
-
console.error(
|
|
6862
|
+
console.error(chalk15.red(`
|
|
6464
6863
|
\u2716 ${err instanceof Error ? err.message : String(err)}
|
|
6465
6864
|
`));
|
|
6466
6865
|
process.exit(1);
|
|
6467
6866
|
}
|
|
6468
6867
|
if (opts.receiver && !isAddress6(opts.receiver)) {
|
|
6469
|
-
console.error(
|
|
6868
|
+
console.error(chalk15.red(`
|
|
6470
6869
|
\u2716 Invalid receiver address: ${opts.receiver}
|
|
6471
6870
|
`));
|
|
6472
6871
|
process.exit(1);
|
|
@@ -6474,7 +6873,7 @@ vaultCmd.command("redeem").description("Redeem vault shares for the underlying a
|
|
|
6474
6873
|
try {
|
|
6475
6874
|
await preflightRedeem(shares);
|
|
6476
6875
|
} catch (err) {
|
|
6477
|
-
console.error(
|
|
6876
|
+
console.error(chalk15.red(`
|
|
6478
6877
|
\u2716 ${err instanceof Error ? err.message : String(err)}
|
|
6479
6878
|
`));
|
|
6480
6879
|
process.exit(1);
|
|
@@ -6487,11 +6886,11 @@ vaultCmd.command("redeem").description("Redeem vault shares for the underlying a
|
|
|
6487
6886
|
opts.receiver
|
|
6488
6887
|
);
|
|
6489
6888
|
spinner.succeed(`Redeemed: ${hash}`);
|
|
6490
|
-
console.log(
|
|
6889
|
+
console.log(chalk15.dim(` ${getExplorerUrl(hash)}`));
|
|
6491
6890
|
console.log(` Assets received: ${formatUnits5(assetsOut, assetDecimals)}`);
|
|
6492
6891
|
} catch (err) {
|
|
6493
6892
|
spinner.fail("Redeem failed");
|
|
6494
|
-
console.error(
|
|
6893
|
+
console.error(chalk15.red(formatContractError(err)));
|
|
6495
6894
|
process.exit(1);
|
|
6496
6895
|
}
|
|
6497
6896
|
});
|
|
@@ -6509,17 +6908,17 @@ ${info.name} (${info.type})`);
|
|
|
6509
6908
|
}
|
|
6510
6909
|
});
|
|
6511
6910
|
try {
|
|
6512
|
-
const { registerChatCommands } = await import("./chat-
|
|
6911
|
+
const { registerChatCommands } = await import("./chat-TD6V5LFD.js");
|
|
6513
6912
|
registerChatCommands(program);
|
|
6514
6913
|
} catch {
|
|
6515
6914
|
program.command("chat <name> [action] [actionArgs...]").description("Syndicate chat (XMTP) \u2014 requires @xmtp/cli").action(() => {
|
|
6516
|
-
console.error(
|
|
6517
|
-
console.error(
|
|
6518
|
-
console.error(
|
|
6915
|
+
console.error(chalk15.red("XMTP CLI not available."));
|
|
6916
|
+
console.error(chalk15.dim("Install with: npm install -g @xmtp/cli"));
|
|
6917
|
+
console.error(chalk15.dim("Or reinstall: npm i -g @sherwoodagent/cli"));
|
|
6519
6918
|
process.exit(1);
|
|
6520
6919
|
});
|
|
6521
6920
|
}
|
|
6522
|
-
var { registerSessionCommands } = await import("./session-
|
|
6921
|
+
var { registerSessionCommands } = await import("./session-IYT3N7MG.js");
|
|
6523
6922
|
registerSessionCommands(program);
|
|
6524
6923
|
registerVeniceCommands(program);
|
|
6525
6924
|
registerAllowanceCommands(program);
|
|
@@ -6527,11 +6926,11 @@ registerIdentityCommands(program);
|
|
|
6527
6926
|
registerProposalCommands(program);
|
|
6528
6927
|
registerGovernorCommands(program);
|
|
6529
6928
|
registerGuardianCommands(program);
|
|
6530
|
-
var { registerResearchCommands } = await import("./research-
|
|
6929
|
+
var { registerResearchCommands } = await import("./research-WBZU4QNU.js");
|
|
6531
6930
|
registerResearchCommands(program);
|
|
6532
|
-
var { registerAgentCommands } = await import("./agent-
|
|
6931
|
+
var { registerAgentCommands } = await import("./agent-E5GHURKJ.js");
|
|
6533
6932
|
registerAgentCommands(program);
|
|
6534
|
-
var { registerTradeCommands } = await import("./trade-
|
|
6933
|
+
var { registerTradeCommands } = await import("./trade-PTYLNJBU.js");
|
|
6535
6934
|
registerTradeCommands(program);
|
|
6536
6935
|
registerGridCommand(program);
|
|
6537
6936
|
var configCmd = program.command("config");
|
|
@@ -6540,58 +6939,58 @@ configCmd.command("set").description("Save settings to ~/.sherwood/config.json (
|
|
|
6540
6939
|
if (opts.privateKey) {
|
|
6541
6940
|
setPrivateKey(opts.privateKey);
|
|
6542
6941
|
const account = getAccount();
|
|
6543
|
-
console.log(
|
|
6544
|
-
console.log(
|
|
6942
|
+
console.log(chalk15.green("Private key saved to ~/.sherwood/config.json"));
|
|
6943
|
+
console.log(chalk15.dim(` Wallet: ${account.address}`));
|
|
6545
6944
|
saved = true;
|
|
6546
6945
|
}
|
|
6547
6946
|
if (opts.vault) {
|
|
6548
6947
|
const chainId = getChain().id;
|
|
6549
6948
|
setChainContract(chainId, "vault", opts.vault);
|
|
6550
|
-
console.log(
|
|
6551
|
-
console.log(
|
|
6949
|
+
console.log(chalk15.green(`Vault saved to ~/.sherwood/config.json (chain ${chainId})`));
|
|
6950
|
+
console.log(chalk15.dim(` Vault: ${opts.vault}`));
|
|
6552
6951
|
saved = true;
|
|
6553
6952
|
}
|
|
6554
6953
|
if (opts.rpc) {
|
|
6555
6954
|
const network = getNetwork();
|
|
6556
6955
|
setConfigRpcUrl(network, opts.rpc);
|
|
6557
|
-
console.log(
|
|
6558
|
-
console.log(
|
|
6956
|
+
console.log(chalk15.green(`RPC URL saved for ${network}`));
|
|
6957
|
+
console.log(chalk15.dim(` RPC: ${opts.rpc}`));
|
|
6559
6958
|
saved = true;
|
|
6560
6959
|
}
|
|
6561
6960
|
if (opts.notifyTo) {
|
|
6562
6961
|
setNotifyTo(opts.notifyTo);
|
|
6563
|
-
console.log(
|
|
6564
|
-
console.log(
|
|
6962
|
+
console.log(chalk15.green("Notify destination saved to ~/.sherwood/config.json"));
|
|
6963
|
+
console.log(chalk15.dim(` Notify to: ${opts.notifyTo}`));
|
|
6565
6964
|
saved = true;
|
|
6566
6965
|
}
|
|
6567
6966
|
if (opts.uniswapApiKey) {
|
|
6568
6967
|
setUniswapApiKey(opts.uniswapApiKey);
|
|
6569
|
-
console.log(
|
|
6968
|
+
console.log(chalk15.green("Uniswap API key saved to ~/.sherwood/config.json"));
|
|
6570
6969
|
saved = true;
|
|
6571
6970
|
}
|
|
6572
6971
|
if (opts.veniceApiKey) {
|
|
6573
6972
|
setVeniceApiKey(opts.veniceApiKey);
|
|
6574
|
-
console.log(
|
|
6973
|
+
console.log(chalk15.green("Venice API key saved to ~/.sherwood/config.json"));
|
|
6575
6974
|
saved = true;
|
|
6576
6975
|
}
|
|
6577
6976
|
if (opts.anthropicApiKey) {
|
|
6578
6977
|
setAnthropicApiKey(opts.anthropicApiKey);
|
|
6579
|
-
console.log(
|
|
6978
|
+
console.log(chalk15.green("Anthropic API key saved to ~/.sherwood/config.json"));
|
|
6580
6979
|
saved = true;
|
|
6581
6980
|
}
|
|
6582
6981
|
if (opts.xmtpGroup) {
|
|
6583
6982
|
const parts = opts.xmtpGroup.split(":");
|
|
6584
6983
|
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
|
6585
|
-
console.log(
|
|
6984
|
+
console.log(chalk15.red("Format: --xmtp-group <subdomain>:<groupId>"));
|
|
6586
6985
|
process.exit(1);
|
|
6587
6986
|
}
|
|
6588
6987
|
cacheGroupId(parts[0], parts[1]);
|
|
6589
|
-
console.log(
|
|
6590
|
-
console.log(
|
|
6988
|
+
console.log(chalk15.green(`XMTP group ID cached for ${parts[0]}`));
|
|
6989
|
+
console.log(chalk15.dim(` Group ID: ${parts[1]}`));
|
|
6591
6990
|
saved = true;
|
|
6592
6991
|
}
|
|
6593
6992
|
if (!saved) {
|
|
6594
|
-
console.log(
|
|
6993
|
+
console.log(chalk15.red("Provide at least one of: --private-key, --vault, --rpc, --notify-to, --uniswap-api-key, --venice-api-key, --anthropic-api-key, --xmtp-group"));
|
|
6595
6994
|
process.exit(1);
|
|
6596
6995
|
}
|
|
6597
6996
|
});
|
|
@@ -6602,25 +7001,25 @@ configCmd.command("show").description("Display current config for the active net
|
|
|
6602
7001
|
const config = loadConfig();
|
|
6603
7002
|
const customRpc = config.rpc?.[network];
|
|
6604
7003
|
console.log();
|
|
6605
|
-
console.log(
|
|
6606
|
-
console.log(
|
|
6607
|
-
console.log(` Network: ${
|
|
6608
|
-
console.log(` RPC: ${customRpc ?
|
|
6609
|
-
console.log(` Wallet: ${config.privateKey ?
|
|
6610
|
-
console.log(` Agent ID: ${config.agentId ??
|
|
6611
|
-
console.log(` Vault: ${contracts.vault ??
|
|
6612
|
-
console.log(` Uniswap: ${getUniswapApiKey() ?
|
|
6613
|
-
console.log(` Venice: ${getVeniceApiKey() ?
|
|
7004
|
+
console.log(chalk15.bold(`Sherwood Config`));
|
|
7005
|
+
console.log(chalk15.dim("\u2500".repeat(50)));
|
|
7006
|
+
console.log(` Network: ${chalk15.cyan(network)} (chain ${chainId})`);
|
|
7007
|
+
console.log(` RPC: ${customRpc ? chalk15.green(customRpc) : chalk15.dim("default")}`);
|
|
7008
|
+
console.log(` Wallet: ${config.privateKey ? chalk15.green("configured") : chalk15.dim("not set")}`);
|
|
7009
|
+
console.log(` Agent ID: ${config.agentId ?? chalk15.dim("not set")}`);
|
|
7010
|
+
console.log(` Vault: ${contracts.vault ?? chalk15.dim("not set")}`);
|
|
7011
|
+
console.log(` Uniswap: ${getUniswapApiKey() ? chalk15.green("API key configured") : chalk15.dim("not set")}`);
|
|
7012
|
+
console.log(` Venice: ${getVeniceApiKey() ? chalk15.green("API key configured") : chalk15.dim("not set")}`);
|
|
6614
7013
|
const groupEntries = Object.entries(config.groupCache || {}).filter(([, v]) => v);
|
|
6615
7014
|
if (groupEntries.length > 0) {
|
|
6616
7015
|
console.log();
|
|
6617
|
-
console.log(
|
|
7016
|
+
console.log(chalk15.bold(" XMTP Groups"));
|
|
6618
7017
|
for (const [sub, gid] of groupEntries) {
|
|
6619
|
-
console.log(` ${sub}: ${
|
|
7018
|
+
console.log(` ${sub}: ${chalk15.cyan(gid)}`);
|
|
6620
7019
|
}
|
|
6621
7020
|
}
|
|
6622
7021
|
console.log();
|
|
6623
|
-
console.log(
|
|
7022
|
+
console.log(chalk15.dim(" Config file: ~/.sherwood/config.json"));
|
|
6624
7023
|
console.log();
|
|
6625
7024
|
});
|
|
6626
7025
|
program.parse();
|