@sherwoodagent/cli 0.42.0 → 0.47.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/{addresses-DCQMJJFI.js → addresses-DOWLGXGE.js} +4 -4
- package/dist/{agent-5YEJ6W6W.js → agent-4HKHLQEJ.js} +3035 -2029
- package/dist/agent-4HKHLQEJ.js.map +1 -0
- package/dist/autoresearch-IA7HGSNW.js +267 -0
- package/dist/autoresearch-IA7HGSNW.js.map +1 -0
- package/dist/{chat-UWJ6JBFE.js → chat-MRTPUZ3X.js} +8 -8
- package/dist/{chunk-FOOZC4AM.js → chunk-2LRC7I33.js} +2 -2
- package/dist/{chunk-4EHI4SKM.js → chunk-2W4KGGPU.js} +11 -1
- package/dist/chunk-2W4KGGPU.js.map +1 -0
- package/dist/chunk-7HJXZ4NM.js +189 -0
- package/dist/chunk-7HJXZ4NM.js.map +1 -0
- package/dist/{chunk-JNDEZFXW.js → chunk-E6LH2FJ6.js} +4 -4
- package/dist/{chunk-XAUMJZU2.js → chunk-FWC57PT7.js} +2 -2
- package/dist/{chunk-4OAZ7TP5.js → chunk-IS2CLLUT.js} +4 -4
- package/dist/{chunk-T2IA6CCW.js → chunk-KJ35EF7N.js} +4 -4
- package/dist/{chunk-GMVDHSCF.js → chunk-OHQOTSFG.js} +39 -3
- package/dist/chunk-OHQOTSFG.js.map +1 -0
- package/dist/chunk-R2P4C2I7.js +560 -0
- package/dist/chunk-R2P4C2I7.js.map +1 -0
- package/dist/{chunk-5Q3VT6NQ.js → chunk-R7X33D2S.js} +5 -5
- package/dist/{chunk-6U3A6ZJ2.js → chunk-WMZEOR5W.js} +6 -127
- package/dist/chunk-WMZEOR5W.js.map +1 -0
- package/dist/{chunk-SWKHSKDH.js → chunk-XDBRKW7I.js} +3 -3
- package/dist/chunk-YTYXBX7B.js +495 -0
- package/dist/chunk-YTYXBX7B.js.map +1 -0
- package/dist/{client-DRVUILER.js → client-UHHCB6EZ.js} +4 -4
- package/dist/{config-YRERDZSL.js → config-REASKDIK.js} +6 -2
- package/dist/{eas-KBU2BAOD.js → eas-PEEWGSAO.js} +6 -6
- package/dist/{governor-KH7RNH2E.js → governor-MG5VVSU4.js} +6 -6
- package/dist/index.js +1012 -265
- package/dist/index.js.map +1 -1
- package/dist/{network-3HB46JMQ.js → network-3ZU7UBDU.js} +3 -3
- package/dist/{price-TTQWI672.js → price-WCCXWPDM.js} +5 -5
- package/dist/{research-J3HYU3N6.js → research-QBKISW2M.js} +9 -9
- package/dist/research-YJWSQS2A.js +14 -0
- package/dist/serve-6MYBOAX6.js +222 -0
- package/dist/serve-6MYBOAX6.js.map +1 -0
- package/dist/{session-NUPHC6ZN.js → session-FPCPCVQX.js} +10 -10
- package/dist/summary-formatter-JIGWN4QS.js +180 -0
- package/dist/summary-formatter-JIGWN4QS.js.map +1 -0
- package/dist/{trade-VGECC4QZ.js → trade-NO6CNWCA.js} +19 -17
- package/dist/{trade-VGECC4QZ.js.map → trade-NO6CNWCA.js.map} +1 -1
- package/dist/{xmtp-JO7W6RFK.js → xmtp-ACB2W573.js} +6 -6
- package/package.json +5 -2
- package/dist/agent-5YEJ6W6W.js.map +0 -1
- package/dist/chunk-4EHI4SKM.js.map +0 -1
- package/dist/chunk-6U3A6ZJ2.js.map +0 -1
- package/dist/chunk-GMVDHSCF.js.map +0 -1
- package/dist/research-NR4VC5LN.js +0 -14
- /package/dist/{addresses-DCQMJJFI.js.map → addresses-DOWLGXGE.js.map} +0 -0
- /package/dist/{chat-UWJ6JBFE.js.map → chat-MRTPUZ3X.js.map} +0 -0
- /package/dist/{chunk-FOOZC4AM.js.map → chunk-2LRC7I33.js.map} +0 -0
- /package/dist/{chunk-JNDEZFXW.js.map → chunk-E6LH2FJ6.js.map} +0 -0
- /package/dist/{chunk-XAUMJZU2.js.map → chunk-FWC57PT7.js.map} +0 -0
- /package/dist/{chunk-4OAZ7TP5.js.map → chunk-IS2CLLUT.js.map} +0 -0
- /package/dist/{chunk-T2IA6CCW.js.map → chunk-KJ35EF7N.js.map} +0 -0
- /package/dist/{chunk-5Q3VT6NQ.js.map → chunk-R7X33D2S.js.map} +0 -0
- /package/dist/{chunk-SWKHSKDH.js.map → chunk-XDBRKW7I.js.map} +0 -0
- /package/dist/{client-DRVUILER.js.map → client-UHHCB6EZ.js.map} +0 -0
- /package/dist/{config-YRERDZSL.js.map → config-REASKDIK.js.map} +0 -0
- /package/dist/{eas-KBU2BAOD.js.map → eas-PEEWGSAO.js.map} +0 -0
- /package/dist/{governor-KH7RNH2E.js.map → governor-MG5VVSU4.js.map} +0 -0
- /package/dist/{network-3HB46JMQ.js.map → network-3ZU7UBDU.js.map} +0 -0
- /package/dist/{price-TTQWI672.js.map → price-WCCXWPDM.js.map} +0 -0
- /package/dist/{research-J3HYU3N6.js.map → research-QBKISW2M.js.map} +0 -0
- /package/dist/{research-NR4VC5LN.js.map → research-YJWSQS2A.js.map} +0 -0
- /package/dist/{session-NUPHC6ZN.js.map → session-FPCPCVQX.js.map} +0 -0
- /package/dist/{xmtp-JO7W6RFK.js.map → xmtp-ACB2W573.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
HyperliquidProvider,
|
|
4
|
+
getLatestSignals
|
|
5
|
+
} from "./chunk-R2P4C2I7.js";
|
|
2
6
|
import {
|
|
3
7
|
UniswapProvider,
|
|
4
8
|
applySlippage,
|
|
5
|
-
chatCompletion,
|
|
6
|
-
checkApiKeyValid,
|
|
7
9
|
encodeSwapPath,
|
|
8
10
|
getMultiHopQuote,
|
|
9
|
-
getQuote
|
|
11
|
+
getQuote
|
|
12
|
+
} from "./chunk-WMZEOR5W.js";
|
|
13
|
+
import {
|
|
14
|
+
chatCompletion,
|
|
15
|
+
checkApiKeyValid,
|
|
10
16
|
listModels,
|
|
11
17
|
provisionApiKey
|
|
12
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-7HJXZ4NM.js";
|
|
13
19
|
import {
|
|
14
20
|
createApproval,
|
|
15
21
|
createJoinRequest,
|
|
@@ -20,14 +26,14 @@ import {
|
|
|
20
26
|
queryJoinRequests,
|
|
21
27
|
revokeJoinRequest,
|
|
22
28
|
stripReferrerPrefix
|
|
23
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-E6LH2FJ6.js";
|
|
24
30
|
import {
|
|
25
31
|
createSyndicate,
|
|
26
32
|
getActiveSyndicates,
|
|
27
33
|
getSyndicate,
|
|
28
34
|
subdomainExists,
|
|
29
35
|
updateMetadata
|
|
30
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-IS2CLLUT.js";
|
|
31
37
|
import {
|
|
32
38
|
approveDepositor,
|
|
33
39
|
deposit,
|
|
@@ -51,7 +57,7 @@ import {
|
|
|
51
57
|
searchSyndicateAcrossChains,
|
|
52
58
|
setTextRecord,
|
|
53
59
|
setVaultAddress
|
|
54
|
-
} from "./chunk-
|
|
60
|
+
} from "./chunk-R7X33D2S.js";
|
|
55
61
|
import {
|
|
56
62
|
PROPOSAL_STATE,
|
|
57
63
|
PROPOSAL_STATES,
|
|
@@ -83,7 +89,7 @@ import {
|
|
|
83
89
|
setVotingPeriod,
|
|
84
90
|
settleProposal,
|
|
85
91
|
vote
|
|
86
|
-
} from "./chunk-
|
|
92
|
+
} from "./chunk-KJ35EF7N.js";
|
|
87
93
|
import {
|
|
88
94
|
AERODROME,
|
|
89
95
|
AGENT_REGISTRY,
|
|
@@ -95,7 +101,7 @@ import {
|
|
|
95
101
|
TOKENS,
|
|
96
102
|
UNISWAP,
|
|
97
103
|
VENICE
|
|
98
|
-
} from "./chunk-
|
|
104
|
+
} from "./chunk-FWC57PT7.js";
|
|
99
105
|
import {
|
|
100
106
|
formatContractError,
|
|
101
107
|
getAccount,
|
|
@@ -104,7 +110,7 @@ import {
|
|
|
104
110
|
sendTxWithRetry,
|
|
105
111
|
waitForReceipt,
|
|
106
112
|
writeContractWithRetry
|
|
107
|
-
} from "./chunk-
|
|
113
|
+
} from "./chunk-XDBRKW7I.js";
|
|
108
114
|
import {
|
|
109
115
|
CHAIN_REGISTRY,
|
|
110
116
|
VALID_NETWORKS,
|
|
@@ -115,7 +121,7 @@ import {
|
|
|
115
121
|
isTestnet,
|
|
116
122
|
setNetwork,
|
|
117
123
|
withNetwork
|
|
118
|
-
} from "./chunk-
|
|
124
|
+
} from "./chunk-2LRC7I33.js";
|
|
119
125
|
import {
|
|
120
126
|
addSyndicate,
|
|
121
127
|
cacheGroupId,
|
|
@@ -131,6 +137,7 @@ import {
|
|
|
131
137
|
getVeniceApiKey,
|
|
132
138
|
loadConfig,
|
|
133
139
|
setAgentId,
|
|
140
|
+
setAnthropicApiKey,
|
|
134
141
|
setChainContract,
|
|
135
142
|
setConfigRpcUrl,
|
|
136
143
|
setNotifyTo,
|
|
@@ -138,7 +145,7 @@ import {
|
|
|
138
145
|
setPrivateKey,
|
|
139
146
|
setUniswapApiKey,
|
|
140
147
|
setVeniceApiKey
|
|
141
|
-
} from "./chunk-
|
|
148
|
+
} from "./chunk-2W4KGGPU.js";
|
|
142
149
|
import {
|
|
143
150
|
BASE_STRATEGY_ABI,
|
|
144
151
|
ERC20_ABI,
|
|
@@ -158,7 +165,7 @@ import { config as loadDotenv } from "dotenv";
|
|
|
158
165
|
import { createRequire } from "module";
|
|
159
166
|
import { Command, Option } from "commander";
|
|
160
167
|
import { parseUnits as parseUnits6, formatUnits as formatUnits5, isAddress as isAddress6 } from "viem";
|
|
161
|
-
import
|
|
168
|
+
import chalk13 from "chalk";
|
|
162
169
|
import ora8 from "ora";
|
|
163
170
|
import { input, confirm, select } from "@inquirer/prompts";
|
|
164
171
|
|
|
@@ -1447,9 +1454,9 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
1447
1454
|
console.error(chalk.red("Missing --name, --performance-fee, or --duration. Use --write-calls to skip proposal submission."));
|
|
1448
1455
|
process.exit(1);
|
|
1449
1456
|
}
|
|
1450
|
-
const { propose: propose2 } = await import("./governor-
|
|
1457
|
+
const { propose: propose2 } = await import("./governor-MG5VVSU4.js");
|
|
1451
1458
|
const { pinJSON } = await import("./ipfs-IGXLLJCF.js");
|
|
1452
|
-
const { parseDuration: parseDuration2 } = await import("./governor-
|
|
1459
|
+
const { parseDuration: parseDuration2 } = await import("./governor-MG5VVSU4.js");
|
|
1453
1460
|
const performanceFeeBps = BigInt(opts.performanceFee);
|
|
1454
1461
|
if (performanceFeeBps < 0n || performanceFeeBps > 10000n) {
|
|
1455
1462
|
console.error(chalk.red("--performance-fee must be 0-10000 (basis points)"));
|
|
@@ -1556,7 +1563,7 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
1556
1563
|
let prices = [];
|
|
1557
1564
|
let priceSource = null;
|
|
1558
1565
|
try {
|
|
1559
|
-
const { getTokenPricesInAsset } = await import("./price-
|
|
1566
|
+
const { getTokenPricesInAsset } = await import("./price-WCCXWPDM.js");
|
|
1560
1567
|
prices = await getTokenPricesInAsset({
|
|
1561
1568
|
tokens: allocations.map((a, i) => ({
|
|
1562
1569
|
token: a.token,
|
|
@@ -1986,7 +1993,7 @@ function registerVeniceCommands(program2) {
|
|
|
1986
1993
|
console.log(chalk2.dim(` Key: ${apiKey.slice(0, 8)}...${apiKey.slice(-4)}`));
|
|
1987
1994
|
console.log(chalk2.dim(" Saved to ~/.sherwood/config.json"));
|
|
1988
1995
|
try {
|
|
1989
|
-
const { createVeniceProvisionAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-
|
|
1996
|
+
const { createVeniceProvisionAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-PEEWGSAO.js");
|
|
1990
1997
|
const { uid } = await createVeniceProvisionAttestation(account.address);
|
|
1991
1998
|
if (uid !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
|
|
1992
1999
|
console.log(chalk2.dim(` Attested: ${getEasScanUrl2(uid)}`));
|
|
@@ -2342,10 +2349,10 @@ ${opts.prompt}`;
|
|
|
2342
2349
|
console.log(chalk2.dim(`Model: ${result.model} | Tokens: ${result.usage.promptTokens} in, ${result.usage.completionTokens} out, ${result.usage.totalTokens} total`));
|
|
2343
2350
|
}
|
|
2344
2351
|
try {
|
|
2345
|
-
const { createVeniceInferenceAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-
|
|
2352
|
+
const { createVeniceInferenceAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-PEEWGSAO.js");
|
|
2346
2353
|
const { keccak256, toHex, isAddress: isAddr } = await import("viem");
|
|
2347
|
-
const { getChainContracts: getChainContracts2 } = await import("./config-
|
|
2348
|
-
const { getChain: getActiveChain } = await import("./network-
|
|
2354
|
+
const { getChainContracts: getChainContracts2 } = await import("./config-REASKDIK.js");
|
|
2355
|
+
const { getChain: getActiveChain } = await import("./network-3ZU7UBDU.js");
|
|
2349
2356
|
const vaultRecipient = opts.vault && isAddr(opts.vault) ? opts.vault : getChainContracts2(getActiveChain().id).vault;
|
|
2350
2357
|
const promptHash = keccak256(toHex(userContent)).slice(0, 18);
|
|
2351
2358
|
const { uid } = await createVeniceInferenceAttestation(
|
|
@@ -3518,8 +3525,8 @@ function printSimulationResult(result, callType) {
|
|
|
3518
3525
|
}
|
|
3519
3526
|
async function sendSimulationAlert(subdomain, proposalId, vault, execResult, settleResult) {
|
|
3520
3527
|
try {
|
|
3521
|
-
const xmtp = await import("./xmtp-
|
|
3522
|
-
const { getAccount: getAccount2 } = await import("./client-
|
|
3528
|
+
const xmtp = await import("./xmtp-ACB2W573.js");
|
|
3529
|
+
const { getAccount: getAccount2 } = await import("./client-UHHCB6EZ.js");
|
|
3523
3530
|
const group = await xmtp.getGroup("", subdomain);
|
|
3524
3531
|
const allRisks = [...execResult.risks, ...settleResult?.risks ?? []];
|
|
3525
3532
|
const criticals = allRisks.filter((r) => r.level === "critical");
|
|
@@ -4610,6 +4617,740 @@ function registerGuardianCommands(program2) {
|
|
|
4610
4617
|
});
|
|
4611
4618
|
}
|
|
4612
4619
|
|
|
4620
|
+
// src/commands/grid.ts
|
|
4621
|
+
import chalk12 from "chalk";
|
|
4622
|
+
|
|
4623
|
+
// src/grid/loop.ts
|
|
4624
|
+
import chalk11 from "chalk";
|
|
4625
|
+
import { appendFile, mkdir as mkdir3 } from "fs/promises";
|
|
4626
|
+
import { join as join3 } from "path";
|
|
4627
|
+
import { homedir as homedir3 } from "os";
|
|
4628
|
+
|
|
4629
|
+
// src/grid/manager.ts
|
|
4630
|
+
import chalk9 from "chalk";
|
|
4631
|
+
|
|
4632
|
+
// src/grid/portfolio.ts
|
|
4633
|
+
import { readFile, writeFile, rename, mkdir } from "fs/promises";
|
|
4634
|
+
import { join, dirname } from "path";
|
|
4635
|
+
import { homedir } from "os";
|
|
4636
|
+
var GRID_STATE_PATH = join(homedir(), ".sherwood", "grid", "portfolio.json");
|
|
4637
|
+
function emptyStats() {
|
|
4638
|
+
return {
|
|
4639
|
+
totalRoundTrips: 0,
|
|
4640
|
+
totalPnlUsd: 0,
|
|
4641
|
+
todayPnlUsd: 0,
|
|
4642
|
+
totalFills: 0,
|
|
4643
|
+
todayFills: 0,
|
|
4644
|
+
lastDailyReset: 0,
|
|
4645
|
+
lastRebalanceAt: 0
|
|
4646
|
+
};
|
|
4647
|
+
}
|
|
4648
|
+
var GridPortfolio = class {
|
|
4649
|
+
state = null;
|
|
4650
|
+
/** Load grid state from disk. Returns null if no grid initialized yet. */
|
|
4651
|
+
async load() {
|
|
4652
|
+
try {
|
|
4653
|
+
const raw = await readFile(GRID_STATE_PATH, "utf-8");
|
|
4654
|
+
this.state = JSON.parse(raw);
|
|
4655
|
+
return this.state;
|
|
4656
|
+
} catch {
|
|
4657
|
+
return null;
|
|
4658
|
+
}
|
|
4659
|
+
}
|
|
4660
|
+
/** Save grid state to disk (atomic write). */
|
|
4661
|
+
async save(state) {
|
|
4662
|
+
this.state = state;
|
|
4663
|
+
await mkdir(dirname(GRID_STATE_PATH), { recursive: true });
|
|
4664
|
+
const tmp = `${GRID_STATE_PATH}.tmp.${process.pid}`;
|
|
4665
|
+
await writeFile(tmp, JSON.stringify(state, null, 2), "utf-8");
|
|
4666
|
+
await rename(tmp, GRID_STATE_PATH);
|
|
4667
|
+
}
|
|
4668
|
+
/**
|
|
4669
|
+
* Initialize the grid by carving capital from the directional portfolio.
|
|
4670
|
+
* Called once on first startup when grid-portfolio.json doesn't exist.
|
|
4671
|
+
*
|
|
4672
|
+
* Returns the USD amount carved out (caller must deduct from directional portfolio).
|
|
4673
|
+
*/
|
|
4674
|
+
async initialize(capital, config) {
|
|
4675
|
+
const allocation = capital;
|
|
4676
|
+
const grids = config.tokens.map((token) => ({
|
|
4677
|
+
token,
|
|
4678
|
+
levels: [],
|
|
4679
|
+
openFills: [],
|
|
4680
|
+
allocation: allocation * (config.tokenSplit[token] ?? 1 / config.tokens.length),
|
|
4681
|
+
stats: emptyStats(),
|
|
4682
|
+
centerPrice: 0,
|
|
4683
|
+
atr: 0
|
|
4684
|
+
}));
|
|
4685
|
+
const state = {
|
|
4686
|
+
totalAllocation: allocation,
|
|
4687
|
+
grids,
|
|
4688
|
+
paused: false,
|
|
4689
|
+
pauseReason: "",
|
|
4690
|
+
initializedAt: Date.now()
|
|
4691
|
+
};
|
|
4692
|
+
await this.save(state);
|
|
4693
|
+
this.state = state;
|
|
4694
|
+
}
|
|
4695
|
+
/** Get current state (must load() first). */
|
|
4696
|
+
getState() {
|
|
4697
|
+
return this.state;
|
|
4698
|
+
}
|
|
4699
|
+
/** Reset daily counters if UTC day boundary crossed. */
|
|
4700
|
+
resetDailyStats(state) {
|
|
4701
|
+
const now = Date.now();
|
|
4702
|
+
const todayMidnight = /* @__PURE__ */ new Date();
|
|
4703
|
+
todayMidnight.setUTCHours(0, 0, 0, 0);
|
|
4704
|
+
const todayMs = todayMidnight.getTime();
|
|
4705
|
+
let changed = false;
|
|
4706
|
+
for (const grid of state.grids) {
|
|
4707
|
+
if (!grid.stats.lastDailyReset || grid.stats.lastDailyReset < todayMs) {
|
|
4708
|
+
grid.stats.todayPnlUsd = 0;
|
|
4709
|
+
grid.stats.todayFills = 0;
|
|
4710
|
+
grid.stats.lastDailyReset = now;
|
|
4711
|
+
changed = true;
|
|
4712
|
+
}
|
|
4713
|
+
}
|
|
4714
|
+
return changed;
|
|
4715
|
+
}
|
|
4716
|
+
/** Check if grid should be paused (pool dropped below threshold). */
|
|
4717
|
+
checkPauseThreshold(state, config) {
|
|
4718
|
+
const currentValue = state.grids.reduce((sum, g) => {
|
|
4719
|
+
const openFillValue = g.openFills.filter((f) => !f.closed).reduce((s, f) => s + f.quantity * f.buyPrice, 0);
|
|
4720
|
+
return sum + g.allocation + openFillValue;
|
|
4721
|
+
}, 0);
|
|
4722
|
+
const dropPct = 1 - currentValue / state.totalAllocation;
|
|
4723
|
+
if (dropPct >= config.pauseThresholdPct) {
|
|
4724
|
+
state.paused = true;
|
|
4725
|
+
state.pauseReason = `Grid pool dropped ${(dropPct * 100).toFixed(1)}% (threshold: ${(config.pauseThresholdPct * 100).toFixed(0)}%)`;
|
|
4726
|
+
return true;
|
|
4727
|
+
}
|
|
4728
|
+
return false;
|
|
4729
|
+
}
|
|
4730
|
+
/** Aggregate stats across all token grids. */
|
|
4731
|
+
aggregateStats(state) {
|
|
4732
|
+
return {
|
|
4733
|
+
totalPnlUsd: state.grids.reduce((s, g) => s + g.stats.totalPnlUsd, 0),
|
|
4734
|
+
todayPnlUsd: state.grids.reduce((s, g) => s + g.stats.todayPnlUsd, 0),
|
|
4735
|
+
todayFills: state.grids.reduce((s, g) => s + g.stats.todayFills, 0),
|
|
4736
|
+
totalRoundTrips: state.grids.reduce((s, g) => s + g.stats.totalRoundTrips, 0)
|
|
4737
|
+
};
|
|
4738
|
+
}
|
|
4739
|
+
};
|
|
4740
|
+
|
|
4741
|
+
// src/grid/manager.ts
|
|
4742
|
+
var GridManager = class {
|
|
4743
|
+
config;
|
|
4744
|
+
portfolio;
|
|
4745
|
+
hl;
|
|
4746
|
+
constructor(config) {
|
|
4747
|
+
this.config = config;
|
|
4748
|
+
this.portfolio = new GridPortfolio();
|
|
4749
|
+
this.hl = new HyperliquidProvider();
|
|
4750
|
+
}
|
|
4751
|
+
/** Load or initialize grid state.
|
|
4752
|
+
* Also syncs new tokens added to config after initial grid setup. */
|
|
4753
|
+
async init(totalPortfolioValue) {
|
|
4754
|
+
const existing = await this.portfolio.load();
|
|
4755
|
+
if (!existing) {
|
|
4756
|
+
await this.portfolio.initialize(totalPortfolioValue, this.config);
|
|
4757
|
+
return;
|
|
4758
|
+
}
|
|
4759
|
+
let addedAllocation = 0;
|
|
4760
|
+
for (const token of this.config.tokens) {
|
|
4761
|
+
if (!existing.grids.find((g) => g.token === token)) {
|
|
4762
|
+
const split = this.config.tokenSplit[token] ?? 1 / this.config.tokens.length;
|
|
4763
|
+
const alloc = existing.totalAllocation * split;
|
|
4764
|
+
existing.grids.push({
|
|
4765
|
+
token,
|
|
4766
|
+
levels: [],
|
|
4767
|
+
openFills: [],
|
|
4768
|
+
allocation: alloc,
|
|
4769
|
+
stats: { totalRoundTrips: 0, totalPnlUsd: 0, todayPnlUsd: 0, totalFills: 0, todayFills: 0, lastDailyReset: 0, lastRebalanceAt: 0 },
|
|
4770
|
+
centerPrice: 0,
|
|
4771
|
+
atr: 0
|
|
4772
|
+
});
|
|
4773
|
+
addedAllocation += alloc;
|
|
4774
|
+
console.error(` [grid] Added new token grid: ${token} ($${alloc.toFixed(0)} allocation)`);
|
|
4775
|
+
}
|
|
4776
|
+
}
|
|
4777
|
+
if (addedAllocation > 0) {
|
|
4778
|
+
await this.portfolio.save(existing);
|
|
4779
|
+
}
|
|
4780
|
+
}
|
|
4781
|
+
/**
|
|
4782
|
+
* Run one grid tick — called every cycle from the agent loop.
|
|
4783
|
+
*
|
|
4784
|
+
* 1. Load state + reset daily counters
|
|
4785
|
+
* 2. For each token grid: fetch price, simulate fills, check rebalance
|
|
4786
|
+
* 3. Persist state
|
|
4787
|
+
*
|
|
4788
|
+
* Returns a summary for the cycle log.
|
|
4789
|
+
*/
|
|
4790
|
+
async tick(prices, regime) {
|
|
4791
|
+
const state = await this.portfolio.load();
|
|
4792
|
+
if (!state || state.paused || !this.config.enabled) {
|
|
4793
|
+
return { fills: 0, roundTrips: 0, pnlUsd: 0, paused: state?.paused ?? false };
|
|
4794
|
+
}
|
|
4795
|
+
if (regime === "high-volatility") {
|
|
4796
|
+
console.error(chalk9.dim(` [grid] Paused: high-volatility regime \u2014 too risky for grid`));
|
|
4797
|
+
return { fills: 0, roundTrips: 0, pnlUsd: 0, paused: false };
|
|
4798
|
+
}
|
|
4799
|
+
this.portfolio.resetDailyStats(state);
|
|
4800
|
+
let totalFills = 0;
|
|
4801
|
+
let totalRoundTrips = 0;
|
|
4802
|
+
let totalPnl = 0;
|
|
4803
|
+
for (const grid of state.grids) {
|
|
4804
|
+
const price = prices[grid.token];
|
|
4805
|
+
if (!price || price <= 0) continue;
|
|
4806
|
+
if (grid.levels.length === 0 || this.needsFullRebuild(grid)) {
|
|
4807
|
+
await this.buildGrid(grid, price);
|
|
4808
|
+
}
|
|
4809
|
+
const { fills, roundTrips, pnlUsd } = this.simulateFills(grid, price);
|
|
4810
|
+
totalFills += fills;
|
|
4811
|
+
totalRoundTrips += roundTrips;
|
|
4812
|
+
totalPnl += pnlUsd;
|
|
4813
|
+
if (this.needsShift(grid, price)) {
|
|
4814
|
+
await this.shiftGrid(grid, price);
|
|
4815
|
+
}
|
|
4816
|
+
}
|
|
4817
|
+
this.portfolio.checkPauseThreshold(state, this.config);
|
|
4818
|
+
await this.portfolio.save(state);
|
|
4819
|
+
return { fills: totalFills, roundTrips: totalRoundTrips, pnlUsd: totalPnl, paused: false };
|
|
4820
|
+
}
|
|
4821
|
+
/** Build a fresh grid centered on the current price using ATR. */
|
|
4822
|
+
async buildGrid(grid, currentPrice) {
|
|
4823
|
+
const candles = await this.hl.getCandles(grid.token, "4h", 14 * 24 * 60 * 60 * 1e3);
|
|
4824
|
+
if (!candles || candles.length < this.config.atrPeriod) {
|
|
4825
|
+
console.error(chalk9.dim(` [grid] Cannot build grid for ${grid.token}: insufficient candle data`));
|
|
4826
|
+
return;
|
|
4827
|
+
}
|
|
4828
|
+
const signals = getLatestSignals(candles);
|
|
4829
|
+
const atr = signals.atr;
|
|
4830
|
+
if (!Number.isFinite(atr) || atr <= 0) {
|
|
4831
|
+
console.error(chalk9.dim(` [grid] Cannot build grid for ${grid.token}: invalid ATR (${atr})`));
|
|
4832
|
+
return;
|
|
4833
|
+
}
|
|
4834
|
+
const range = atr * this.config.atrMultiplier;
|
|
4835
|
+
const spacing = range / this.config.levelsPerSide;
|
|
4836
|
+
const effectiveCapital = grid.allocation * this.config.leverage;
|
|
4837
|
+
const quantity = effectiveCapital / (this.config.levelsPerSide * currentPrice);
|
|
4838
|
+
const levels = [];
|
|
4839
|
+
for (let i = 1; i <= this.config.levelsPerSide; i++) {
|
|
4840
|
+
levels.push({
|
|
4841
|
+
price: currentPrice - spacing * i,
|
|
4842
|
+
side: "buy",
|
|
4843
|
+
quantity,
|
|
4844
|
+
filled: false,
|
|
4845
|
+
filledAt: 0
|
|
4846
|
+
});
|
|
4847
|
+
}
|
|
4848
|
+
for (let i = 1; i <= this.config.levelsPerSide; i++) {
|
|
4849
|
+
levels.push({
|
|
4850
|
+
price: currentPrice + spacing * i,
|
|
4851
|
+
side: "sell",
|
|
4852
|
+
quantity,
|
|
4853
|
+
filled: false,
|
|
4854
|
+
filledAt: 0
|
|
4855
|
+
});
|
|
4856
|
+
}
|
|
4857
|
+
grid.levels = levels;
|
|
4858
|
+
grid.centerPrice = currentPrice;
|
|
4859
|
+
grid.atr = atr;
|
|
4860
|
+
grid.stats.lastRebalanceAt = Date.now();
|
|
4861
|
+
console.error(chalk9.dim(
|
|
4862
|
+
` [grid] Built ${grid.token} grid: center=$${currentPrice.toFixed(2)} ATR=$${atr.toFixed(2)} range=$${(currentPrice - range).toFixed(2)}-$${(currentPrice + range).toFixed(2)} spacing=$${spacing.toFixed(2)} qty=${quantity.toFixed(6)}`
|
|
4863
|
+
));
|
|
4864
|
+
}
|
|
4865
|
+
/** Simulate order fills against the current price. */
|
|
4866
|
+
simulateFills(grid, currentPrice) {
|
|
4867
|
+
let fills = 0;
|
|
4868
|
+
let roundTrips = 0;
|
|
4869
|
+
let pnlUsd = 0;
|
|
4870
|
+
const now = Date.now();
|
|
4871
|
+
const spacing = grid.atr > 0 ? grid.atr * this.config.atrMultiplier / this.config.levelsPerSide : 0;
|
|
4872
|
+
if (spacing <= 0) return { fills: 0, roundTrips: 0, pnlUsd: 0 };
|
|
4873
|
+
for (const level of grid.levels) {
|
|
4874
|
+
if (level.filled) continue;
|
|
4875
|
+
if (level.side === "buy" && currentPrice <= level.price) {
|
|
4876
|
+
level.filled = true;
|
|
4877
|
+
level.filledAt = now;
|
|
4878
|
+
fills++;
|
|
4879
|
+
grid.stats.totalFills++;
|
|
4880
|
+
grid.stats.todayFills++;
|
|
4881
|
+
grid.openFills.push({
|
|
4882
|
+
token: grid.token,
|
|
4883
|
+
buyPrice: level.price,
|
|
4884
|
+
targetSellPrice: level.price + spacing,
|
|
4885
|
+
quantity: level.quantity,
|
|
4886
|
+
filledAt: now,
|
|
4887
|
+
closed: false,
|
|
4888
|
+
pnlUsd: 0,
|
|
4889
|
+
closedAt: 0
|
|
4890
|
+
});
|
|
4891
|
+
console.error(chalk9.dim(
|
|
4892
|
+
` [grid] BUY fill ${grid.token} @ $${level.price.toFixed(2)} qty=${level.quantity.toFixed(6)}`
|
|
4893
|
+
));
|
|
4894
|
+
}
|
|
4895
|
+
if (level.side === "sell" && currentPrice >= level.price) {
|
|
4896
|
+
level.filled = true;
|
|
4897
|
+
level.filledAt = now;
|
|
4898
|
+
fills++;
|
|
4899
|
+
grid.stats.totalFills++;
|
|
4900
|
+
grid.stats.todayFills++;
|
|
4901
|
+
console.error(chalk9.dim(
|
|
4902
|
+
` [grid] SELL level ${grid.token} @ $${level.price.toFixed(2)} qty=${level.quantity.toFixed(6)}`
|
|
4903
|
+
));
|
|
4904
|
+
}
|
|
4905
|
+
}
|
|
4906
|
+
for (const openFill of grid.openFills) {
|
|
4907
|
+
if (openFill.closed) continue;
|
|
4908
|
+
if (currentPrice >= openFill.targetSellPrice) {
|
|
4909
|
+
const profit = (currentPrice - openFill.buyPrice) * openFill.quantity * this.config.leverage;
|
|
4910
|
+
if (profit >= this.config.minProfitPerFillUsd) {
|
|
4911
|
+
openFill.closed = true;
|
|
4912
|
+
openFill.pnlUsd = profit;
|
|
4913
|
+
openFill.closedAt = now;
|
|
4914
|
+
pnlUsd += profit;
|
|
4915
|
+
roundTrips++;
|
|
4916
|
+
grid.stats.totalRoundTrips++;
|
|
4917
|
+
grid.stats.totalPnlUsd += profit;
|
|
4918
|
+
grid.stats.todayPnlUsd += profit;
|
|
4919
|
+
grid.allocation += profit;
|
|
4920
|
+
console.error(chalk9.green(
|
|
4921
|
+
` [grid] ROUND-TRIP ${grid.token}: buy $${openFill.buyPrice.toFixed(2)} \u2192 sell $${currentPrice.toFixed(2)} = +$${profit.toFixed(2)}`
|
|
4922
|
+
));
|
|
4923
|
+
}
|
|
4924
|
+
}
|
|
4925
|
+
}
|
|
4926
|
+
const pruneThreshold = now - 24 * 60 * 60 * 1e3;
|
|
4927
|
+
grid.openFills = grid.openFills.filter((f) => !f.closed || f.closedAt > pruneThreshold);
|
|
4928
|
+
return { fills, roundTrips, pnlUsd };
|
|
4929
|
+
}
|
|
4930
|
+
/** Check if the grid needs a full rebuild. */
|
|
4931
|
+
needsFullRebuild(grid) {
|
|
4932
|
+
if (grid.levels.length === 0) return true;
|
|
4933
|
+
const elapsed = Date.now() - grid.stats.lastRebalanceAt;
|
|
4934
|
+
return elapsed >= this.config.fullRebuildIntervalMs;
|
|
4935
|
+
}
|
|
4936
|
+
/** Check if price has drifted enough to warrant shifting the grid. */
|
|
4937
|
+
needsShift(grid, currentPrice) {
|
|
4938
|
+
if (grid.centerPrice <= 0 || grid.atr <= 0) return false;
|
|
4939
|
+
const range = grid.atr * this.config.atrMultiplier;
|
|
4940
|
+
const distFromCenter = Math.abs(currentPrice - grid.centerPrice);
|
|
4941
|
+
return distFromCenter / range >= this.config.rebalanceDriftPct;
|
|
4942
|
+
}
|
|
4943
|
+
/** Shift the grid to re-center on the current price. */
|
|
4944
|
+
async shiftGrid(grid, currentPrice) {
|
|
4945
|
+
console.error(chalk9.dim(
|
|
4946
|
+
` [grid] Shifting ${grid.token} grid: center $${grid.centerPrice.toFixed(2)} \u2192 $${currentPrice.toFixed(2)}`
|
|
4947
|
+
));
|
|
4948
|
+
await this.buildGrid(grid, currentPrice);
|
|
4949
|
+
}
|
|
4950
|
+
/** Get open fill exposure per token — used by the hedge manager. */
|
|
4951
|
+
getOpenFillExposure() {
|
|
4952
|
+
const state = this.portfolio.getState();
|
|
4953
|
+
if (!state) return [];
|
|
4954
|
+
return state.grids.map((g) => {
|
|
4955
|
+
const opens = g.openFills.filter((f) => !f.closed);
|
|
4956
|
+
const totalQty = opens.reduce((s, f) => s + f.quantity, 0);
|
|
4957
|
+
const totalNotional = opens.reduce((s, f) => s + f.quantity * f.buyPrice, 0);
|
|
4958
|
+
return {
|
|
4959
|
+
token: g.token,
|
|
4960
|
+
totalQuantity: totalQty,
|
|
4961
|
+
totalNotional,
|
|
4962
|
+
avgEntryPrice: totalQty > 0 ? totalNotional / totalQty : 0,
|
|
4963
|
+
fillCount: opens.length
|
|
4964
|
+
};
|
|
4965
|
+
}).filter((e) => e.fillCount > 0);
|
|
4966
|
+
}
|
|
4967
|
+
/** Get aggregate stats for display. */
|
|
4968
|
+
getStats() {
|
|
4969
|
+
const state = this.portfolio.getState();
|
|
4970
|
+
if (!state) return null;
|
|
4971
|
+
const agg = this.portfolio.aggregateStats(state);
|
|
4972
|
+
return {
|
|
4973
|
+
...agg,
|
|
4974
|
+
allocation: state.totalAllocation,
|
|
4975
|
+
paused: state.paused
|
|
4976
|
+
};
|
|
4977
|
+
}
|
|
4978
|
+
};
|
|
4979
|
+
|
|
4980
|
+
// src/grid/config.ts
|
|
4981
|
+
var DEFAULT_GRID_CONFIG = {
|
|
4982
|
+
enabled: true,
|
|
4983
|
+
tokens: ["bitcoin", "ethereum", "solana"],
|
|
4984
|
+
allocationPct: 0.5,
|
|
4985
|
+
leverage: 5,
|
|
4986
|
+
// was 4 — +25% profit per RT
|
|
4987
|
+
levelsPerSide: 15,
|
|
4988
|
+
atrMultiplier: 2,
|
|
4989
|
+
atrPeriod: 14,
|
|
4990
|
+
rebalanceDriftPct: 0.4,
|
|
4991
|
+
// was 0.55 — rebalance faster, keep levels near price
|
|
4992
|
+
fullRebuildIntervalMs: 12 * 60 * 60 * 1e3,
|
|
4993
|
+
// 12h
|
|
4994
|
+
tokenSplit: { bitcoin: 0.45, ethereum: 0.3, solana: 0.25 },
|
|
4995
|
+
minProfitPerFillUsd: 0.5,
|
|
4996
|
+
pauseThresholdPct: 0.2
|
|
4997
|
+
};
|
|
4998
|
+
|
|
4999
|
+
// src/grid/hedge.ts
|
|
5000
|
+
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
5001
|
+
import { join as join2, dirname as dirname2 } from "path";
|
|
5002
|
+
import { homedir as homedir2 } from "os";
|
|
5003
|
+
import chalk10 from "chalk";
|
|
5004
|
+
var HEDGE_RATIO = 0.3;
|
|
5005
|
+
var MIN_FILLS_TO_HEDGE = 3;
|
|
5006
|
+
var ADJUSTMENT_THRESHOLD = 0.1;
|
|
5007
|
+
var HEDGE_STATE_PATH = join2(homedir2(), ".sherwood", "grid", "hedge.json");
|
|
5008
|
+
var GridHedgeManager = class {
|
|
5009
|
+
state = null;
|
|
5010
|
+
async load() {
|
|
5011
|
+
if (this.state) return this.state;
|
|
5012
|
+
try {
|
|
5013
|
+
const raw = await readFile2(HEDGE_STATE_PATH, "utf-8");
|
|
5014
|
+
this.state = JSON.parse(raw);
|
|
5015
|
+
} catch {
|
|
5016
|
+
this.state = {
|
|
5017
|
+
positions: [],
|
|
5018
|
+
totalRealizedPnl: 0,
|
|
5019
|
+
todayRealizedPnl: 0,
|
|
5020
|
+
lastDailyReset: Date.now()
|
|
5021
|
+
};
|
|
5022
|
+
}
|
|
5023
|
+
const todayMidnight = /* @__PURE__ */ new Date();
|
|
5024
|
+
todayMidnight.setUTCHours(0, 0, 0, 0);
|
|
5025
|
+
if (this.state.lastDailyReset < todayMidnight.getTime()) {
|
|
5026
|
+
this.state.todayRealizedPnl = 0;
|
|
5027
|
+
this.state.lastDailyReset = Date.now();
|
|
5028
|
+
}
|
|
5029
|
+
return this.state;
|
|
5030
|
+
}
|
|
5031
|
+
async save() {
|
|
5032
|
+
if (!this.state) return;
|
|
5033
|
+
await mkdir2(dirname2(HEDGE_STATE_PATH), { recursive: true });
|
|
5034
|
+
const tmp = `${HEDGE_STATE_PATH}.tmp.${process.pid}`;
|
|
5035
|
+
await writeFile2(tmp, JSON.stringify(this.state, null, 2), "utf-8");
|
|
5036
|
+
const { rename: rename2 } = await import("fs/promises");
|
|
5037
|
+
await rename2(tmp, HEDGE_STATE_PATH);
|
|
5038
|
+
}
|
|
5039
|
+
/**
|
|
5040
|
+
* Run one hedge tick. Call after grid tick with current prices and open fill data.
|
|
5041
|
+
*
|
|
5042
|
+
* @param openFills - Per-token open fill exposure from the grid portfolio
|
|
5043
|
+
* @param prices - Current mark prices per token
|
|
5044
|
+
*/
|
|
5045
|
+
async tick(openFills, prices) {
|
|
5046
|
+
const state = await this.load();
|
|
5047
|
+
let adjustments = 0;
|
|
5048
|
+
let unrealizedPnl = 0;
|
|
5049
|
+
for (const fill of openFills) {
|
|
5050
|
+
const price = prices[fill.token];
|
|
5051
|
+
if (!price || price <= 0) continue;
|
|
5052
|
+
const desiredShortQty = fill.fillCount >= MIN_FILLS_TO_HEDGE ? fill.totalQuantity * HEDGE_RATIO : 0;
|
|
5053
|
+
const existing = state.positions.find((p) => p.token === fill.token);
|
|
5054
|
+
if (desiredShortQty <= 0) {
|
|
5055
|
+
if (existing && existing.quantity > 0) {
|
|
5056
|
+
const closePnl = (existing.entryPrice - price) * existing.quantity;
|
|
5057
|
+
state.totalRealizedPnl += closePnl;
|
|
5058
|
+
state.todayRealizedPnl += closePnl;
|
|
5059
|
+
existing.realizedPnl += closePnl;
|
|
5060
|
+
console.error(chalk10.yellow(
|
|
5061
|
+
` [hedge] CLOSE ${fill.token} short ${existing.quantity.toFixed(6)} @ $${price.toFixed(2)} PnL: $${closePnl.toFixed(2)}`
|
|
5062
|
+
));
|
|
5063
|
+
existing.quantity = 0;
|
|
5064
|
+
existing.entryPrice = 0;
|
|
5065
|
+
adjustments++;
|
|
5066
|
+
}
|
|
5067
|
+
continue;
|
|
5068
|
+
}
|
|
5069
|
+
if (!existing) {
|
|
5070
|
+
state.positions.push({
|
|
5071
|
+
token: fill.token,
|
|
5072
|
+
entryPrice: price,
|
|
5073
|
+
quantity: desiredShortQty,
|
|
5074
|
+
lastAdjustedAt: Date.now(),
|
|
5075
|
+
realizedPnl: 0
|
|
5076
|
+
});
|
|
5077
|
+
console.error(chalk10.magenta(
|
|
5078
|
+
` [hedge] OPEN ${fill.token} short ${desiredShortQty.toFixed(6)} @ $${price.toFixed(2)} (${(HEDGE_RATIO * 100).toFixed(0)}% of ${fill.fillCount} open fills)`
|
|
5079
|
+
));
|
|
5080
|
+
adjustments++;
|
|
5081
|
+
} else if (existing.quantity > 0) {
|
|
5082
|
+
const diff = Math.abs(desiredShortQty - existing.quantity) / existing.quantity;
|
|
5083
|
+
if (diff > ADJUSTMENT_THRESHOLD) {
|
|
5084
|
+
if (desiredShortQty > existing.quantity) {
|
|
5085
|
+
const addQty = desiredShortQty - existing.quantity;
|
|
5086
|
+
const newAvgEntry = (existing.entryPrice * existing.quantity + price * addQty) / desiredShortQty;
|
|
5087
|
+
existing.entryPrice = newAvgEntry;
|
|
5088
|
+
existing.quantity = desiredShortQty;
|
|
5089
|
+
existing.lastAdjustedAt = Date.now();
|
|
5090
|
+
console.error(chalk10.magenta(
|
|
5091
|
+
` [hedge] INCREASE ${fill.token} short to ${desiredShortQty.toFixed(6)} @ avg $${newAvgEntry.toFixed(2)}`
|
|
5092
|
+
));
|
|
5093
|
+
adjustments++;
|
|
5094
|
+
} else {
|
|
5095
|
+
const closeQty = existing.quantity - desiredShortQty;
|
|
5096
|
+
const closePnl = (existing.entryPrice - price) * closeQty;
|
|
5097
|
+
state.totalRealizedPnl += closePnl;
|
|
5098
|
+
state.todayRealizedPnl += closePnl;
|
|
5099
|
+
existing.realizedPnl += closePnl;
|
|
5100
|
+
existing.quantity = desiredShortQty;
|
|
5101
|
+
existing.lastAdjustedAt = Date.now();
|
|
5102
|
+
console.error(chalk10.magenta(
|
|
5103
|
+
` [hedge] DECREASE ${fill.token} short to ${desiredShortQty.toFixed(6)}, closed ${closeQty.toFixed(6)} PnL: $${closePnl.toFixed(2)}`
|
|
5104
|
+
));
|
|
5105
|
+
adjustments++;
|
|
5106
|
+
}
|
|
5107
|
+
}
|
|
5108
|
+
const uPnl = (existing.entryPrice - price) * existing.quantity;
|
|
5109
|
+
unrealizedPnl += uPnl;
|
|
5110
|
+
} else {
|
|
5111
|
+
existing.entryPrice = price;
|
|
5112
|
+
existing.quantity = desiredShortQty;
|
|
5113
|
+
existing.lastAdjustedAt = Date.now();
|
|
5114
|
+
console.error(chalk10.magenta(
|
|
5115
|
+
` [hedge] REOPEN ${fill.token} short ${desiredShortQty.toFixed(6)} @ $${price.toFixed(2)}`
|
|
5116
|
+
));
|
|
5117
|
+
adjustments++;
|
|
5118
|
+
}
|
|
5119
|
+
}
|
|
5120
|
+
for (const pos of state.positions) {
|
|
5121
|
+
if (pos.quantity <= 0) continue;
|
|
5122
|
+
if (!openFills.find((f) => f.token === pos.token)) {
|
|
5123
|
+
const price = prices[pos.token];
|
|
5124
|
+
if (price) {
|
|
5125
|
+
const closePnl = (pos.entryPrice - price) * pos.quantity;
|
|
5126
|
+
state.totalRealizedPnl += closePnl;
|
|
5127
|
+
state.todayRealizedPnl += closePnl;
|
|
5128
|
+
pos.realizedPnl += closePnl;
|
|
5129
|
+
console.error(chalk10.yellow(
|
|
5130
|
+
` [hedge] CLOSE orphaned ${pos.token} short, PnL: $${closePnl.toFixed(2)}`
|
|
5131
|
+
));
|
|
5132
|
+
pos.quantity = 0;
|
|
5133
|
+
adjustments++;
|
|
5134
|
+
}
|
|
5135
|
+
}
|
|
5136
|
+
}
|
|
5137
|
+
await this.save();
|
|
5138
|
+
return {
|
|
5139
|
+
adjustments,
|
|
5140
|
+
unrealizedPnl,
|
|
5141
|
+
totalRealizedPnl: state.totalRealizedPnl
|
|
5142
|
+
};
|
|
5143
|
+
}
|
|
5144
|
+
/** Get current hedge status for display. */
|
|
5145
|
+
getStatus() {
|
|
5146
|
+
if (!this.state) return null;
|
|
5147
|
+
return {
|
|
5148
|
+
positions: this.state.positions.filter((p) => p.quantity > 0),
|
|
5149
|
+
totalRealizedPnl: this.state.totalRealizedPnl,
|
|
5150
|
+
todayRealizedPnl: this.state.todayRealizedPnl
|
|
5151
|
+
};
|
|
5152
|
+
}
|
|
5153
|
+
};
|
|
5154
|
+
|
|
5155
|
+
// src/grid/loop.ts
|
|
5156
|
+
var GRID_CYCLES_PATH = join3(homedir3(), ".sherwood", "grid", "cycles.jsonl");
|
|
5157
|
+
var GridLoop = class {
|
|
5158
|
+
cfg;
|
|
5159
|
+
gridConfig;
|
|
5160
|
+
manager;
|
|
5161
|
+
hedge;
|
|
5162
|
+
hl;
|
|
5163
|
+
running = false;
|
|
5164
|
+
cycleCount = 0;
|
|
5165
|
+
timer = null;
|
|
5166
|
+
constructor(cfg) {
|
|
5167
|
+
this.cfg = cfg;
|
|
5168
|
+
this.gridConfig = { ...DEFAULT_GRID_CONFIG, ...cfg.config };
|
|
5169
|
+
this.manager = new GridManager(this.gridConfig);
|
|
5170
|
+
this.hedge = new GridHedgeManager();
|
|
5171
|
+
this.hl = new HyperliquidProvider();
|
|
5172
|
+
}
|
|
5173
|
+
/** Start the grid loop. Resolves when shut down via SIGINT/SIGTERM. */
|
|
5174
|
+
async start() {
|
|
5175
|
+
this.running = true;
|
|
5176
|
+
const shutdown = () => {
|
|
5177
|
+
console.error(chalk11.yellow("\n [grid-loop] Shutting down\u2026"));
|
|
5178
|
+
this.running = false;
|
|
5179
|
+
if (this.timer) {
|
|
5180
|
+
clearTimeout(this.timer);
|
|
5181
|
+
this.timer = null;
|
|
5182
|
+
}
|
|
5183
|
+
};
|
|
5184
|
+
process.on("SIGINT", shutdown);
|
|
5185
|
+
process.on("SIGTERM", shutdown);
|
|
5186
|
+
await this.manager.init(this.cfg.capital);
|
|
5187
|
+
console.error(chalk11.cyan(
|
|
5188
|
+
`
|
|
5189
|
+
[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
|
+
`
|
|
5191
|
+
));
|
|
5192
|
+
while (this.running) {
|
|
5193
|
+
try {
|
|
5194
|
+
await this.tick();
|
|
5195
|
+
} catch (err) {
|
|
5196
|
+
console.error(chalk11.red(` [grid-loop] Tick error: ${err.message}`));
|
|
5197
|
+
}
|
|
5198
|
+
if (this.running) {
|
|
5199
|
+
await new Promise((resolve2) => {
|
|
5200
|
+
this.timer = setTimeout(resolve2, this.cfg.cycle);
|
|
5201
|
+
});
|
|
5202
|
+
}
|
|
5203
|
+
}
|
|
5204
|
+
process.off("SIGINT", shutdown);
|
|
5205
|
+
process.off("SIGTERM", shutdown);
|
|
5206
|
+
console.error(chalk11.yellow(" [grid-loop] Stopped."));
|
|
5207
|
+
}
|
|
5208
|
+
/** Execute one grid cycle. */
|
|
5209
|
+
async tick() {
|
|
5210
|
+
this.cycleCount++;
|
|
5211
|
+
const start = Date.now();
|
|
5212
|
+
const prices = {};
|
|
5213
|
+
for (const token of this.gridConfig.tokens) {
|
|
5214
|
+
const data = await this.hl.getHyperliquidData(token);
|
|
5215
|
+
if (data?.markPrice && data.markPrice > 0) {
|
|
5216
|
+
prices[token] = data.markPrice;
|
|
5217
|
+
}
|
|
5218
|
+
}
|
|
5219
|
+
if (Object.keys(prices).length === 0) {
|
|
5220
|
+
console.error(chalk11.dim(` [grid-loop] #${this.cycleCount} \u2014 no prices, skipping`));
|
|
5221
|
+
return;
|
|
5222
|
+
}
|
|
5223
|
+
const result = await this.manager.tick(prices);
|
|
5224
|
+
const elapsed = Date.now() - start;
|
|
5225
|
+
if (result.roundTrips > 0) {
|
|
5226
|
+
console.error(chalk11.green(
|
|
5227
|
+
` [grid-loop] #${this.cycleCount} \u2014 ${result.roundTrips} RT(s), +$${result.pnlUsd.toFixed(2)} PnL, ${result.fills} fill(s) [${elapsed}ms]`
|
|
5228
|
+
));
|
|
5229
|
+
}
|
|
5230
|
+
if (result.fills > 0 && result.roundTrips === 0) {
|
|
5231
|
+
console.error(chalk11.dim(
|
|
5232
|
+
` [grid-loop] #${this.cycleCount} \u2014 ${result.fills} fill(s), 0 RTs [${elapsed}ms]`
|
|
5233
|
+
));
|
|
5234
|
+
}
|
|
5235
|
+
const openExposure = this.manager.getOpenFillExposure();
|
|
5236
|
+
const hedgeResult = await this.hedge.tick(openExposure, prices);
|
|
5237
|
+
if (hedgeResult.adjustments > 0) {
|
|
5238
|
+
console.error(chalk11.magenta(
|
|
5239
|
+
` [hedge] ${hedgeResult.adjustments} adjustment(s), unrealized: $${hedgeResult.unrealizedPnl.toFixed(2)}, total realized: $${hedgeResult.totalRealizedPnl.toFixed(2)}`
|
|
5240
|
+
));
|
|
5241
|
+
}
|
|
5242
|
+
const stats = this.manager.getStats();
|
|
5243
|
+
const cycleEntry = {
|
|
5244
|
+
cycleNumber: this.cycleCount,
|
|
5245
|
+
timestamp: Date.now(),
|
|
5246
|
+
gridFills: result.fills,
|
|
5247
|
+
gridRoundTrips: result.roundTrips,
|
|
5248
|
+
gridPnlUsd: result.pnlUsd,
|
|
5249
|
+
totalPnlUsd: stats?.totalPnlUsd ?? 0,
|
|
5250
|
+
todayPnlUsd: stats?.todayPnlUsd ?? 0,
|
|
5251
|
+
totalRoundTrips: stats?.totalRoundTrips ?? 0,
|
|
5252
|
+
allocation: stats?.allocation ?? 0,
|
|
5253
|
+
paused: stats?.paused ?? false,
|
|
5254
|
+
hedgeUnrealizedPnl: hedgeResult.unrealizedPnl,
|
|
5255
|
+
hedgeTotalRealizedPnl: hedgeResult.totalRealizedPnl
|
|
5256
|
+
};
|
|
5257
|
+
try {
|
|
5258
|
+
await mkdir3(join3(homedir3(), ".sherwood", "grid"), { recursive: true });
|
|
5259
|
+
await appendFile(GRID_CYCLES_PATH, JSON.stringify(cycleEntry) + "\n");
|
|
5260
|
+
} catch {
|
|
5261
|
+
}
|
|
5262
|
+
if (this.cycleCount % 60 === 0) {
|
|
5263
|
+
const stats2 = this.manager.getStats();
|
|
5264
|
+
if (stats2) {
|
|
5265
|
+
const hedgeStatus = this.hedge.getStatus();
|
|
5266
|
+
const hedgeInfo = hedgeStatus && hedgeStatus.positions.length > 0 ? ` hedge=$${hedgeResult.unrealizedPnl.toFixed(2)}unr/$${hedgeStatus.totalRealizedPnl.toFixed(2)}real` : "";
|
|
5267
|
+
console.error(chalk11.cyan(
|
|
5268
|
+
` [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
|
+
));
|
|
5270
|
+
}
|
|
5271
|
+
}
|
|
5272
|
+
}
|
|
5273
|
+
};
|
|
5274
|
+
|
|
5275
|
+
// src/commands/grid.ts
|
|
5276
|
+
var DIM5 = chalk12.gray;
|
|
5277
|
+
var G5 = chalk12.green;
|
|
5278
|
+
var BOLD5 = chalk12.white.bold;
|
|
5279
|
+
var W5 = chalk12.white;
|
|
5280
|
+
var SEP5 = () => console.log(DIM5("\u2500".repeat(60)));
|
|
5281
|
+
function registerGridCommand(program2) {
|
|
5282
|
+
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) => {
|
|
5284
|
+
const capital = parseFloat(opts.capital);
|
|
5285
|
+
const cycleMs = parseInt(opts.cycle, 10) * 1e3;
|
|
5286
|
+
const tokens = opts.tokens.split(",").map((t) => t.trim());
|
|
5287
|
+
const leverage = parseFloat(opts.leverage);
|
|
5288
|
+
const levels = parseInt(opts.levels, 10);
|
|
5289
|
+
const weight = 1 / tokens.length;
|
|
5290
|
+
const tokenSplit = {};
|
|
5291
|
+
for (const t of tokens) {
|
|
5292
|
+
tokenSplit[t] = weight;
|
|
5293
|
+
}
|
|
5294
|
+
console.log();
|
|
5295
|
+
console.log(G5.bold(" Grid Strategy"));
|
|
5296
|
+
SEP5();
|
|
5297
|
+
console.log(W5(` Capital: $${capital.toLocaleString()}`));
|
|
5298
|
+
console.log(W5(` Cycle: ${opts.cycle}s`));
|
|
5299
|
+
console.log(W5(` Tokens: ${tokens.join(", ")}`));
|
|
5300
|
+
console.log(W5(` Leverage: ${leverage}x`));
|
|
5301
|
+
console.log(W5(` Levels: ${levels}/side`));
|
|
5302
|
+
SEP5();
|
|
5303
|
+
const loop = new GridLoop({
|
|
5304
|
+
capital,
|
|
5305
|
+
cycle: cycleMs,
|
|
5306
|
+
config: {
|
|
5307
|
+
...DEFAULT_GRID_CONFIG,
|
|
5308
|
+
tokens,
|
|
5309
|
+
leverage,
|
|
5310
|
+
levelsPerSide: levels,
|
|
5311
|
+
tokenSplit
|
|
5312
|
+
}
|
|
5313
|
+
});
|
|
5314
|
+
await loop.start();
|
|
5315
|
+
});
|
|
5316
|
+
grid.command("status").description("Show current grid portfolio status").action(async () => {
|
|
5317
|
+
const portfolio = new GridPortfolio();
|
|
5318
|
+
const state = await portfolio.load();
|
|
5319
|
+
if (!state) {
|
|
5320
|
+
console.log(DIM5("\n No grid portfolio found. Run `sherwood grid start` first.\n"));
|
|
5321
|
+
return;
|
|
5322
|
+
}
|
|
5323
|
+
portfolio.resetDailyStats(state);
|
|
5324
|
+
const agg = portfolio.aggregateStats(state);
|
|
5325
|
+
console.log();
|
|
5326
|
+
console.log(G5.bold(" Grid Portfolio"));
|
|
5327
|
+
SEP5();
|
|
5328
|
+
console.log(W5(` Allocation: $${state.totalAllocation.toLocaleString()}`));
|
|
5329
|
+
console.log(W5(` Status: ${state.paused ? chalk12.red("PAUSED \u2014 " + state.pauseReason) : G5("Active")}`));
|
|
5330
|
+
console.log(W5(` Initialized: ${new Date(state.initializedAt).toLocaleString()}`));
|
|
5331
|
+
SEP5();
|
|
5332
|
+
console.log();
|
|
5333
|
+
console.log(BOLD5(" Token Alloc RTs PnL Today PnL Fills"));
|
|
5334
|
+
console.log(DIM5(" " + "\u2500".repeat(72)));
|
|
5335
|
+
for (const g of state.grids) {
|
|
5336
|
+
const name = g.token.padEnd(13);
|
|
5337
|
+
const alloc = `$${g.allocation.toFixed(0)}`.padStart(8);
|
|
5338
|
+
const rts = String(g.stats.totalRoundTrips).padStart(6);
|
|
5339
|
+
const pnl = `$${g.stats.totalPnlUsd.toFixed(2)}`.padStart(10);
|
|
5340
|
+
const todayPnl = `$${g.stats.todayPnlUsd.toFixed(2)}`.padStart(10);
|
|
5341
|
+
const fills = String(g.stats.totalFills).padStart(6);
|
|
5342
|
+
const pnlColor = g.stats.totalPnlUsd >= 0 ? G5 : chalk12.red;
|
|
5343
|
+
const todayColor = g.stats.todayPnlUsd >= 0 ? G5 : chalk12.red;
|
|
5344
|
+
console.log(` ${W5(name)} ${alloc} ${rts} ${pnlColor(pnl)} ${todayColor(todayPnl)} ${fills}`);
|
|
5345
|
+
}
|
|
5346
|
+
console.log(DIM5(" " + "\u2500".repeat(72)));
|
|
5347
|
+
const totalPnlColor = agg.totalPnlUsd >= 0 ? G5 : chalk12.red;
|
|
5348
|
+
const todayTotalColor = agg.todayPnlUsd >= 0 ? G5 : chalk12.red;
|
|
5349
|
+
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
|
+
console.log();
|
|
5351
|
+
});
|
|
5352
|
+
}
|
|
5353
|
+
|
|
4613
5354
|
// src/index.ts
|
|
4614
5355
|
try {
|
|
4615
5356
|
loadDotenv();
|
|
@@ -4618,20 +5359,20 @@ try {
|
|
|
4618
5359
|
var require2 = createRequire(import.meta.url);
|
|
4619
5360
|
var { version: CLI_VERSION } = require2("../package.json");
|
|
4620
5361
|
async function loadXmtp() {
|
|
4621
|
-
return import("./xmtp-
|
|
5362
|
+
return import("./xmtp-ACB2W573.js");
|
|
4622
5363
|
}
|
|
4623
5364
|
async function loadCron() {
|
|
4624
5365
|
return import("./cron-EOULYO3R.js");
|
|
4625
5366
|
}
|
|
4626
|
-
var
|
|
4627
|
-
var
|
|
4628
|
-
var
|
|
4629
|
-
var
|
|
4630
|
-
var LABEL5 =
|
|
4631
|
-
var
|
|
5367
|
+
var G6 = chalk13.green;
|
|
5368
|
+
var W6 = chalk13.white;
|
|
5369
|
+
var DIM6 = chalk13.gray;
|
|
5370
|
+
var BOLD6 = chalk13.white.bold;
|
|
5371
|
+
var LABEL5 = chalk13.green.bold;
|
|
5372
|
+
var SEP6 = () => console.log(DIM6("\u2500".repeat(60)));
|
|
4632
5373
|
function validateAddress(value, name) {
|
|
4633
5374
|
if (!isAddress6(value)) {
|
|
4634
|
-
console.error(
|
|
5375
|
+
console.error(chalk13.red(`Invalid ${name} address: ${value}`));
|
|
4635
5376
|
process.exit(1);
|
|
4636
5377
|
}
|
|
4637
5378
|
return value;
|
|
@@ -4651,7 +5392,7 @@ program.name("sherwood").description("CLI for agent-managed investment syndicate
|
|
|
4651
5392
|
process.env.ENABLE_TESTNET = "true";
|
|
4652
5393
|
if (network !== "base") {
|
|
4653
5394
|
console.warn(
|
|
4654
|
-
|
|
5395
|
+
chalk13.yellow("[warn] --testnet ignored, --chain takes precedence")
|
|
4655
5396
|
);
|
|
4656
5397
|
} else {
|
|
4657
5398
|
network = "base-sepolia";
|
|
@@ -4659,7 +5400,7 @@ program.name("sherwood").description("CLI for agent-managed investment syndicate
|
|
|
4659
5400
|
}
|
|
4660
5401
|
setNetwork(network);
|
|
4661
5402
|
if (getNetwork() !== "base") {
|
|
4662
|
-
console.log(
|
|
5403
|
+
console.log(chalk13.yellow(`[${getNetwork()}]`));
|
|
4663
5404
|
}
|
|
4664
5405
|
});
|
|
4665
5406
|
var syndicate = program.command("syndicate");
|
|
@@ -4667,31 +5408,31 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
4667
5408
|
try {
|
|
4668
5409
|
console.log();
|
|
4669
5410
|
console.log(LABEL5(" \u25C6 Create Syndicate"));
|
|
4670
|
-
|
|
5411
|
+
SEP6();
|
|
4671
5412
|
const wallet = getAccount();
|
|
4672
|
-
console.log(
|
|
4673
|
-
console.log(
|
|
4674
|
-
|
|
5413
|
+
console.log(DIM6(` Wallet: ${wallet.address}`));
|
|
5414
|
+
console.log(DIM6(` Network: ${getChain().name}`));
|
|
5415
|
+
SEP6();
|
|
4675
5416
|
const savedAgentId = getAgentId();
|
|
4676
5417
|
const nonInteractive = opts.yes;
|
|
4677
5418
|
const name = opts.name || (nonInteractive ? (() => {
|
|
4678
5419
|
throw new Error("--name is required in non-interactive mode (-y)");
|
|
4679
5420
|
})() : await input({
|
|
4680
|
-
message:
|
|
5421
|
+
message: G6("Syndicate name"),
|
|
4681
5422
|
validate: (v) => v.length > 0 || "Name is required"
|
|
4682
5423
|
}));
|
|
4683
5424
|
const subdomain = opts.subdomain || (nonInteractive ? name.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "") : await input({
|
|
4684
|
-
message:
|
|
5425
|
+
message: G6("ENS subdomain"),
|
|
4685
5426
|
default: name.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, ""),
|
|
4686
5427
|
validate: (v) => v.length >= 3 || "Must be at least 3 characters"
|
|
4687
5428
|
}));
|
|
4688
5429
|
const description = opts.description || (nonInteractive ? `${name} \u2014 a Sherwood syndicate` : await input({
|
|
4689
|
-
message:
|
|
5430
|
+
message: G6("Description"),
|
|
4690
5431
|
default: `${name} \u2014 a Sherwood syndicate`
|
|
4691
5432
|
}));
|
|
4692
|
-
const agentIdStr = opts.agentId || (savedAgentId ? nonInteractive ? String(savedAgentId) : await input({ message:
|
|
5433
|
+
const agentIdStr = opts.agentId || (savedAgentId ? nonInteractive ? String(savedAgentId) : await input({ message: G6("Agent ID (ERC-8004)"), default: String(savedAgentId) }) : nonInteractive ? "0" : await input({ message: G6("Agent ID (ERC-8004)"), validate: (v) => /^\d+$/.test(v) || "Must be a number" }));
|
|
4693
5434
|
const openDeposits = opts.openDeposits !== void 0 ? opts.openDeposits : nonInteractive ? true : await confirm({
|
|
4694
|
-
message:
|
|
5435
|
+
message: G6("Open deposits? (anyone can deposit)"),
|
|
4695
5436
|
default: true
|
|
4696
5437
|
});
|
|
4697
5438
|
const ASSET_SYMBOLS = {
|
|
@@ -4707,14 +5448,14 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
4707
5448
|
asset = opts.asset;
|
|
4708
5449
|
} else {
|
|
4709
5450
|
const supported = Object.keys(ASSET_SYMBOLS).join(", ");
|
|
4710
|
-
console.error(
|
|
5451
|
+
console.error(chalk13.red(` Unknown asset "${opts.asset}". Use a symbol (${supported}) or a 0x address.`));
|
|
4711
5452
|
process.exit(1);
|
|
4712
5453
|
}
|
|
4713
5454
|
} else if (nonInteractive) {
|
|
4714
5455
|
asset = TOKENS().USDC !== "0x0000000000000000000000000000000000000000" ? ASSET_SYMBOLS.USDC : ASSET_SYMBOLS.WETH;
|
|
4715
5456
|
} else {
|
|
4716
5457
|
const assetChoice = await select({
|
|
4717
|
-
message:
|
|
5458
|
+
message: G6("Vault asset (what token do depositors provide?)"),
|
|
4718
5459
|
choices: [
|
|
4719
5460
|
{ name: "USDC", value: "USDC", description: "USD Coin (6 decimals)" },
|
|
4720
5461
|
{ name: "WETH", value: "WETH", description: "Wrapped Ether (18 decimals)" }
|
|
@@ -4730,25 +5471,25 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
4730
5471
|
const symbol = `sw${assetSymbol}`;
|
|
4731
5472
|
console.log();
|
|
4732
5473
|
console.log(LABEL5(" \u25C6 Review"));
|
|
4733
|
-
|
|
4734
|
-
console.log(
|
|
4735
|
-
console.log(
|
|
4736
|
-
console.log(
|
|
4737
|
-
console.log(
|
|
4738
|
-
console.log(
|
|
4739
|
-
console.log(
|
|
4740
|
-
console.log(
|
|
4741
|
-
|
|
5474
|
+
SEP6();
|
|
5475
|
+
console.log(W6(` Name: ${BOLD6(name)}`));
|
|
5476
|
+
console.log(W6(` ENS: ${G6(`${subdomain}.sherwoodagent.eth`)}`));
|
|
5477
|
+
console.log(W6(` Description: ${DIM6(description)}`));
|
|
5478
|
+
console.log(W6(` Agent ID: #${agentIdStr}`));
|
|
5479
|
+
console.log(W6(` Asset: ${assetSymbol} (${asset.slice(0, 10)}...)`));
|
|
5480
|
+
console.log(W6(` Share token: ${symbol}`));
|
|
5481
|
+
console.log(W6(` Open deposits: ${openDeposits ? G6("yes") : chalk13.red("no (whitelist)")}`));
|
|
5482
|
+
SEP6();
|
|
4742
5483
|
if (!nonInteractive) {
|
|
4743
|
-
const go = await confirm({ message:
|
|
5484
|
+
const go = await confirm({ message: G6("Deploy syndicate?"), default: true });
|
|
4744
5485
|
if (!go) {
|
|
4745
|
-
console.log(
|
|
5486
|
+
console.log(DIM6(" Cancelled."));
|
|
4746
5487
|
return;
|
|
4747
5488
|
}
|
|
4748
5489
|
}
|
|
4749
5490
|
let metadataURI = opts.metadataUri || "";
|
|
4750
5491
|
if (!metadataURI) {
|
|
4751
|
-
const spinner2 = ora8({ text:
|
|
5492
|
+
const spinner2 = ora8({ text: W6("Uploading metadata to IPFS..."), color: "green" }).start();
|
|
4752
5493
|
try {
|
|
4753
5494
|
const metadata = {
|
|
4754
5495
|
schema: "sherwood/syndicate/v1",
|
|
@@ -4762,9 +5503,9 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
4762
5503
|
links: {}
|
|
4763
5504
|
};
|
|
4764
5505
|
metadataURI = await uploadMetadata(metadata);
|
|
4765
|
-
spinner2.succeed(
|
|
5506
|
+
spinner2.succeed(G6(`Metadata pinned: ${DIM6(metadataURI)}`));
|
|
4766
5507
|
} catch (err) {
|
|
4767
|
-
spinner2.warn(
|
|
5508
|
+
spinner2.warn(chalk13.yellow(`IPFS upload failed \u2014 using inline metadata`));
|
|
4768
5509
|
const json = JSON.stringify({ name, description, subdomain, asset: assetSymbol, openDeposits, createdBy: "@sherwoodagent/cli" });
|
|
4769
5510
|
metadataURI = `data:application/json;base64,${Buffer.from(json).toString("base64")}`;
|
|
4770
5511
|
}
|
|
@@ -4774,43 +5515,43 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
4774
5515
|
const existingInfo = await getSyndicate(existingId);
|
|
4775
5516
|
const callerAddress = getAccount().address.toLowerCase();
|
|
4776
5517
|
if (existingInfo.creator.toLowerCase() !== callerAddress) {
|
|
4777
|
-
console.error(
|
|
5518
|
+
console.error(chalk13.red(`
|
|
4778
5519
|
\u2716 Subdomain '${subdomain}' is already taken by another syndicate.
|
|
4779
5520
|
`));
|
|
4780
5521
|
process.exit(1);
|
|
4781
5522
|
}
|
|
4782
|
-
console.log(
|
|
5523
|
+
console.log(chalk13.yellow(`
|
|
4783
5524
|
\u26A0 Syndicate '${subdomain}' already exists \u2014 resuming setup...
|
|
4784
5525
|
`));
|
|
4785
|
-
const spinner2 = ora8({ text:
|
|
5526
|
+
const spinner2 = ora8({ text: W6("Resuming setup..."), color: "green" }).start();
|
|
4786
5527
|
const chainId2 = getChain().id;
|
|
4787
5528
|
setChainContract(chainId2, "vault", existingInfo.vault);
|
|
4788
5529
|
addSyndicate(chainId2, { subdomain, vault: existingInfo.vault, role: "creator" });
|
|
4789
5530
|
setPrimarySyndicate(chainId2, subdomain);
|
|
4790
|
-
spinner2.text =
|
|
5531
|
+
spinner2.text = W6("Checking agent registration...");
|
|
4791
5532
|
try {
|
|
4792
5533
|
setVaultAddress(existingInfo.vault);
|
|
4793
5534
|
const isAlreadyAgent = await isAgent(getAccount().address);
|
|
4794
5535
|
if (!isAlreadyAgent) {
|
|
4795
|
-
spinner2.text =
|
|
5536
|
+
spinner2.text = W6("Registering creator as agent...");
|
|
4796
5537
|
await registerAgent(BigInt(agentIdStr), getAccount().address);
|
|
4797
5538
|
}
|
|
4798
5539
|
} catch {
|
|
4799
5540
|
}
|
|
4800
|
-
spinner2.text =
|
|
5541
|
+
spinner2.text = W6("Checking chat group...");
|
|
4801
5542
|
try {
|
|
4802
5543
|
const xmtp = await loadXmtp();
|
|
4803
5544
|
const xmtpClient = await xmtp.getXmtpClient();
|
|
4804
5545
|
try {
|
|
4805
5546
|
await xmtp.getGroup(xmtpClient, subdomain);
|
|
4806
5547
|
} catch {
|
|
4807
|
-
spinner2.text =
|
|
5548
|
+
spinner2.text = W6("Creating chat group...");
|
|
4808
5549
|
const groupId = await xmtp.createSyndicateGroup(xmtpClient, subdomain, opts.publicChat);
|
|
4809
5550
|
await setTextRecord(subdomain, "xmtpGroupId", groupId, existingInfo.vault);
|
|
4810
5551
|
cacheGroupId(subdomain, groupId);
|
|
4811
5552
|
}
|
|
4812
5553
|
} catch {
|
|
4813
|
-
console.warn(
|
|
5554
|
+
console.warn(chalk13.yellow("\n \u26A0 Could not set up XMTP chat group"));
|
|
4814
5555
|
}
|
|
4815
5556
|
try {
|
|
4816
5557
|
const cron = await loadCron();
|
|
@@ -4820,19 +5561,19 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
4820
5561
|
spinner2.stop();
|
|
4821
5562
|
console.log();
|
|
4822
5563
|
console.log(LABEL5(" \u25C6 Syndicate Setup Resumed"));
|
|
4823
|
-
|
|
4824
|
-
console.log(
|
|
4825
|
-
console.log(
|
|
4826
|
-
console.log(
|
|
4827
|
-
console.log(
|
|
5564
|
+
SEP6();
|
|
5565
|
+
console.log(W6(` ID: ${G6(`#${existingId}`)}`));
|
|
5566
|
+
console.log(W6(` Vault: ${G6(existingInfo.vault)}`));
|
|
5567
|
+
console.log(W6(` ENS: ${G6(`${subdomain}.sherwoodagent.eth`)}`));
|
|
5568
|
+
console.log(W6(` Chat: ${DIM6(`sherwood chat ${subdomain}`)}`));
|
|
4828
5569
|
const refParam2 = getAgentId() ? `?ref=${getAgentId()}` : "";
|
|
4829
|
-
console.log(
|
|
4830
|
-
|
|
4831
|
-
console.log(
|
|
5570
|
+
console.log(W6(` Share: ${G6(`https://sherwood.sh/syndicate/${subdomain}${refParam2}`)}`));
|
|
5571
|
+
SEP6();
|
|
5572
|
+
console.log(G6(" \u2713 Syndicate saved to ~/.sherwood/config.json (set as primary)"));
|
|
4832
5573
|
console.log();
|
|
4833
5574
|
return;
|
|
4834
5575
|
}
|
|
4835
|
-
const spinner = ora8({ text:
|
|
5576
|
+
const spinner = ora8({ text: W6("Deploying vault via factory..."), color: "green" }).start();
|
|
4836
5577
|
const result = await createSyndicate({
|
|
4837
5578
|
creatorAgentId: BigInt(agentIdStr),
|
|
4838
5579
|
metadataURI,
|
|
@@ -4847,7 +5588,7 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
4847
5588
|
addSyndicate(chainId, { subdomain, vault: result.vault, role: "creator" });
|
|
4848
5589
|
setPrimarySyndicate(chainId, subdomain);
|
|
4849
5590
|
await new Promise((r) => setTimeout(r, 2e3));
|
|
4850
|
-
spinner.text =
|
|
5591
|
+
spinner.text = W6("Registering creator as agent...");
|
|
4851
5592
|
try {
|
|
4852
5593
|
setVaultAddress(result.vault);
|
|
4853
5594
|
const creatorAddress = getAccount().address;
|
|
@@ -4857,9 +5598,9 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
4857
5598
|
// agentAddress = creator EOA (direct execution)
|
|
4858
5599
|
);
|
|
4859
5600
|
} catch (regErr) {
|
|
4860
|
-
console.warn(
|
|
5601
|
+
console.warn(chalk13.yellow("\n \u26A0 Could not auto-register creator as agent \u2014 register manually with `syndicate add`"));
|
|
4861
5602
|
}
|
|
4862
|
-
spinner.text =
|
|
5603
|
+
spinner.text = W6("Setting up chat...");
|
|
4863
5604
|
try {
|
|
4864
5605
|
const xmtp = await loadXmtp();
|
|
4865
5606
|
const xmtpClient = await xmtp.getXmtpClient();
|
|
@@ -4870,36 +5611,36 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
4870
5611
|
} catch {
|
|
4871
5612
|
}
|
|
4872
5613
|
} catch {
|
|
4873
|
-
console.warn(
|
|
4874
|
-
console.warn(
|
|
5614
|
+
console.warn(chalk13.yellow("\n \u26A0 Could not create XMTP chat group"));
|
|
5615
|
+
console.warn(chalk13.dim(` Recover later with: sherwood chat ${subdomain} init`));
|
|
4875
5616
|
}
|
|
4876
5617
|
try {
|
|
4877
5618
|
const cron = await loadCron();
|
|
4878
5619
|
const cronResult = cron.registerSyndicateCrons(subdomain, isTestnet(), getNotifyTo());
|
|
4879
5620
|
if (cronResult.isOpenClaw && cronResult.registered) {
|
|
4880
|
-
console.log(
|
|
5621
|
+
console.log(G6(" \u2713 Participation crons registered (15m check + hourly summary)"));
|
|
4881
5622
|
} else if (!cronResult.isOpenClaw) {
|
|
4882
|
-
console.log(
|
|
5623
|
+
console.log(DIM6(" Tip: Set up a scheduled process to run `sherwood session check " + subdomain + "` periodically"));
|
|
4883
5624
|
}
|
|
4884
5625
|
} catch {
|
|
4885
5626
|
}
|
|
4886
5627
|
spinner.stop();
|
|
4887
5628
|
console.log();
|
|
4888
5629
|
console.log(LABEL5(" \u25C6 Syndicate Created"));
|
|
4889
|
-
|
|
4890
|
-
console.log(
|
|
4891
|
-
console.log(
|
|
4892
|
-
console.log(
|
|
4893
|
-
console.log(
|
|
4894
|
-
console.log(
|
|
4895
|
-
console.log(
|
|
5630
|
+
SEP6();
|
|
5631
|
+
console.log(W6(` ID: ${G6(`#${result.syndicateId}`)}`));
|
|
5632
|
+
console.log(W6(` Vault: ${G6(result.vault)}`));
|
|
5633
|
+
console.log(W6(` ENS: ${G6(`${subdomain}.sherwoodagent.eth`)}`));
|
|
5634
|
+
console.log(W6(` Metadata: ${DIM6(metadataURI.length > 50 ? metadataURI.slice(0, 50) + "..." : metadataURI)}`));
|
|
5635
|
+
console.log(W6(` Explorer: ${DIM6(getExplorerUrl(result.hash))}`));
|
|
5636
|
+
console.log(W6(` Chat: ${DIM6(`sherwood chat ${subdomain}`)}`));
|
|
4896
5637
|
const refParam = getAgentId() ? `?ref=${getAgentId()}` : "";
|
|
4897
|
-
console.log(
|
|
4898
|
-
|
|
4899
|
-
console.log(
|
|
5638
|
+
console.log(W6(` Share: ${G6(`https://sherwood.sh/syndicate/${subdomain}${refParam}`)}`));
|
|
5639
|
+
SEP6();
|
|
5640
|
+
console.log(G6(" \u2713 Syndicate saved to ~/.sherwood/config.json (set as primary)"));
|
|
4900
5641
|
console.log();
|
|
4901
5642
|
} catch (err) {
|
|
4902
|
-
console.error(
|
|
5643
|
+
console.error(chalk13.red(`
|
|
4903
5644
|
\u2716 ${formatContractError(err)}`));
|
|
4904
5645
|
process.exit(1);
|
|
4905
5646
|
}
|
|
@@ -4949,38 +5690,38 @@ syndicate.command("list").description("List active syndicates (queries subgraph,
|
|
|
4949
5690
|
}
|
|
4950
5691
|
spinner.stop();
|
|
4951
5692
|
if (syndicates.length === 0) {
|
|
4952
|
-
console.log(
|
|
5693
|
+
console.log(chalk13.dim("No active syndicates found."));
|
|
4953
5694
|
return;
|
|
4954
5695
|
}
|
|
4955
5696
|
console.log();
|
|
4956
|
-
console.log(
|
|
5697
|
+
console.log(chalk13.bold(`Active Syndicates (${syndicates.length})`));
|
|
4957
5698
|
if (!process.env.SUBGRAPH_URL && !opts.allChains) {
|
|
4958
|
-
console.log(
|
|
5699
|
+
console.log(chalk13.dim(" (Set SUBGRAPH_URL for faster indexed queries)"));
|
|
4959
5700
|
}
|
|
4960
5701
|
if (opts.allChains) {
|
|
4961
|
-
console.log(
|
|
5702
|
+
console.log(chalk13.dim(" (Aggregated across chains via on-chain factory calls; SUBGRAPH_URL is single-chain only)"));
|
|
4962
5703
|
}
|
|
4963
|
-
console.log(
|
|
5704
|
+
console.log(chalk13.dim("\u2500".repeat(70)));
|
|
4964
5705
|
for (const s of syndicates) {
|
|
4965
5706
|
const ts = typeof s.createdAt === "string" ? Number(s.createdAt) : Number(s.createdAt);
|
|
4966
5707
|
const date = new Date(ts * 1e3).toLocaleDateString();
|
|
4967
5708
|
const ensName = s.subdomain ? `${s.subdomain}.sherwoodagent.eth` : "";
|
|
4968
|
-
const chainTag = s.chain ?
|
|
4969
|
-
console.log(` ${chainTag}#${s.id} ${
|
|
4970
|
-
if (ensName) console.log(` Vault: ${
|
|
5709
|
+
const chainTag = s.chain ? chalk13.dim(`[${s.chain}] `) : "";
|
|
5710
|
+
console.log(` ${chainTag}#${s.id} ${chalk13.bold(ensName || String(s.vault))}`);
|
|
5711
|
+
if (ensName) console.log(` Vault: ${chalk13.cyan(String(s.vault))}`);
|
|
4971
5712
|
console.log(` Creator: ${s.creator}`);
|
|
4972
5713
|
console.log(` Created: ${date}`);
|
|
4973
5714
|
if (s.totalDeposits) {
|
|
4974
5715
|
console.log(` Deposits: ${s.totalDeposits} USDC`);
|
|
4975
5716
|
}
|
|
4976
5717
|
if (s.metadataURI) {
|
|
4977
|
-
console.log(` Metadata: ${
|
|
5718
|
+
console.log(` Metadata: ${chalk13.dim(s.metadataURI)}`);
|
|
4978
5719
|
}
|
|
4979
5720
|
console.log();
|
|
4980
5721
|
}
|
|
4981
5722
|
} catch (err) {
|
|
4982
5723
|
spinner.fail("Failed to load syndicates");
|
|
4983
|
-
console.error(
|
|
5724
|
+
console.error(chalk13.red(formatContractError(err)));
|
|
4984
5725
|
process.exit(1);
|
|
4985
5726
|
}
|
|
4986
5727
|
});
|
|
@@ -5020,38 +5761,38 @@ syndicate.command("info").description("Display syndicate details by ID or subdom
|
|
|
5020
5761
|
spinner.stop();
|
|
5021
5762
|
if (crossChainHint) {
|
|
5022
5763
|
const others = crossChainHint.others.length > 0 ? ` (also on: ${crossChainHint.others.join(", ")})` : "";
|
|
5023
|
-
console.log(
|
|
5024
|
-
console.log(
|
|
5764
|
+
console.log(chalk13.yellow(` Found on chain: ${chalk13.bold(crossChainHint.network)}${others}`));
|
|
5765
|
+
console.log(chalk13.dim(` Re-run with --chain ${crossChainHint.network} to interact with this syndicate.`));
|
|
5025
5766
|
console.log();
|
|
5026
5767
|
}
|
|
5027
5768
|
if (!info.vault || info.vault === "0x0000000000000000000000000000000000000000") {
|
|
5028
|
-
console.log(
|
|
5769
|
+
console.log(chalk13.red(`Syndicate #${id} not found.`));
|
|
5029
5770
|
process.exit(1);
|
|
5030
5771
|
}
|
|
5031
5772
|
const date = new Date(Number(info.createdAt) * 1e3).toLocaleDateString();
|
|
5032
5773
|
console.log();
|
|
5033
|
-
console.log(
|
|
5034
|
-
console.log(
|
|
5774
|
+
console.log(chalk13.bold(`Syndicate #${info.id}`));
|
|
5775
|
+
console.log(chalk13.dim("\u2500".repeat(40)));
|
|
5035
5776
|
if (info.subdomain) {
|
|
5036
|
-
console.log(` ENS: ${
|
|
5777
|
+
console.log(` ENS: ${chalk13.bold(`${info.subdomain}.sherwoodagent.eth`)}`);
|
|
5037
5778
|
}
|
|
5038
|
-
console.log(` Vault: ${
|
|
5779
|
+
console.log(` Vault: ${chalk13.cyan(info.vault)}`);
|
|
5039
5780
|
console.log(` Creator: ${info.creator}`);
|
|
5040
5781
|
console.log(` Created: ${date}`);
|
|
5041
|
-
console.log(` Active: ${info.active ?
|
|
5782
|
+
console.log(` Active: ${info.active ? chalk13.green("yes") : chalk13.red("no")}`);
|
|
5042
5783
|
if (info.metadataURI) {
|
|
5043
|
-
console.log(` Metadata: ${
|
|
5784
|
+
console.log(` Metadata: ${chalk13.dim(info.metadataURI)}`);
|
|
5044
5785
|
}
|
|
5045
5786
|
if (info.subdomain) {
|
|
5046
5787
|
const xmtpGroupId = getCachedGroupId(info.subdomain);
|
|
5047
5788
|
if (xmtpGroupId) {
|
|
5048
|
-
console.log(` XMTP Group: ${
|
|
5789
|
+
console.log(` XMTP Group: ${chalk13.cyan(xmtpGroupId)}`);
|
|
5049
5790
|
}
|
|
5050
5791
|
}
|
|
5051
5792
|
setVaultAddress(info.vault);
|
|
5052
5793
|
const vaultInfo = crossChainHint ? await withNetwork(crossChainHint.network, () => getVaultInfo()) : await getVaultInfo();
|
|
5053
5794
|
console.log();
|
|
5054
|
-
console.log(
|
|
5795
|
+
console.log(chalk13.bold(" Vault Stats"));
|
|
5055
5796
|
console.log(` Total Assets: ${vaultInfo.totalAssets}`);
|
|
5056
5797
|
console.log(` Agent Count: ${vaultInfo.agentCount}`);
|
|
5057
5798
|
console.log(` Redemptions Locked: ${vaultInfo.redemptionsLocked}`);
|
|
@@ -5059,12 +5800,12 @@ syndicate.command("info").description("Display syndicate details by ID or subdom
|
|
|
5059
5800
|
console.log();
|
|
5060
5801
|
} catch (err) {
|
|
5061
5802
|
spinner.fail("Failed to load syndicate info");
|
|
5062
|
-
console.error(
|
|
5803
|
+
console.error(chalk13.red(formatContractError(err)));
|
|
5063
5804
|
process.exit(1);
|
|
5064
5805
|
}
|
|
5065
5806
|
});
|
|
5066
5807
|
syndicate.command("update-metadata").description("Update syndicate metadata (creator only)").requiredOption("--id <id>", "Syndicate ID").option("--name <name>", "Syndicate name").option("--description <text>", "Short description").option("--uri <uri>", "Direct metadata URI (skips IPFS upload)").action(async (opts) => {
|
|
5067
|
-
const spinner = ora8({ text:
|
|
5808
|
+
const spinner = ora8({ text: W6("Loading syndicate..."), color: "green" }).start();
|
|
5068
5809
|
try {
|
|
5069
5810
|
const syndicateId = BigInt(opts.id);
|
|
5070
5811
|
let metadataURI = opts.uri;
|
|
@@ -5076,7 +5817,7 @@ syndicate.command("update-metadata").description("Update syndicate metadata (cre
|
|
|
5076
5817
|
}
|
|
5077
5818
|
const name = opts.name || info.subdomain;
|
|
5078
5819
|
const description = opts.description || `${name} \u2014 a Sherwood syndicate on ${info.subdomain}.sherwoodagent.eth`;
|
|
5079
|
-
spinner.text =
|
|
5820
|
+
spinner.text = W6("Uploading metadata to IPFS...");
|
|
5080
5821
|
const metadata = {
|
|
5081
5822
|
schema: "sherwood/syndicate/v1",
|
|
5082
5823
|
name,
|
|
@@ -5087,15 +5828,15 @@ syndicate.command("update-metadata").description("Update syndicate metadata (cre
|
|
|
5087
5828
|
links: {}
|
|
5088
5829
|
};
|
|
5089
5830
|
metadataURI = await uploadMetadata(metadata);
|
|
5090
|
-
spinner.text =
|
|
5831
|
+
spinner.text = W6("Updating on-chain metadata...");
|
|
5091
5832
|
}
|
|
5092
5833
|
const hash = await updateMetadata(syndicateId, metadataURI);
|
|
5093
|
-
spinner.succeed(
|
|
5094
|
-
console.log(
|
|
5095
|
-
console.log(
|
|
5834
|
+
spinner.succeed(G6(`Metadata updated`));
|
|
5835
|
+
console.log(DIM6(` IPFS: ${metadataURI}`));
|
|
5836
|
+
console.log(DIM6(` ${getExplorerUrl(hash)}`));
|
|
5096
5837
|
} catch (err) {
|
|
5097
5838
|
spinner.fail("Metadata update failed");
|
|
5098
|
-
console.error(
|
|
5839
|
+
console.error(chalk13.red(formatContractError(err)));
|
|
5099
5840
|
process.exit(1);
|
|
5100
5841
|
}
|
|
5101
5842
|
});
|
|
@@ -5106,10 +5847,10 @@ syndicate.command("approve-depositor").description("Approve an address to deposi
|
|
|
5106
5847
|
const depositor = validateAddress(opts.depositor, "depositor");
|
|
5107
5848
|
const hash = await approveDepositor(depositor);
|
|
5108
5849
|
spinner.succeed(`Depositor approved: ${hash}`);
|
|
5109
|
-
console.log(
|
|
5850
|
+
console.log(chalk13.dim(` ${getExplorerUrl(hash)}`));
|
|
5110
5851
|
} catch (err) {
|
|
5111
5852
|
spinner.fail("Approval failed");
|
|
5112
|
-
console.error(
|
|
5853
|
+
console.error(chalk13.red(formatContractError(err)));
|
|
5113
5854
|
process.exit(1);
|
|
5114
5855
|
}
|
|
5115
5856
|
});
|
|
@@ -5120,10 +5861,10 @@ syndicate.command("remove-depositor").description("Remove an address from the de
|
|
|
5120
5861
|
const depositor = validateAddress(opts.depositor, "depositor");
|
|
5121
5862
|
const hash = await removeDepositor(depositor);
|
|
5122
5863
|
spinner.succeed(`Depositor removed: ${hash}`);
|
|
5123
|
-
console.log(
|
|
5864
|
+
console.log(chalk13.dim(` ${getExplorerUrl(hash)}`));
|
|
5124
5865
|
} catch (err) {
|
|
5125
5866
|
spinner.fail("Removal failed");
|
|
5126
|
-
console.error(
|
|
5867
|
+
console.error(chalk13.red(formatContractError(err)));
|
|
5127
5868
|
process.exit(1);
|
|
5128
5869
|
}
|
|
5129
5870
|
});
|
|
@@ -5143,7 +5884,7 @@ syndicate.command("add").description("Register an agent on a syndicate vault (cr
|
|
|
5143
5884
|
if (opts.agentId) {
|
|
5144
5885
|
agentId = BigInt(opts.agentId);
|
|
5145
5886
|
} else {
|
|
5146
|
-
const { AGENT_REGISTRY: AGENT_REGISTRY2 } = await import("./addresses-
|
|
5887
|
+
const { AGENT_REGISTRY: AGENT_REGISTRY2 } = await import("./addresses-DOWLGXGE.js");
|
|
5147
5888
|
const registry2 = AGENT_REGISTRY2().IDENTITY_REGISTRY;
|
|
5148
5889
|
if (registry2 === "0x0000000000000000000000000000000000000000") {
|
|
5149
5890
|
agentId = 0n;
|
|
@@ -5164,7 +5905,7 @@ syndicate.command("add").description("Register an agent on a syndicate vault (cr
|
|
|
5164
5905
|
});
|
|
5165
5906
|
if (balance === 0n) {
|
|
5166
5907
|
spinner.fail("Agent wallet does not own an ERC-8004 identity NFT");
|
|
5167
|
-
console.error(
|
|
5908
|
+
console.error(chalk13.dim(" Mint one first: sherwood identity mint --name <name>"));
|
|
5168
5909
|
process.exit(1);
|
|
5169
5910
|
}
|
|
5170
5911
|
const tokenId = await client.readContract({
|
|
@@ -5180,13 +5921,13 @@ syndicate.command("add").description("Register an agent on a syndicate vault (cr
|
|
|
5180
5921
|
args: [agentWallet, 0n]
|
|
5181
5922
|
});
|
|
5182
5923
|
agentId = tokenId;
|
|
5183
|
-
console.log(
|
|
5924
|
+
console.log(chalk13.dim(` Resolved agent ID: #${agentId}`));
|
|
5184
5925
|
}
|
|
5185
5926
|
}
|
|
5186
5927
|
spinner.text = "Registering agent...";
|
|
5187
5928
|
const hash = await registerAgent(agentId, agentWallet);
|
|
5188
5929
|
spinner.succeed(`Agent registered: ${hash}`);
|
|
5189
|
-
console.log(
|
|
5930
|
+
console.log(chalk13.dim(` ${getExplorerUrl(hash)}`));
|
|
5190
5931
|
try {
|
|
5191
5932
|
const xmtp = await loadXmtp();
|
|
5192
5933
|
const xmtpClient = await xmtp.getXmtpClient();
|
|
@@ -5198,14 +5939,14 @@ syndicate.command("add").description("Register an agent on a syndicate vault (cr
|
|
|
5198
5939
|
syndicate: subdomain,
|
|
5199
5940
|
timestamp: Math.floor(Date.now() / 1e3)
|
|
5200
5941
|
});
|
|
5201
|
-
console.log(
|
|
5942
|
+
console.log(chalk13.dim(` Added to chat: ${subdomain}`));
|
|
5202
5943
|
} catch {
|
|
5203
|
-
console.warn(
|
|
5204
|
-
console.warn(
|
|
5944
|
+
console.warn(chalk13.yellow(" \u26A0 Could not add agent to chat group"));
|
|
5945
|
+
console.warn(chalk13.dim(` If no group exists, run: sherwood chat ${subdomain} init`));
|
|
5205
5946
|
}
|
|
5206
5947
|
} catch (err) {
|
|
5207
5948
|
spinner.fail("Registration failed");
|
|
5208
|
-
console.error(
|
|
5949
|
+
console.error(chalk13.red(formatContractError(err)));
|
|
5209
5950
|
process.exit(1);
|
|
5210
5951
|
}
|
|
5211
5952
|
});
|
|
@@ -5213,7 +5954,7 @@ syndicate.command("share").description("Print the shareable dashboard URL for yo
|
|
|
5213
5954
|
try {
|
|
5214
5955
|
const subdomain = opts.subdomain || getPrimarySyndicate(getChain().id)?.subdomain;
|
|
5215
5956
|
if (!subdomain) {
|
|
5216
|
-
console.error(
|
|
5957
|
+
console.error(chalk13.red(" No syndicate configured. Pass --subdomain <name> or run 'syndicate create/join' first."));
|
|
5217
5958
|
process.exit(1);
|
|
5218
5959
|
}
|
|
5219
5960
|
const syndicateInfo = await resolveSyndicate(subdomain);
|
|
@@ -5222,12 +5963,12 @@ syndicate.command("share").description("Print the shareable dashboard URL for yo
|
|
|
5222
5963
|
const dashboardUrl = `https://sherwood.sh/syndicate/${subdomain}${refParam}`;
|
|
5223
5964
|
console.log();
|
|
5224
5965
|
console.log(LABEL5(" \u25C6 Share Your Syndicate"));
|
|
5225
|
-
|
|
5226
|
-
console.log(
|
|
5227
|
-
console.log(
|
|
5228
|
-
console.log(
|
|
5229
|
-
console.log(
|
|
5230
|
-
|
|
5966
|
+
SEP6();
|
|
5967
|
+
console.log(W6(` Dashboard: ${G6(dashboardUrl)}`));
|
|
5968
|
+
console.log(W6(` ENS: ${DIM6(`${subdomain}.sherwoodagent.eth`)}`));
|
|
5969
|
+
console.log(W6(` Vault: ${DIM6(syndicateInfo.vault)}`));
|
|
5970
|
+
console.log(W6(` Join CLI: ${DIM6(`sherwood syndicate join --subdomain ${subdomain}`)}`));
|
|
5971
|
+
SEP6();
|
|
5231
5972
|
try {
|
|
5232
5973
|
const { exec } = await import("child_process");
|
|
5233
5974
|
const platform = (await import("os")).platform();
|
|
@@ -5235,12 +5976,12 @@ syndicate.command("share").description("Print the shareable dashboard URL for yo
|
|
|
5235
5976
|
const proc = exec(cmd);
|
|
5236
5977
|
proc.stdin?.write(dashboardUrl);
|
|
5237
5978
|
proc.stdin?.end();
|
|
5238
|
-
console.log(
|
|
5979
|
+
console.log(G6(" \u2713 Dashboard URL copied to clipboard"));
|
|
5239
5980
|
} catch {
|
|
5240
5981
|
}
|
|
5241
5982
|
console.log();
|
|
5242
5983
|
} catch (err) {
|
|
5243
|
-
console.error(
|
|
5984
|
+
console.error(chalk13.red(` \u2716 ${formatContractError(err)}`));
|
|
5244
5985
|
process.exit(1);
|
|
5245
5986
|
}
|
|
5246
5987
|
});
|
|
@@ -5265,14 +6006,14 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
|
|
|
5265
6006
|
const hits = await searchSyndicateAcrossChains(subdomain);
|
|
5266
6007
|
spinner.fail(`Syndicate "${subdomain}" not found on ${getNetwork()}`);
|
|
5267
6008
|
if (hits.length === 0) {
|
|
5268
|
-
console.error(
|
|
6009
|
+
console.error(chalk13.red(` Not found on any supported chain.`));
|
|
5269
6010
|
} else if (hits.length === 1) {
|
|
5270
|
-
console.error(
|
|
5271
|
-
console.error(
|
|
6011
|
+
console.error(chalk13.yellow(` Found on chain: ${chalk13.bold(hits[0].network)}`));
|
|
6012
|
+
console.error(chalk13.dim(` Re-run: sherwood --chain ${hits[0].network} syndicate join --subdomain ${subdomain}`));
|
|
5272
6013
|
} else {
|
|
5273
6014
|
const chains = hits.map((h) => h.network).join(", ");
|
|
5274
|
-
console.error(
|
|
5275
|
-
console.error(
|
|
6015
|
+
console.error(chalk13.yellow(` Found on chains: ${chalk13.bold(chains)}`));
|
|
6016
|
+
console.error(chalk13.dim(` Specify which chain to join with --chain <network>.`));
|
|
5276
6017
|
}
|
|
5277
6018
|
process.exit(1);
|
|
5278
6019
|
}
|
|
@@ -5285,17 +6026,17 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
|
|
|
5285
6026
|
try {
|
|
5286
6027
|
const xmtp = await loadXmtp();
|
|
5287
6028
|
await xmtp.getXmtpClient();
|
|
5288
|
-
console.log(
|
|
6029
|
+
console.log(chalk13.dim(" XMTP identity ready"));
|
|
5289
6030
|
} catch {
|
|
5290
|
-
console.warn(
|
|
6031
|
+
console.warn(chalk13.yellow(" \u26A0 Could not initialize XMTP identity"));
|
|
5291
6032
|
}
|
|
5292
6033
|
try {
|
|
5293
6034
|
const cron = await loadCron();
|
|
5294
6035
|
const cronResult = cron.registerSyndicateCrons(subdomain, isTestnet(), getNotifyTo());
|
|
5295
6036
|
if (cronResult.isOpenClaw && cronResult.registered) {
|
|
5296
|
-
console.log(
|
|
6037
|
+
console.log(chalk13.green(" \u2713 Participation crons registered"));
|
|
5297
6038
|
} else if (!cronResult.isOpenClaw) {
|
|
5298
|
-
console.log(
|
|
6039
|
+
console.log(chalk13.dim(" Tip: Set up a scheduled process to run `sherwood session check " + subdomain + "` periodically"));
|
|
5299
6040
|
}
|
|
5300
6041
|
} catch {
|
|
5301
6042
|
}
|
|
@@ -5308,14 +6049,14 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
|
|
|
5308
6049
|
);
|
|
5309
6050
|
if (existingRequest) {
|
|
5310
6051
|
spinner.succeed("You already have a pending join request for this syndicate");
|
|
5311
|
-
console.log(
|
|
5312
|
-
console.log(
|
|
6052
|
+
console.log(chalk13.dim(` Attestation: ${existingRequest.uid}`));
|
|
6053
|
+
console.log(chalk13.dim(` Submitted: ${new Date(existingRequest.time * 1e3).toLocaleString()}`));
|
|
5313
6054
|
try {
|
|
5314
6055
|
const xmtp = await loadXmtp();
|
|
5315
6056
|
await xmtp.getXmtpClient();
|
|
5316
|
-
console.log(
|
|
6057
|
+
console.log(chalk13.dim(" XMTP identity ready"));
|
|
5317
6058
|
} catch {
|
|
5318
|
-
console.warn(
|
|
6059
|
+
console.warn(chalk13.yellow(" \u26A0 Could not initialize XMTP identity"));
|
|
5319
6060
|
}
|
|
5320
6061
|
return;
|
|
5321
6062
|
}
|
|
@@ -5336,7 +6077,7 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
|
|
|
5336
6077
|
spinner.succeed("Join request created (XMTP identity ready)");
|
|
5337
6078
|
} catch {
|
|
5338
6079
|
spinner.succeed("Join request created");
|
|
5339
|
-
console.warn(
|
|
6080
|
+
console.warn(chalk13.yellow(" \u26A0 Could not initialize XMTP identity \u2014 creator may not be able to auto-add you to chat"));
|
|
5340
6081
|
}
|
|
5341
6082
|
addSyndicate(getChain().id, { subdomain, vault: syndicate2.vault, role: "agent" });
|
|
5342
6083
|
setPrimarySyndicate(getChain().id, subdomain);
|
|
@@ -5344,29 +6085,29 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
|
|
|
5344
6085
|
const cron = await loadCron();
|
|
5345
6086
|
const cronResult = cron.registerSyndicateCrons(subdomain, isTestnet(), getNotifyTo());
|
|
5346
6087
|
if (cronResult.isOpenClaw && cronResult.registered) {
|
|
5347
|
-
console.log(
|
|
6088
|
+
console.log(G6(" \u2713 Participation crons registered (will activate after approval)"));
|
|
5348
6089
|
} else if (!cronResult.isOpenClaw) {
|
|
5349
|
-
console.log(
|
|
6090
|
+
console.log(DIM6(" Tip: Set up a scheduled process to run `sherwood session check " + subdomain + "` periodically"));
|
|
5350
6091
|
}
|
|
5351
6092
|
} catch {
|
|
5352
6093
|
}
|
|
5353
6094
|
console.log();
|
|
5354
6095
|
console.log(LABEL5(" \u25C6 Join Request Submitted"));
|
|
5355
|
-
|
|
5356
|
-
console.log(
|
|
5357
|
-
console.log(
|
|
5358
|
-
console.log(
|
|
5359
|
-
console.log(
|
|
5360
|
-
console.log(
|
|
5361
|
-
console.log(
|
|
5362
|
-
|
|
5363
|
-
console.log(
|
|
5364
|
-
console.log(
|
|
5365
|
-
console.log(
|
|
6096
|
+
SEP6();
|
|
6097
|
+
console.log(W6(` Syndicate: ${G6(`${subdomain}.sherwoodagent.eth`)}`));
|
|
6098
|
+
console.log(W6(` Agent ID: #${agentId}`));
|
|
6099
|
+
console.log(W6(` Creator: ${DIM6(syndicate2.creator)}`));
|
|
6100
|
+
console.log(W6(` Attestation: ${DIM6(uid)}`));
|
|
6101
|
+
console.log(W6(` EAS Scan: ${DIM6(getEasScanUrl(uid))}`));
|
|
6102
|
+
console.log(W6(` Explorer: ${DIM6(getEasExplorerUrl(hash))}`));
|
|
6103
|
+
SEP6();
|
|
6104
|
+
console.log(G6(" \u2713 Syndicate saved to ~/.sherwood/config.json (set as primary)"));
|
|
6105
|
+
console.log(G6(" \u2713 The creator can review with:"));
|
|
6106
|
+
console.log(DIM6(` sherwood syndicate requests --subdomain ${subdomain}`));
|
|
5366
6107
|
console.log();
|
|
5367
6108
|
} catch (err) {
|
|
5368
6109
|
spinner.fail("Join request failed");
|
|
5369
|
-
console.error(
|
|
6110
|
+
console.error(chalk13.red(formatContractError(err)));
|
|
5370
6111
|
process.exit(1);
|
|
5371
6112
|
}
|
|
5372
6113
|
});
|
|
@@ -5407,34 +6148,34 @@ syndicate.command("requests").description("View pending join requests for a synd
|
|
|
5407
6148
|
);
|
|
5408
6149
|
spinner.stop();
|
|
5409
6150
|
if (requests.length === 0) {
|
|
5410
|
-
console.log(
|
|
6151
|
+
console.log(DIM6("\n No pending join requests.\n"));
|
|
5411
6152
|
return;
|
|
5412
6153
|
}
|
|
5413
6154
|
console.log();
|
|
5414
6155
|
console.log(LABEL5(` \u25C6 Pending Join Requests (${requests.length})`));
|
|
5415
|
-
|
|
6156
|
+
SEP6();
|
|
5416
6157
|
for (let i = 0; i < requests.length; i++) {
|
|
5417
6158
|
const req = requests[i];
|
|
5418
6159
|
const date = new Date(req.time * 1e3).toLocaleString();
|
|
5419
6160
|
const referrer = parseReferrer(req.decoded.message);
|
|
5420
6161
|
const cleanMessage = stripReferrerPrefix(req.decoded.message);
|
|
5421
|
-
console.log(
|
|
5422
|
-
console.log(
|
|
6162
|
+
console.log(W6(` ${i + 1}. Agent #${req.decoded.agentId} ${DIM6(`(${req.attester})`)}`));
|
|
6163
|
+
console.log(DIM6(` Message: "${cleanMessage}"`));
|
|
5423
6164
|
if (referrer !== null) {
|
|
5424
|
-
console.log(
|
|
6165
|
+
console.log(G6(` Referred by: Agent #${referrer}`));
|
|
5425
6166
|
}
|
|
5426
|
-
console.log(
|
|
5427
|
-
console.log(
|
|
6167
|
+
console.log(DIM6(` Requested: ${date}`));
|
|
6168
|
+
console.log(DIM6(` Attestation: ${req.uid}`));
|
|
5428
6169
|
console.log();
|
|
5429
6170
|
}
|
|
5430
|
-
console.log(
|
|
5431
|
-
console.log(
|
|
5432
|
-
console.log(
|
|
5433
|
-
console.log(
|
|
6171
|
+
console.log(G6(" To approve:"));
|
|
6172
|
+
console.log(DIM6(` sherwood syndicate approve --agent-id <id> --wallet <addr>`));
|
|
6173
|
+
console.log(G6(" To reject:"));
|
|
6174
|
+
console.log(DIM6(` sherwood syndicate reject --attestation <uid>`));
|
|
5434
6175
|
console.log();
|
|
5435
6176
|
} catch (err) {
|
|
5436
6177
|
spinner.fail("Failed to load requests");
|
|
5437
|
-
console.error(
|
|
6178
|
+
console.error(chalk13.red(formatContractError(err)));
|
|
5438
6179
|
process.exit(1);
|
|
5439
6180
|
}
|
|
5440
6181
|
});
|
|
@@ -5464,11 +6205,11 @@ syndicate.command("approve").description("Approve an agent join request (registe
|
|
|
5464
6205
|
agentWallet
|
|
5465
6206
|
);
|
|
5466
6207
|
agentWasRegistered = true;
|
|
5467
|
-
console.log(
|
|
6208
|
+
console.log(DIM6(` Agent registered: ${getExplorerUrl(regHash)}`));
|
|
5468
6209
|
} catch (regErr) {
|
|
5469
6210
|
const msg = regErr instanceof Error ? regErr.message : String(regErr);
|
|
5470
6211
|
if (msg.includes("0xe098d3ee") || msg.includes("AgentAlreadyRegistered")) {
|
|
5471
|
-
console.log(
|
|
6212
|
+
console.log(DIM6(" Agent already registered on vault \u2014 skipping"));
|
|
5472
6213
|
} else {
|
|
5473
6214
|
throw regErr;
|
|
5474
6215
|
}
|
|
@@ -5484,7 +6225,7 @@ syndicate.command("approve").description("Approve an agent join request (registe
|
|
|
5484
6225
|
let approvalUid;
|
|
5485
6226
|
if (alreadyApproved) {
|
|
5486
6227
|
approvalUid = alreadyApproved.uid;
|
|
5487
|
-
console.log(
|
|
6228
|
+
console.log(DIM6(` Approval attestation already exists \u2014 skipping`));
|
|
5488
6229
|
} else {
|
|
5489
6230
|
spinner.text = "Creating approval attestation...";
|
|
5490
6231
|
const result = await createApproval(
|
|
@@ -5507,23 +6248,23 @@ syndicate.command("approve").description("Approve an agent join request (registe
|
|
|
5507
6248
|
syndicate: subdomain,
|
|
5508
6249
|
timestamp: Math.floor(Date.now() / 1e3)
|
|
5509
6250
|
});
|
|
5510
|
-
console.log(
|
|
6251
|
+
console.log(DIM6(` Added to chat: ${subdomain}`));
|
|
5511
6252
|
} catch {
|
|
5512
|
-
console.warn(
|
|
5513
|
-
console.warn(
|
|
6253
|
+
console.warn(chalk13.yellow(" \u26A0 Could not add agent to chat group"));
|
|
6254
|
+
console.warn(chalk13.dim(` If no group exists, run: sherwood chat ${subdomain} init`));
|
|
5514
6255
|
}
|
|
5515
6256
|
spinner.succeed("Agent approved and registered");
|
|
5516
6257
|
console.log();
|
|
5517
6258
|
console.log(LABEL5(" \u25C6 Agent Approved"));
|
|
5518
|
-
|
|
5519
|
-
console.log(
|
|
5520
|
-
console.log(
|
|
5521
|
-
console.log(
|
|
5522
|
-
console.log(
|
|
5523
|
-
|
|
6259
|
+
SEP6();
|
|
6260
|
+
console.log(W6(` Agent ID: #${opts.agentId}`));
|
|
6261
|
+
console.log(W6(` Wallet: ${G6(opts.wallet)}`));
|
|
6262
|
+
console.log(W6(` Approval: ${DIM6(approvalUid)}`));
|
|
6263
|
+
console.log(W6(` EAS Scan: ${DIM6(getEasScanUrl(approvalUid))}`));
|
|
6264
|
+
SEP6();
|
|
5524
6265
|
} catch (err) {
|
|
5525
6266
|
spinner.fail("Approval failed");
|
|
5526
|
-
console.error(
|
|
6267
|
+
console.error(chalk13.red(formatContractError(err)));
|
|
5527
6268
|
process.exit(1);
|
|
5528
6269
|
}
|
|
5529
6270
|
});
|
|
@@ -5532,17 +6273,17 @@ syndicate.command("reject").description("Reject a join request by revoking its a
|
|
|
5532
6273
|
try {
|
|
5533
6274
|
const hash = await revokeJoinRequest(opts.attestation);
|
|
5534
6275
|
spinner.succeed("Join request rejected");
|
|
5535
|
-
console.log(
|
|
6276
|
+
console.log(DIM6(` ${getEasExplorerUrl(hash)}`));
|
|
5536
6277
|
} catch (err) {
|
|
5537
6278
|
spinner.fail("Rejection failed");
|
|
5538
|
-
console.error(
|
|
6279
|
+
console.error(chalk13.red(formatContractError(err)));
|
|
5539
6280
|
process.exit(1);
|
|
5540
6281
|
}
|
|
5541
6282
|
});
|
|
5542
6283
|
syndicate.command("leave").description("Leave a syndicate \u2014 removes participation crons and session state").option("--subdomain <name>", "Syndicate subdomain to leave").action(async (opts) => {
|
|
5543
6284
|
const subdomain = opts.subdomain || getPrimarySyndicate(getChain().id)?.subdomain;
|
|
5544
6285
|
if (!subdomain) {
|
|
5545
|
-
console.error(
|
|
6286
|
+
console.error(chalk13.red(" No syndicate configured. Pass --subdomain <name>."));
|
|
5546
6287
|
process.exit(1);
|
|
5547
6288
|
}
|
|
5548
6289
|
const spinner = ora8("Cleaning up...").start();
|
|
@@ -5558,17 +6299,17 @@ syndicate.command("leave").description("Leave a syndicate \u2014 removes partici
|
|
|
5558
6299
|
resetSession(subdomain);
|
|
5559
6300
|
spinner.succeed("Left syndicate");
|
|
5560
6301
|
if (cronsRemoved) {
|
|
5561
|
-
console.log(
|
|
6302
|
+
console.log(G6(" \u2713 Participation crons removed"));
|
|
5562
6303
|
}
|
|
5563
|
-
console.log(
|
|
6304
|
+
console.log(G6(" \u2713 Session state cleared"));
|
|
5564
6305
|
console.log();
|
|
5565
|
-
console.log(
|
|
5566
|
-
console.log(
|
|
5567
|
-
console.log(
|
|
6306
|
+
console.log(chalk13.dim(" Note: This does not remove you on-chain. To exit your position:"));
|
|
6307
|
+
console.log(chalk13.dim(" sherwood vault balance \u2014 check your LP share balance"));
|
|
6308
|
+
console.log(chalk13.dim(" Redeem shares via the vault contract or dashboard"));
|
|
5568
6309
|
console.log();
|
|
5569
6310
|
} catch (err) {
|
|
5570
6311
|
spinner.fail("Leave failed");
|
|
5571
|
-
console.error(
|
|
6312
|
+
console.error(chalk13.red(err instanceof Error ? err.message : String(err)));
|
|
5572
6313
|
process.exit(1);
|
|
5573
6314
|
}
|
|
5574
6315
|
});
|
|
@@ -5579,21 +6320,21 @@ syndicate.command("set-primary").description("Set the active syndicate for CLI c
|
|
|
5579
6320
|
if (!opts.subdomain && !opts.vault) {
|
|
5580
6321
|
const primary = getPrimarySyndicate(chainId);
|
|
5581
6322
|
if (memberships.length === 0) {
|
|
5582
|
-
console.log(
|
|
6323
|
+
console.log(DIM6("\n No syndicates saved. Create or join one first.\n"));
|
|
5583
6324
|
return;
|
|
5584
6325
|
}
|
|
5585
6326
|
console.log();
|
|
5586
6327
|
console.log(LABEL5(" \u25C6 Your Syndicates"));
|
|
5587
|
-
|
|
6328
|
+
SEP6();
|
|
5588
6329
|
for (const m of memberships) {
|
|
5589
6330
|
const isPrimary = primary?.subdomain === m.subdomain;
|
|
5590
|
-
const marker = isPrimary ?
|
|
5591
|
-
console.log(
|
|
5592
|
-
console.log(
|
|
6331
|
+
const marker = isPrimary ? G6(" \u2605") : "";
|
|
6332
|
+
console.log(W6(` ${m.subdomain}.sherwoodagent.eth${marker} ${DIM6(`(${m.role})`)}`));
|
|
6333
|
+
console.log(DIM6(` vault: ${m.vault}`));
|
|
5593
6334
|
}
|
|
5594
|
-
|
|
6335
|
+
SEP6();
|
|
5595
6336
|
if (memberships.length > 1) {
|
|
5596
|
-
console.log(
|
|
6337
|
+
console.log(DIM6(" Switch with: sherwood syndicate set-primary --subdomain <name>"));
|
|
5597
6338
|
}
|
|
5598
6339
|
console.log();
|
|
5599
6340
|
return;
|
|
@@ -5619,15 +6360,15 @@ syndicate.command("set-primary").description("Set the active syndicate for CLI c
|
|
|
5619
6360
|
}
|
|
5620
6361
|
}
|
|
5621
6362
|
if (!target) {
|
|
5622
|
-
console.error(
|
|
6363
|
+
console.error(chalk13.red(" Could not resolve syndicate. Pass --subdomain or --vault."));
|
|
5623
6364
|
process.exit(1);
|
|
5624
6365
|
}
|
|
5625
6366
|
setPrimarySyndicate(chainId, target);
|
|
5626
|
-
console.log(
|
|
6367
|
+
console.log(G6(`
|
|
5627
6368
|
\u2713 Primary syndicate set to ${target}.sherwoodagent.eth
|
|
5628
6369
|
`));
|
|
5629
6370
|
} catch (err) {
|
|
5630
|
-
console.error(
|
|
6371
|
+
console.error(chalk13.red(formatContractError(err)));
|
|
5631
6372
|
process.exit(1);
|
|
5632
6373
|
}
|
|
5633
6374
|
});
|
|
@@ -5641,7 +6382,7 @@ vaultCmd.command("deposit").description("Deposit into a vault").option("--vault
|
|
|
5641
6382
|
await preflightDeposit(amount);
|
|
5642
6383
|
}
|
|
5643
6384
|
} catch (err) {
|
|
5644
|
-
console.error(
|
|
6385
|
+
console.error(chalk13.red(`
|
|
5645
6386
|
\u2716 ${err instanceof Error ? err.message : String(err)}
|
|
5646
6387
|
`));
|
|
5647
6388
|
process.exit(1);
|
|
@@ -5656,10 +6397,10 @@ vaultCmd.command("deposit").description("Deposit into a vault").option("--vault
|
|
|
5656
6397
|
hash = await deposit(amount);
|
|
5657
6398
|
}
|
|
5658
6399
|
spinner.succeed(`Deposited: ${hash}`);
|
|
5659
|
-
console.log(
|
|
6400
|
+
console.log(chalk13.dim(` ${getExplorerUrl(hash)}`));
|
|
5660
6401
|
} catch (err) {
|
|
5661
6402
|
spinner.fail("Deposit failed");
|
|
5662
|
-
console.error(
|
|
6403
|
+
console.error(chalk13.red(formatContractError(err)));
|
|
5663
6404
|
process.exit(1);
|
|
5664
6405
|
}
|
|
5665
6406
|
});
|
|
@@ -5670,8 +6411,8 @@ vaultCmd.command("info").description("Display vault state").option("--vault <add
|
|
|
5670
6411
|
const info = await getVaultInfo();
|
|
5671
6412
|
spinner.stop();
|
|
5672
6413
|
console.log();
|
|
5673
|
-
console.log(
|
|
5674
|
-
console.log(
|
|
6414
|
+
console.log(chalk13.bold("Vault Info"));
|
|
6415
|
+
console.log(chalk13.dim("\u2500".repeat(40)));
|
|
5675
6416
|
console.log(` Address: ${info.address}`);
|
|
5676
6417
|
console.log(` Total Assets: ${info.totalAssets}`);
|
|
5677
6418
|
console.log(` Agent Count: ${info.agentCount}`);
|
|
@@ -5680,7 +6421,7 @@ vaultCmd.command("info").description("Display vault state").option("--vault <add
|
|
|
5680
6421
|
console.log();
|
|
5681
6422
|
} catch (err) {
|
|
5682
6423
|
spinner.fail("Failed to load vault info");
|
|
5683
|
-
console.error(
|
|
6424
|
+
console.error(chalk13.red(formatContractError(err)));
|
|
5684
6425
|
process.exit(1);
|
|
5685
6426
|
}
|
|
5686
6427
|
});
|
|
@@ -5691,15 +6432,15 @@ vaultCmd.command("balance").description("Show LP share balance and asset value")
|
|
|
5691
6432
|
const balance = await getBalance(opts.address);
|
|
5692
6433
|
spinner.stop();
|
|
5693
6434
|
console.log();
|
|
5694
|
-
console.log(
|
|
5695
|
-
console.log(
|
|
6435
|
+
console.log(chalk13.bold("LP Position"));
|
|
6436
|
+
console.log(chalk13.dim("\u2500".repeat(40)));
|
|
5696
6437
|
console.log(` Shares: ${balance.shares.toString()}`);
|
|
5697
6438
|
console.log(` Asset Value: ${balance.assetsValue}`);
|
|
5698
6439
|
console.log(` % of Vault: ${balance.percentOfVault}`);
|
|
5699
6440
|
console.log();
|
|
5700
6441
|
} catch (err) {
|
|
5701
6442
|
spinner.fail("Failed to load balance");
|
|
5702
|
-
console.error(
|
|
6443
|
+
console.error(chalk13.red(formatContractError(err)));
|
|
5703
6444
|
process.exit(1);
|
|
5704
6445
|
}
|
|
5705
6446
|
});
|
|
@@ -5714,18 +6455,18 @@ vaultCmd.command("redeem").description("Redeem vault shares for the underlying a
|
|
|
5714
6455
|
} else {
|
|
5715
6456
|
shares = await getShareBalance();
|
|
5716
6457
|
if (shares === 0n) {
|
|
5717
|
-
console.error(
|
|
6458
|
+
console.error(chalk13.red("\n \u2716 No shares to redeem.\n"));
|
|
5718
6459
|
process.exit(1);
|
|
5719
6460
|
}
|
|
5720
6461
|
}
|
|
5721
6462
|
} catch (err) {
|
|
5722
|
-
console.error(
|
|
6463
|
+
console.error(chalk13.red(`
|
|
5723
6464
|
\u2716 ${err instanceof Error ? err.message : String(err)}
|
|
5724
6465
|
`));
|
|
5725
6466
|
process.exit(1);
|
|
5726
6467
|
}
|
|
5727
6468
|
if (opts.receiver && !isAddress6(opts.receiver)) {
|
|
5728
|
-
console.error(
|
|
6469
|
+
console.error(chalk13.red(`
|
|
5729
6470
|
\u2716 Invalid receiver address: ${opts.receiver}
|
|
5730
6471
|
`));
|
|
5731
6472
|
process.exit(1);
|
|
@@ -5733,7 +6474,7 @@ vaultCmd.command("redeem").description("Redeem vault shares for the underlying a
|
|
|
5733
6474
|
try {
|
|
5734
6475
|
await preflightRedeem(shares);
|
|
5735
6476
|
} catch (err) {
|
|
5736
|
-
console.error(
|
|
6477
|
+
console.error(chalk13.red(`
|
|
5737
6478
|
\u2716 ${err instanceof Error ? err.message : String(err)}
|
|
5738
6479
|
`));
|
|
5739
6480
|
process.exit(1);
|
|
@@ -5746,18 +6487,18 @@ vaultCmd.command("redeem").description("Redeem vault shares for the underlying a
|
|
|
5746
6487
|
opts.receiver
|
|
5747
6488
|
);
|
|
5748
6489
|
spinner.succeed(`Redeemed: ${hash}`);
|
|
5749
|
-
console.log(
|
|
6490
|
+
console.log(chalk13.dim(` ${getExplorerUrl(hash)}`));
|
|
5750
6491
|
console.log(` Assets received: ${formatUnits5(assetsOut, assetDecimals)}`);
|
|
5751
6492
|
} catch (err) {
|
|
5752
6493
|
spinner.fail("Redeem failed");
|
|
5753
|
-
console.error(
|
|
6494
|
+
console.error(chalk13.red(formatContractError(err)));
|
|
5754
6495
|
process.exit(1);
|
|
5755
6496
|
}
|
|
5756
6497
|
});
|
|
5757
6498
|
var strategy = program.command("strategy").description("Strategy templates \u2014 list, clone, propose");
|
|
5758
6499
|
registerStrategyTemplateCommands(strategy);
|
|
5759
6500
|
program.command("providers").description("List available DeFi providers").action(async () => {
|
|
5760
|
-
const { MessariProvider, NansenProvider } = await import("./research-
|
|
6501
|
+
const { MessariProvider, NansenProvider } = await import("./research-YJWSQS2A.js");
|
|
5761
6502
|
const providers = [new MoonwellProvider(), new UniswapProvider(), new MessariProvider(), new NansenProvider()];
|
|
5762
6503
|
for (const p of providers) {
|
|
5763
6504
|
const info = p.info();
|
|
@@ -5768,17 +6509,17 @@ ${info.name} (${info.type})`);
|
|
|
5768
6509
|
}
|
|
5769
6510
|
});
|
|
5770
6511
|
try {
|
|
5771
|
-
const { registerChatCommands } = await import("./chat-
|
|
6512
|
+
const { registerChatCommands } = await import("./chat-MRTPUZ3X.js");
|
|
5772
6513
|
registerChatCommands(program);
|
|
5773
6514
|
} catch {
|
|
5774
6515
|
program.command("chat <name> [action] [actionArgs...]").description("Syndicate chat (XMTP) \u2014 requires @xmtp/cli").action(() => {
|
|
5775
|
-
console.error(
|
|
5776
|
-
console.error(
|
|
5777
|
-
console.error(
|
|
6516
|
+
console.error(chalk13.red("XMTP CLI not available."));
|
|
6517
|
+
console.error(chalk13.dim("Install with: npm install -g @xmtp/cli"));
|
|
6518
|
+
console.error(chalk13.dim("Or reinstall: npm i -g @sherwoodagent/cli"));
|
|
5778
6519
|
process.exit(1);
|
|
5779
6520
|
});
|
|
5780
6521
|
}
|
|
5781
|
-
var { registerSessionCommands } = await import("./session-
|
|
6522
|
+
var { registerSessionCommands } = await import("./session-FPCPCVQX.js");
|
|
5782
6523
|
registerSessionCommands(program);
|
|
5783
6524
|
registerVeniceCommands(program);
|
|
5784
6525
|
registerAllowanceCommands(program);
|
|
@@ -5786,65 +6527,71 @@ registerIdentityCommands(program);
|
|
|
5786
6527
|
registerProposalCommands(program);
|
|
5787
6528
|
registerGovernorCommands(program);
|
|
5788
6529
|
registerGuardianCommands(program);
|
|
5789
|
-
var { registerResearchCommands } = await import("./research-
|
|
6530
|
+
var { registerResearchCommands } = await import("./research-QBKISW2M.js");
|
|
5790
6531
|
registerResearchCommands(program);
|
|
5791
|
-
var { registerAgentCommands } = await import("./agent-
|
|
6532
|
+
var { registerAgentCommands } = await import("./agent-4HKHLQEJ.js");
|
|
5792
6533
|
registerAgentCommands(program);
|
|
5793
|
-
var { registerTradeCommands } = await import("./trade-
|
|
6534
|
+
var { registerTradeCommands } = await import("./trade-NO6CNWCA.js");
|
|
5794
6535
|
registerTradeCommands(program);
|
|
6536
|
+
registerGridCommand(program);
|
|
5795
6537
|
var configCmd = program.command("config");
|
|
5796
|
-
configCmd.command("set").description("Save settings to ~/.sherwood/config.json (persists across sessions)").option("--private-key <key>", "Wallet private key (0x-prefixed)").option("--vault <address>", "Default SyndicateVault address").option("--rpc <url>", "Custom RPC URL for the active --chain network").option("--notify-to <id>", "Destination for cron summaries (Telegram chat ID, phone, etc.)").option("--uniswap-api-key <key>", "Uniswap Trading API key (from developers.uniswap.org)").option("--venice-api-key <key>", "Venice AI inference API key").option("--xmtp-group <subdomain:groupId>", "Cache an XMTP group ID for a syndicate (e.g. my-fund:abc123)").action((opts) => {
|
|
6538
|
+
configCmd.command("set").description("Save settings to ~/.sherwood/config.json (persists across sessions)").option("--private-key <key>", "Wallet private key (0x-prefixed)").option("--vault <address>", "Default SyndicateVault address").option("--rpc <url>", "Custom RPC URL for the active --chain network").option("--notify-to <id>", "Destination for cron summaries (Telegram chat ID, phone, etc.)").option("--uniswap-api-key <key>", "Uniswap Trading API key (from developers.uniswap.org)").option("--venice-api-key <key>", "Venice AI inference API key").option("--anthropic-api-key <key>", "Anthropic Claude API key (for LLM judge)").option("--xmtp-group <subdomain:groupId>", "Cache an XMTP group ID for a syndicate (e.g. my-fund:abc123)").action((opts) => {
|
|
5797
6539
|
let saved = false;
|
|
5798
6540
|
if (opts.privateKey) {
|
|
5799
6541
|
setPrivateKey(opts.privateKey);
|
|
5800
6542
|
const account = getAccount();
|
|
5801
|
-
console.log(
|
|
5802
|
-
console.log(
|
|
6543
|
+
console.log(chalk13.green("Private key saved to ~/.sherwood/config.json"));
|
|
6544
|
+
console.log(chalk13.dim(` Wallet: ${account.address}`));
|
|
5803
6545
|
saved = true;
|
|
5804
6546
|
}
|
|
5805
6547
|
if (opts.vault) {
|
|
5806
6548
|
const chainId = getChain().id;
|
|
5807
6549
|
setChainContract(chainId, "vault", opts.vault);
|
|
5808
|
-
console.log(
|
|
5809
|
-
console.log(
|
|
6550
|
+
console.log(chalk13.green(`Vault saved to ~/.sherwood/config.json (chain ${chainId})`));
|
|
6551
|
+
console.log(chalk13.dim(` Vault: ${opts.vault}`));
|
|
5810
6552
|
saved = true;
|
|
5811
6553
|
}
|
|
5812
6554
|
if (opts.rpc) {
|
|
5813
6555
|
const network = getNetwork();
|
|
5814
6556
|
setConfigRpcUrl(network, opts.rpc);
|
|
5815
|
-
console.log(
|
|
5816
|
-
console.log(
|
|
6557
|
+
console.log(chalk13.green(`RPC URL saved for ${network}`));
|
|
6558
|
+
console.log(chalk13.dim(` RPC: ${opts.rpc}`));
|
|
5817
6559
|
saved = true;
|
|
5818
6560
|
}
|
|
5819
6561
|
if (opts.notifyTo) {
|
|
5820
6562
|
setNotifyTo(opts.notifyTo);
|
|
5821
|
-
console.log(
|
|
5822
|
-
console.log(
|
|
6563
|
+
console.log(chalk13.green("Notify destination saved to ~/.sherwood/config.json"));
|
|
6564
|
+
console.log(chalk13.dim(` Notify to: ${opts.notifyTo}`));
|
|
5823
6565
|
saved = true;
|
|
5824
6566
|
}
|
|
5825
6567
|
if (opts.uniswapApiKey) {
|
|
5826
6568
|
setUniswapApiKey(opts.uniswapApiKey);
|
|
5827
|
-
console.log(
|
|
6569
|
+
console.log(chalk13.green("Uniswap API key saved to ~/.sherwood/config.json"));
|
|
5828
6570
|
saved = true;
|
|
5829
6571
|
}
|
|
5830
6572
|
if (opts.veniceApiKey) {
|
|
5831
6573
|
setVeniceApiKey(opts.veniceApiKey);
|
|
5832
|
-
console.log(
|
|
6574
|
+
console.log(chalk13.green("Venice API key saved to ~/.sherwood/config.json"));
|
|
6575
|
+
saved = true;
|
|
6576
|
+
}
|
|
6577
|
+
if (opts.anthropicApiKey) {
|
|
6578
|
+
setAnthropicApiKey(opts.anthropicApiKey);
|
|
6579
|
+
console.log(chalk13.green("Anthropic API key saved to ~/.sherwood/config.json"));
|
|
5833
6580
|
saved = true;
|
|
5834
6581
|
}
|
|
5835
6582
|
if (opts.xmtpGroup) {
|
|
5836
6583
|
const parts = opts.xmtpGroup.split(":");
|
|
5837
6584
|
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
|
5838
|
-
console.log(
|
|
6585
|
+
console.log(chalk13.red("Format: --xmtp-group <subdomain>:<groupId>"));
|
|
5839
6586
|
process.exit(1);
|
|
5840
6587
|
}
|
|
5841
6588
|
cacheGroupId(parts[0], parts[1]);
|
|
5842
|
-
console.log(
|
|
5843
|
-
console.log(
|
|
6589
|
+
console.log(chalk13.green(`XMTP group ID cached for ${parts[0]}`));
|
|
6590
|
+
console.log(chalk13.dim(` Group ID: ${parts[1]}`));
|
|
5844
6591
|
saved = true;
|
|
5845
6592
|
}
|
|
5846
6593
|
if (!saved) {
|
|
5847
|
-
console.log(
|
|
6594
|
+
console.log(chalk13.red("Provide at least one of: --private-key, --vault, --rpc, --notify-to, --uniswap-api-key, --venice-api-key, --anthropic-api-key, --xmtp-group"));
|
|
5848
6595
|
process.exit(1);
|
|
5849
6596
|
}
|
|
5850
6597
|
});
|
|
@@ -5855,25 +6602,25 @@ configCmd.command("show").description("Display current config for the active net
|
|
|
5855
6602
|
const config = loadConfig();
|
|
5856
6603
|
const customRpc = config.rpc?.[network];
|
|
5857
6604
|
console.log();
|
|
5858
|
-
console.log(
|
|
5859
|
-
console.log(
|
|
5860
|
-
console.log(` Network: ${
|
|
5861
|
-
console.log(` RPC: ${customRpc ?
|
|
5862
|
-
console.log(` Wallet: ${config.privateKey ?
|
|
5863
|
-
console.log(` Agent ID: ${config.agentId ??
|
|
5864
|
-
console.log(` Vault: ${contracts.vault ??
|
|
5865
|
-
console.log(` Uniswap: ${getUniswapApiKey() ?
|
|
5866
|
-
console.log(` Venice: ${getVeniceApiKey() ?
|
|
6605
|
+
console.log(chalk13.bold(`Sherwood Config`));
|
|
6606
|
+
console.log(chalk13.dim("\u2500".repeat(50)));
|
|
6607
|
+
console.log(` Network: ${chalk13.cyan(network)} (chain ${chainId})`);
|
|
6608
|
+
console.log(` RPC: ${customRpc ? chalk13.green(customRpc) : chalk13.dim("default")}`);
|
|
6609
|
+
console.log(` Wallet: ${config.privateKey ? chalk13.green("configured") : chalk13.dim("not set")}`);
|
|
6610
|
+
console.log(` Agent ID: ${config.agentId ?? chalk13.dim("not set")}`);
|
|
6611
|
+
console.log(` Vault: ${contracts.vault ?? chalk13.dim("not set")}`);
|
|
6612
|
+
console.log(` Uniswap: ${getUniswapApiKey() ? chalk13.green("API key configured") : chalk13.dim("not set")}`);
|
|
6613
|
+
console.log(` Venice: ${getVeniceApiKey() ? chalk13.green("API key configured") : chalk13.dim("not set")}`);
|
|
5867
6614
|
const groupEntries = Object.entries(config.groupCache || {}).filter(([, v]) => v);
|
|
5868
6615
|
if (groupEntries.length > 0) {
|
|
5869
6616
|
console.log();
|
|
5870
|
-
console.log(
|
|
6617
|
+
console.log(chalk13.bold(" XMTP Groups"));
|
|
5871
6618
|
for (const [sub, gid] of groupEntries) {
|
|
5872
|
-
console.log(` ${sub}: ${
|
|
6619
|
+
console.log(` ${sub}: ${chalk13.cyan(gid)}`);
|
|
5873
6620
|
}
|
|
5874
6621
|
}
|
|
5875
6622
|
console.log();
|
|
5876
|
-
console.log(
|
|
6623
|
+
console.log(chalk13.dim(" Config file: ~/.sherwood/config.json"));
|
|
5877
6624
|
console.log();
|
|
5878
6625
|
});
|
|
5879
6626
|
program.parse();
|