@sherwoodagent/cli 0.47.1 → 0.51.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-4HKHLQEJ.js → agent-ZPAZTJG3.js} +48 -154
- package/dist/agent-ZPAZTJG3.js.map +1 -0
- package/dist/{chunk-R2P4C2I7.js → chunk-OMW47WOO.js} +142 -2
- package/dist/chunk-OMW47WOO.js.map +1 -0
- package/dist/index.js +570 -163
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/agent-4HKHLQEJ.js.map +0 -1
- package/dist/chunk-R2P4C2I7.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
HyperliquidProvider,
|
|
4
|
-
getLatestSignals
|
|
5
|
-
|
|
4
|
+
getLatestSignals,
|
|
5
|
+
hlCancelAllOrders,
|
|
6
|
+
hlGetMeta,
|
|
7
|
+
hlPlaceLimitOrder,
|
|
8
|
+
resolveHLCoin
|
|
9
|
+
} from "./chunk-OMW47WOO.js";
|
|
6
10
|
import {
|
|
7
11
|
UniswapProvider,
|
|
8
12
|
applySlippage,
|
|
@@ -164,8 +168,8 @@ import {
|
|
|
164
168
|
import { config as loadDotenv } from "dotenv";
|
|
165
169
|
import { createRequire } from "module";
|
|
166
170
|
import { Command, Option } from "commander";
|
|
167
|
-
import { parseUnits as
|
|
168
|
-
import
|
|
171
|
+
import { parseUnits as parseUnits7, formatUnits as formatUnits5, isAddress as isAddress6 } from "viem";
|
|
172
|
+
import chalk15 from "chalk";
|
|
169
173
|
import ora8 from "ora";
|
|
170
174
|
import { input, confirm, select } from "@inquirer/prompts";
|
|
171
175
|
|
|
@@ -2350,11 +2354,11 @@ ${opts.prompt}`;
|
|
|
2350
2354
|
}
|
|
2351
2355
|
try {
|
|
2352
2356
|
const { createVeniceInferenceAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-PEEWGSAO.js");
|
|
2353
|
-
const { keccak256, toHex, isAddress: isAddr } = await import("viem");
|
|
2357
|
+
const { keccak256: keccak2562, toHex, isAddress: isAddr } = await import("viem");
|
|
2354
2358
|
const { getChainContracts: getChainContracts2 } = await import("./config-REASKDIK.js");
|
|
2355
2359
|
const { getChain: getActiveChain } = await import("./network-3ZU7UBDU.js");
|
|
2356
2360
|
const vaultRecipient = opts.vault && isAddr(opts.vault) ? opts.vault : getChainContracts2(getActiveChain().id).vault;
|
|
2357
|
-
const promptHash =
|
|
2361
|
+
const promptHash = keccak2562(toHex(userContent)).slice(0, 18);
|
|
2358
2362
|
const { uid } = await createVeniceInferenceAttestation(
|
|
2359
2363
|
result.model,
|
|
2360
2364
|
result.usage.promptTokens,
|
|
@@ -4618,13 +4622,13 @@ function registerGuardianCommands(program2) {
|
|
|
4618
4622
|
}
|
|
4619
4623
|
|
|
4620
4624
|
// src/commands/grid.ts
|
|
4621
|
-
import
|
|
4625
|
+
import chalk14 from "chalk";
|
|
4622
4626
|
|
|
4623
4627
|
// src/grid/loop.ts
|
|
4624
|
-
import
|
|
4625
|
-
import { appendFile, mkdir as
|
|
4626
|
-
import { join as
|
|
4627
|
-
import { homedir as
|
|
4628
|
+
import chalk13 from "chalk";
|
|
4629
|
+
import { appendFile, mkdir as mkdir4 } from "fs/promises";
|
|
4630
|
+
import { join as join4 } from "path";
|
|
4631
|
+
import { homedir as homedir4 } from "os";
|
|
4628
4632
|
|
|
4629
4633
|
// src/grid/manager.ts
|
|
4630
4634
|
import chalk9 from "chalk";
|
|
@@ -4964,6 +4968,45 @@ var GridManager = class {
|
|
|
4964
4968
|
};
|
|
4965
4969
|
}).filter((e) => e.fillCount > 0);
|
|
4966
4970
|
}
|
|
4971
|
+
/**
|
|
4972
|
+
* Compute the orders that should be placed for the current grid state,
|
|
4973
|
+
* without simulating fills. Used by the live executor.
|
|
4974
|
+
*
|
|
4975
|
+
* Returns:
|
|
4976
|
+
* - ordersToPlace: all current grid levels that haven't been filled
|
|
4977
|
+
* - assetsToCancel: tokens whose grid was rebalanced (need cancel-and-place)
|
|
4978
|
+
* - needsRebalance: whether any grid was rebuilt this tick
|
|
4979
|
+
*/
|
|
4980
|
+
computeOrders(prices) {
|
|
4981
|
+
const state = this.portfolio.getState();
|
|
4982
|
+
if (!state || state.paused || !this.config.enabled) {
|
|
4983
|
+
return { ordersToPlace: [], assetsToCancel: [], needsRebalance: false };
|
|
4984
|
+
}
|
|
4985
|
+
const ordersToPlace = [];
|
|
4986
|
+
const assetsToCancel = [];
|
|
4987
|
+
let needsRebalance = false;
|
|
4988
|
+
for (const grid of state.grids) {
|
|
4989
|
+
const price = prices[grid.token];
|
|
4990
|
+
if (!price || price <= 0) continue;
|
|
4991
|
+
const wasEmpty = grid.levels.length === 0;
|
|
4992
|
+
const fullRebuild = wasEmpty || this.needsFullRebuild(grid);
|
|
4993
|
+
const shift = !fullRebuild && grid.centerPrice > 0 && this.needsShift(grid, price);
|
|
4994
|
+
if (fullRebuild || shift) {
|
|
4995
|
+
needsRebalance = true;
|
|
4996
|
+
if (!wasEmpty) assetsToCancel.push(grid.token);
|
|
4997
|
+
}
|
|
4998
|
+
for (const level of grid.levels) {
|
|
4999
|
+
if (level.filled) continue;
|
|
5000
|
+
ordersToPlace.push({
|
|
5001
|
+
token: grid.token,
|
|
5002
|
+
isBuy: level.side === "buy",
|
|
5003
|
+
price: level.price,
|
|
5004
|
+
quantity: level.quantity
|
|
5005
|
+
});
|
|
5006
|
+
}
|
|
5007
|
+
}
|
|
5008
|
+
return { ordersToPlace, assetsToCancel, needsRebalance };
|
|
5009
|
+
}
|
|
4967
5010
|
/** Get aggregate stats for display. */
|
|
4968
5011
|
getStats() {
|
|
4969
5012
|
const state = this.portfolio.getState();
|
|
@@ -5033,8 +5076,8 @@ var GridHedgeManager = class {
|
|
|
5033
5076
|
await mkdir2(dirname2(HEDGE_STATE_PATH), { recursive: true });
|
|
5034
5077
|
const tmp = `${HEDGE_STATE_PATH}.tmp.${process.pid}`;
|
|
5035
5078
|
await writeFile2(tmp, JSON.stringify(this.state, null, 2), "utf-8");
|
|
5036
|
-
const { rename:
|
|
5037
|
-
await
|
|
5079
|
+
const { rename: rename3 } = await import("fs/promises");
|
|
5080
|
+
await rename3(tmp, HEDGE_STATE_PATH);
|
|
5038
5081
|
}
|
|
5039
5082
|
/**
|
|
5040
5083
|
* Run one hedge tick. Call after grid tick with current prices and open fill data.
|
|
@@ -5152,14 +5195,330 @@ var GridHedgeManager = class {
|
|
|
5152
5195
|
}
|
|
5153
5196
|
};
|
|
5154
5197
|
|
|
5198
|
+
// src/grid/executor.ts
|
|
5199
|
+
import chalk11 from "chalk";
|
|
5200
|
+
var GridExecutor = class {
|
|
5201
|
+
cfg;
|
|
5202
|
+
constructor(cfg) {
|
|
5203
|
+
this.cfg = cfg;
|
|
5204
|
+
}
|
|
5205
|
+
/**
|
|
5206
|
+
* Execute the order plan against Hyperliquid.
|
|
5207
|
+
* Cancels stale orders for rebalanced tokens, then places new orders.
|
|
5208
|
+
*/
|
|
5209
|
+
async execute(plan) {
|
|
5210
|
+
const errors = [];
|
|
5211
|
+
let cancelled = 0;
|
|
5212
|
+
let placed = 0;
|
|
5213
|
+
for (const token of plan.assetsToCancel) {
|
|
5214
|
+
const coin = resolveHLCoin(token);
|
|
5215
|
+
if (!coin) {
|
|
5216
|
+
errors.push(`No HL ticker for ${token}`);
|
|
5217
|
+
continue;
|
|
5218
|
+
}
|
|
5219
|
+
try {
|
|
5220
|
+
await hlCancelAllOrders(coin);
|
|
5221
|
+
cancelled++;
|
|
5222
|
+
console.error(chalk11.dim(` [grid-exec] Cancelled all orders for ${coin}`));
|
|
5223
|
+
} catch (e) {
|
|
5224
|
+
errors.push(`Cancel ${coin} failed: ${e.message}`);
|
|
5225
|
+
}
|
|
5226
|
+
}
|
|
5227
|
+
for (const order of plan.ordersToPlace) {
|
|
5228
|
+
const coin = resolveHLCoin(order.token);
|
|
5229
|
+
if (!coin) {
|
|
5230
|
+
errors.push(`No HL ticker for ${order.token}`);
|
|
5231
|
+
continue;
|
|
5232
|
+
}
|
|
5233
|
+
try {
|
|
5234
|
+
const res = await hlPlaceLimitOrder(coin, order.isBuy, order.quantity, order.price);
|
|
5235
|
+
if (res.success) {
|
|
5236
|
+
placed++;
|
|
5237
|
+
} else {
|
|
5238
|
+
errors.push(`Place ${coin} ${order.isBuy ? "buy" : "sell"} @${order.price}: ${res.error}`);
|
|
5239
|
+
}
|
|
5240
|
+
} catch (e) {
|
|
5241
|
+
errors.push(`Place ${coin} threw: ${e.message}`);
|
|
5242
|
+
}
|
|
5243
|
+
}
|
|
5244
|
+
return { placed, cancelled, errors };
|
|
5245
|
+
}
|
|
5246
|
+
};
|
|
5247
|
+
|
|
5248
|
+
// src/grid/onchain-executor.ts
|
|
5249
|
+
import chalk12 from "chalk";
|
|
5250
|
+
import { mkdir as mkdir3, readFile as readFile3, rename as rename2, writeFile as writeFile3 } from "fs/promises";
|
|
5251
|
+
import { dirname as dirname3, join as join3 } from "path";
|
|
5252
|
+
import { homedir as homedir3 } from "os";
|
|
5253
|
+
import { encodeAbiParameters as encodeAbiParameters10, parseUnits as parseUnits6 } from "viem";
|
|
5254
|
+
|
|
5255
|
+
// src/grid/cloid.ts
|
|
5256
|
+
import { keccak256, encodeAbiParameters as encodeAbiParameters9 } from "viem";
|
|
5257
|
+
function gridCloid(strategy2, assetIndex, isBuy, levelIndex, nonce) {
|
|
5258
|
+
const hash = keccak256(
|
|
5259
|
+
encodeAbiParameters9(
|
|
5260
|
+
[
|
|
5261
|
+
{ type: "address" },
|
|
5262
|
+
{ type: "uint32" },
|
|
5263
|
+
{ type: "bool" },
|
|
5264
|
+
{ type: "uint32" },
|
|
5265
|
+
{ type: "uint32" }
|
|
5266
|
+
],
|
|
5267
|
+
[strategy2, assetIndex, isBuy, levelIndex, nonce]
|
|
5268
|
+
)
|
|
5269
|
+
);
|
|
5270
|
+
const top16 = hash.slice(0, 2 + 32);
|
|
5271
|
+
return BigInt(top16);
|
|
5272
|
+
}
|
|
5273
|
+
|
|
5274
|
+
// src/grid/strategy-abi.ts
|
|
5275
|
+
var HYPERLIQUID_GRID_STRATEGY_ABI = [
|
|
5276
|
+
{
|
|
5277
|
+
type: "function",
|
|
5278
|
+
name: "updateParams",
|
|
5279
|
+
inputs: [{ name: "data", type: "bytes" }],
|
|
5280
|
+
outputs: [],
|
|
5281
|
+
stateMutability: "nonpayable"
|
|
5282
|
+
}
|
|
5283
|
+
];
|
|
5284
|
+
|
|
5285
|
+
// src/grid/onchain-executor.ts
|
|
5286
|
+
var ONCHAIN_STATE_PATH = join3(homedir3(), ".sherwood", "grid", "onchain-state.json");
|
|
5287
|
+
var UINT64_MAX = 18446744073709551615n;
|
|
5288
|
+
var ACTION_PLACE_GRID = 1;
|
|
5289
|
+
var ACTION_CANCEL_ALL = 2;
|
|
5290
|
+
var ACTION_CANCEL_AND_PLACE = 3;
|
|
5291
|
+
var GRID_ORDER_COMPONENTS = [
|
|
5292
|
+
{ name: "assetIndex", type: "uint32" },
|
|
5293
|
+
{ name: "isBuy", type: "bool" },
|
|
5294
|
+
{ name: "limitPx", type: "uint64" },
|
|
5295
|
+
{ name: "sz", type: "uint64" },
|
|
5296
|
+
{ name: "cloid", type: "uint128" }
|
|
5297
|
+
];
|
|
5298
|
+
var OnchainGridExecutor = class {
|
|
5299
|
+
cfg;
|
|
5300
|
+
nonces = /* @__PURE__ */ new Map();
|
|
5301
|
+
placedCloids = /* @__PURE__ */ new Map();
|
|
5302
|
+
meta = null;
|
|
5303
|
+
busy = false;
|
|
5304
|
+
constructor(cfg) {
|
|
5305
|
+
this.cfg = cfg;
|
|
5306
|
+
}
|
|
5307
|
+
/** Load persisted state. Call once before the first execute(). */
|
|
5308
|
+
async load() {
|
|
5309
|
+
try {
|
|
5310
|
+
const raw = await readFile3(ONCHAIN_STATE_PATH, "utf-8");
|
|
5311
|
+
const state = JSON.parse(raw);
|
|
5312
|
+
for (const [tok, n] of Object.entries(state.nonces)) this.nonces.set(tok, n);
|
|
5313
|
+
for (const [tok, cloids] of Object.entries(state.placedCloids)) {
|
|
5314
|
+
this.placedCloids.set(tok, cloids.map((s) => BigInt(s)));
|
|
5315
|
+
}
|
|
5316
|
+
console.error(chalk12.dim(` [grid-onchain] Loaded state: ${this.nonces.size} tokens`));
|
|
5317
|
+
} catch (e) {
|
|
5318
|
+
const code = e.code;
|
|
5319
|
+
if (code === "ENOENT") {
|
|
5320
|
+
} else {
|
|
5321
|
+
console.error(
|
|
5322
|
+
chalk12.yellow(
|
|
5323
|
+
` [grid-onchain] Failed to load state (${e.message}) \u2014 starting fresh. Existing on-chain orders may be orphaned.`
|
|
5324
|
+
)
|
|
5325
|
+
);
|
|
5326
|
+
}
|
|
5327
|
+
}
|
|
5328
|
+
if (!this.meta) this.meta = await hlGetMeta();
|
|
5329
|
+
}
|
|
5330
|
+
/**
|
|
5331
|
+
* Atomic save: write to .tmp, then POSIX-rename. A crash mid-write leaves
|
|
5332
|
+
* either the old file intact or the new file fully written — never a
|
|
5333
|
+
* partial JSON that would crash load() on restart.
|
|
5334
|
+
*/
|
|
5335
|
+
async save() {
|
|
5336
|
+
const state = {
|
|
5337
|
+
nonces: Object.fromEntries(this.nonces),
|
|
5338
|
+
placedCloids: Object.fromEntries(
|
|
5339
|
+
[...this.placedCloids].map(([tok, cloids]) => [tok, cloids.map((c) => c.toString())])
|
|
5340
|
+
)
|
|
5341
|
+
};
|
|
5342
|
+
await mkdir3(dirname3(ONCHAIN_STATE_PATH), { recursive: true });
|
|
5343
|
+
const tmp = ONCHAIN_STATE_PATH + ".tmp";
|
|
5344
|
+
await writeFile3(tmp, JSON.stringify(state, null, 2));
|
|
5345
|
+
await rename2(tmp, ONCHAIN_STATE_PATH);
|
|
5346
|
+
}
|
|
5347
|
+
/**
|
|
5348
|
+
* Execute the order plan: cancel stale orders for rebalanced tokens, then
|
|
5349
|
+
* place new orders. Each (token) is one tx. Persists state after each tx
|
|
5350
|
+
* so a crash mid-execution still leaves a recoverable on-disk record.
|
|
5351
|
+
*
|
|
5352
|
+
* Concurrency-guarded: a second concurrent call returns immediately rather
|
|
5353
|
+
* than racing tx submission against another in-flight execute().
|
|
5354
|
+
*/
|
|
5355
|
+
async execute(plan) {
|
|
5356
|
+
if (this.busy) {
|
|
5357
|
+
return { placed: 0, cancelled: 0, txs: [], errors: ["execute() already in flight \u2014 skipping"] };
|
|
5358
|
+
}
|
|
5359
|
+
this.busy = true;
|
|
5360
|
+
try {
|
|
5361
|
+
if (!this.meta) await this.load();
|
|
5362
|
+
const errors = [];
|
|
5363
|
+
const txs = [];
|
|
5364
|
+
let placed = 0;
|
|
5365
|
+
let cancelled = 0;
|
|
5366
|
+
const ordersByToken = /* @__PURE__ */ new Map();
|
|
5367
|
+
for (const o of plan.ordersToPlace) {
|
|
5368
|
+
if (!ordersByToken.has(o.token)) ordersByToken.set(o.token, []);
|
|
5369
|
+
ordersByToken.get(o.token).push(o);
|
|
5370
|
+
}
|
|
5371
|
+
const cancelSet = new Set(plan.assetsToCancel);
|
|
5372
|
+
const allTokens = /* @__PURE__ */ new Set([...ordersByToken.keys(), ...cancelSet]);
|
|
5373
|
+
for (const token of allTokens) {
|
|
5374
|
+
const assetIndex = this.cfg.assetIndices[token];
|
|
5375
|
+
if (assetIndex === void 0) {
|
|
5376
|
+
errors.push(`No asset index for ${token}`);
|
|
5377
|
+
continue;
|
|
5378
|
+
}
|
|
5379
|
+
const coin = resolveHLCoin(token);
|
|
5380
|
+
if (!coin) {
|
|
5381
|
+
errors.push(`No HL ticker for ${token}`);
|
|
5382
|
+
continue;
|
|
5383
|
+
}
|
|
5384
|
+
const meta = this.meta.get(coin);
|
|
5385
|
+
if (!meta) {
|
|
5386
|
+
errors.push(`No HL meta for ${coin} (run \`sherwood grid status\` to refresh)`);
|
|
5387
|
+
continue;
|
|
5388
|
+
}
|
|
5389
|
+
const wantCancel = cancelSet.has(token);
|
|
5390
|
+
const orders = ordersByToken.get(token) ?? [];
|
|
5391
|
+
const wantPlace = orders.length > 0;
|
|
5392
|
+
try {
|
|
5393
|
+
if (wantCancel && wantPlace) {
|
|
5394
|
+
const tx = await this.cancelAndPlace(token, assetIndex, orders, meta);
|
|
5395
|
+
txs.push(tx);
|
|
5396
|
+
cancelled += 1;
|
|
5397
|
+
placed += orders.length;
|
|
5398
|
+
} else if (wantCancel) {
|
|
5399
|
+
const tx = await this.cancelAll(token, assetIndex);
|
|
5400
|
+
txs.push(tx);
|
|
5401
|
+
cancelled += 1;
|
|
5402
|
+
} else if (wantPlace) {
|
|
5403
|
+
const tx = await this.placeGrid(token, assetIndex, orders, meta);
|
|
5404
|
+
txs.push(tx);
|
|
5405
|
+
placed += orders.length;
|
|
5406
|
+
}
|
|
5407
|
+
await this.save();
|
|
5408
|
+
} catch (e) {
|
|
5409
|
+
errors.push(`${token}: ${e.message}`);
|
|
5410
|
+
}
|
|
5411
|
+
}
|
|
5412
|
+
return { placed, cancelled, txs, errors };
|
|
5413
|
+
} finally {
|
|
5414
|
+
this.busy = false;
|
|
5415
|
+
}
|
|
5416
|
+
}
|
|
5417
|
+
currentNonce(token) {
|
|
5418
|
+
return this.nonces.get(token) ?? 0;
|
|
5419
|
+
}
|
|
5420
|
+
bumpNonce(token) {
|
|
5421
|
+
const next = this.currentNonce(token) + 1;
|
|
5422
|
+
this.nonces.set(token, next);
|
|
5423
|
+
return next;
|
|
5424
|
+
}
|
|
5425
|
+
/**
|
|
5426
|
+
* Pure encoding — does NOT mutate placedCloids. Caller commits CLOIDs
|
|
5427
|
+
* only after the on-chain tx confirms successfully.
|
|
5428
|
+
*
|
|
5429
|
+
* Uses viem's `parseUnits` for decimal scaling instead of float math so
|
|
5430
|
+
* high-szDecimals tokens (BTC=5, ETH=4) keep full precision on small sizes.
|
|
5431
|
+
*/
|
|
5432
|
+
encodeOrders(assetIndex, orders, meta, nonce) {
|
|
5433
|
+
const cloids = [];
|
|
5434
|
+
const encoded = orders.map((o, i) => {
|
|
5435
|
+
const cloid = gridCloid(this.cfg.strategyAddress, assetIndex, o.isBuy, i, nonce);
|
|
5436
|
+
cloids.push(cloid);
|
|
5437
|
+
const limitPx = parseUnits6(o.price.toFixed(meta.pxDecimals), meta.pxDecimals);
|
|
5438
|
+
const sz = parseUnits6(o.quantity.toFixed(meta.szDecimals), meta.szDecimals);
|
|
5439
|
+
if (limitPx > UINT64_MAX || sz > UINT64_MAX) {
|
|
5440
|
+
throw new Error(
|
|
5441
|
+
`Order exceeds uint64: token=${o.token} px=${limitPx} sz=${sz}`
|
|
5442
|
+
);
|
|
5443
|
+
}
|
|
5444
|
+
return { assetIndex, isBuy: o.isBuy, limitPx, sz, cloid };
|
|
5445
|
+
});
|
|
5446
|
+
return { encoded, cloids };
|
|
5447
|
+
}
|
|
5448
|
+
async placeGrid(token, assetIndex, orders, meta) {
|
|
5449
|
+
const nonce = this.bumpNonce(token);
|
|
5450
|
+
const { encoded, cloids } = this.encodeOrders(assetIndex, orders, meta, nonce);
|
|
5451
|
+
const data = encodeAbiParameters10(
|
|
5452
|
+
[{ type: "uint8" }, { type: "tuple[]", components: [...GRID_ORDER_COMPONENTS] }],
|
|
5453
|
+
[ACTION_PLACE_GRID, encoded]
|
|
5454
|
+
);
|
|
5455
|
+
const tx = await this.send(data);
|
|
5456
|
+
this.placedCloids.set(token, cloids);
|
|
5457
|
+
return tx;
|
|
5458
|
+
}
|
|
5459
|
+
async cancelAll(token, assetIndex) {
|
|
5460
|
+
const cloids = this.placedCloids.get(token) ?? [];
|
|
5461
|
+
if (cloids.length === 0) {
|
|
5462
|
+
this.bumpNonce(token);
|
|
5463
|
+
return "0x";
|
|
5464
|
+
}
|
|
5465
|
+
const data = encodeAbiParameters10(
|
|
5466
|
+
[{ type: "uint8" }, { type: "uint32" }, { type: "uint128[]" }],
|
|
5467
|
+
[ACTION_CANCEL_ALL, assetIndex, cloids]
|
|
5468
|
+
);
|
|
5469
|
+
const tx = await this.send(data);
|
|
5470
|
+
this.placedCloids.set(token, []);
|
|
5471
|
+
this.bumpNonce(token);
|
|
5472
|
+
return tx;
|
|
5473
|
+
}
|
|
5474
|
+
async cancelAndPlace(token, assetIndex, orders, meta) {
|
|
5475
|
+
const oldCloids = this.placedCloids.get(token) ?? [];
|
|
5476
|
+
const newNonce = this.bumpNonce(token);
|
|
5477
|
+
const { encoded, cloids: newCloids } = this.encodeOrders(assetIndex, orders, meta, newNonce);
|
|
5478
|
+
const data = encodeAbiParameters10(
|
|
5479
|
+
[
|
|
5480
|
+
{ type: "uint8" },
|
|
5481
|
+
{ type: "uint32" },
|
|
5482
|
+
{ type: "uint128[]" },
|
|
5483
|
+
{ type: "tuple[]", components: [...GRID_ORDER_COMPONENTS] }
|
|
5484
|
+
],
|
|
5485
|
+
[ACTION_CANCEL_AND_PLACE, assetIndex, oldCloids, encoded]
|
|
5486
|
+
);
|
|
5487
|
+
const tx = await this.send(data);
|
|
5488
|
+
this.placedCloids.set(token, newCloids);
|
|
5489
|
+
return tx;
|
|
5490
|
+
}
|
|
5491
|
+
/**
|
|
5492
|
+
* Submit and wait for receipt. Throwing here surfaces to the per-token
|
|
5493
|
+
* try/catch in execute(), recording the error and skipping the save() so
|
|
5494
|
+
* placedCloids never reflects orders that didn't land.
|
|
5495
|
+
*/
|
|
5496
|
+
async send(data) {
|
|
5497
|
+
const tx = await writeContractWithRetry({
|
|
5498
|
+
address: this.cfg.strategyAddress,
|
|
5499
|
+
abi: HYPERLIQUID_GRID_STRATEGY_ABI,
|
|
5500
|
+
functionName: "updateParams",
|
|
5501
|
+
args: [data]
|
|
5502
|
+
});
|
|
5503
|
+
const pub = getPublicClient();
|
|
5504
|
+
const receipt = await pub.waitForTransactionReceipt({ hash: tx, timeout: 3e4 });
|
|
5505
|
+
if (receipt.status !== "success") {
|
|
5506
|
+
throw new Error(`Tx ${tx} reverted on-chain`);
|
|
5507
|
+
}
|
|
5508
|
+
console.error(chalk12.dim(` [grid-onchain] tx ${tx.slice(0, 10)}... confirmed`));
|
|
5509
|
+
return tx;
|
|
5510
|
+
}
|
|
5511
|
+
};
|
|
5512
|
+
|
|
5155
5513
|
// src/grid/loop.ts
|
|
5156
|
-
var GRID_CYCLES_PATH =
|
|
5514
|
+
var GRID_CYCLES_PATH = join4(homedir4(), ".sherwood", "grid", "cycles.jsonl");
|
|
5157
5515
|
var GridLoop = class {
|
|
5158
5516
|
cfg;
|
|
5159
5517
|
gridConfig;
|
|
5160
5518
|
manager;
|
|
5161
5519
|
hedge;
|
|
5162
5520
|
hl;
|
|
5521
|
+
executor = null;
|
|
5163
5522
|
running = false;
|
|
5164
5523
|
cycleCount = 0;
|
|
5165
5524
|
timer = null;
|
|
@@ -5169,12 +5528,25 @@ var GridLoop = class {
|
|
|
5169
5528
|
this.manager = new GridManager(this.gridConfig);
|
|
5170
5529
|
this.hedge = new GridHedgeManager();
|
|
5171
5530
|
this.hl = new HyperliquidProvider();
|
|
5531
|
+
if (cfg.live) {
|
|
5532
|
+
if (!cfg.assetIndices) {
|
|
5533
|
+
throw new Error("assetIndices required when live=true");
|
|
5534
|
+
}
|
|
5535
|
+
if (cfg.strategyAddress) {
|
|
5536
|
+
this.executor = new OnchainGridExecutor({
|
|
5537
|
+
strategyAddress: cfg.strategyAddress,
|
|
5538
|
+
assetIndices: cfg.assetIndices
|
|
5539
|
+
});
|
|
5540
|
+
} else {
|
|
5541
|
+
this.executor = new GridExecutor({ assetIndices: cfg.assetIndices });
|
|
5542
|
+
}
|
|
5543
|
+
}
|
|
5172
5544
|
}
|
|
5173
5545
|
/** Start the grid loop. Resolves when shut down via SIGINT/SIGTERM. */
|
|
5174
5546
|
async start() {
|
|
5175
5547
|
this.running = true;
|
|
5176
5548
|
const shutdown = () => {
|
|
5177
|
-
console.error(
|
|
5549
|
+
console.error(chalk13.yellow("\n [grid-loop] Shutting down\u2026"));
|
|
5178
5550
|
this.running = false;
|
|
5179
5551
|
if (this.timer) {
|
|
5180
5552
|
clearTimeout(this.timer);
|
|
@@ -5184,7 +5556,10 @@ var GridLoop = class {
|
|
|
5184
5556
|
process.on("SIGINT", shutdown);
|
|
5185
5557
|
process.on("SIGTERM", shutdown);
|
|
5186
5558
|
await this.manager.init(this.cfg.capital);
|
|
5187
|
-
|
|
5559
|
+
if (this.executor instanceof OnchainGridExecutor) {
|
|
5560
|
+
await this.executor.load();
|
|
5561
|
+
}
|
|
5562
|
+
console.error(chalk13.cyan(
|
|
5188
5563
|
`
|
|
5189
5564
|
[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
5565
|
`
|
|
@@ -5193,7 +5568,7 @@ var GridLoop = class {
|
|
|
5193
5568
|
try {
|
|
5194
5569
|
await this.tick();
|
|
5195
5570
|
} catch (err) {
|
|
5196
|
-
console.error(
|
|
5571
|
+
console.error(chalk13.red(` [grid-loop] Tick error: ${err.message}`));
|
|
5197
5572
|
}
|
|
5198
5573
|
if (this.running) {
|
|
5199
5574
|
await new Promise((resolve2) => {
|
|
@@ -5203,7 +5578,7 @@ var GridLoop = class {
|
|
|
5203
5578
|
}
|
|
5204
5579
|
process.off("SIGINT", shutdown);
|
|
5205
5580
|
process.off("SIGTERM", shutdown);
|
|
5206
|
-
console.error(
|
|
5581
|
+
console.error(chalk13.yellow(" [grid-loop] Stopped."));
|
|
5207
5582
|
}
|
|
5208
5583
|
/** Execute one grid cycle. */
|
|
5209
5584
|
async tick() {
|
|
@@ -5217,25 +5592,37 @@ var GridLoop = class {
|
|
|
5217
5592
|
}
|
|
5218
5593
|
}
|
|
5219
5594
|
if (Object.keys(prices).length === 0) {
|
|
5220
|
-
console.error(
|
|
5595
|
+
console.error(chalk13.dim(` [grid-loop] #${this.cycleCount} \u2014 no prices, skipping`));
|
|
5221
5596
|
return;
|
|
5222
5597
|
}
|
|
5223
5598
|
const result = await this.manager.tick(prices);
|
|
5224
5599
|
const elapsed = Date.now() - start;
|
|
5600
|
+
if (this.executor) {
|
|
5601
|
+
const plan = this.manager.computeOrders(prices);
|
|
5602
|
+
if (plan.ordersToPlace.length > 0 || plan.assetsToCancel.length > 0) {
|
|
5603
|
+
const res = await this.executor.execute(plan);
|
|
5604
|
+
if (res.errors.length > 0) {
|
|
5605
|
+
console.error(chalk13.yellow(` [grid-loop] Executor errors: ${res.errors.join("; ")}`));
|
|
5606
|
+
}
|
|
5607
|
+
if (res.placed > 0 || res.cancelled > 0) {
|
|
5608
|
+
console.error(chalk13.cyan(` [grid-loop] Live: placed=${res.placed} cancelled=${res.cancelled}`));
|
|
5609
|
+
}
|
|
5610
|
+
}
|
|
5611
|
+
}
|
|
5225
5612
|
if (result.roundTrips > 0) {
|
|
5226
|
-
console.error(
|
|
5613
|
+
console.error(chalk13.green(
|
|
5227
5614
|
` [grid-loop] #${this.cycleCount} \u2014 ${result.roundTrips} RT(s), +$${result.pnlUsd.toFixed(2)} PnL, ${result.fills} fill(s) [${elapsed}ms]`
|
|
5228
5615
|
));
|
|
5229
5616
|
}
|
|
5230
5617
|
if (result.fills > 0 && result.roundTrips === 0) {
|
|
5231
|
-
console.error(
|
|
5618
|
+
console.error(chalk13.dim(
|
|
5232
5619
|
` [grid-loop] #${this.cycleCount} \u2014 ${result.fills} fill(s), 0 RTs [${elapsed}ms]`
|
|
5233
5620
|
));
|
|
5234
5621
|
}
|
|
5235
5622
|
const openExposure = this.manager.getOpenFillExposure();
|
|
5236
5623
|
const hedgeResult = await this.hedge.tick(openExposure, prices);
|
|
5237
5624
|
if (hedgeResult.adjustments > 0) {
|
|
5238
|
-
console.error(
|
|
5625
|
+
console.error(chalk13.magenta(
|
|
5239
5626
|
` [hedge] ${hedgeResult.adjustments} adjustment(s), unrealized: $${hedgeResult.unrealizedPnl.toFixed(2)}, total realized: $${hedgeResult.totalRealizedPnl.toFixed(2)}`
|
|
5240
5627
|
));
|
|
5241
5628
|
}
|
|
@@ -5255,7 +5642,7 @@ var GridLoop = class {
|
|
|
5255
5642
|
hedgeTotalRealizedPnl: hedgeResult.totalRealizedPnl
|
|
5256
5643
|
};
|
|
5257
5644
|
try {
|
|
5258
|
-
await
|
|
5645
|
+
await mkdir4(join4(homedir4(), ".sherwood", "grid"), { recursive: true });
|
|
5259
5646
|
await appendFile(GRID_CYCLES_PATH, JSON.stringify(cycleEntry) + "\n");
|
|
5260
5647
|
} catch {
|
|
5261
5648
|
}
|
|
@@ -5264,7 +5651,7 @@ var GridLoop = class {
|
|
|
5264
5651
|
if (stats2) {
|
|
5265
5652
|
const hedgeStatus = this.hedge.getStatus();
|
|
5266
5653
|
const hedgeInfo = hedgeStatus && hedgeStatus.positions.length > 0 ? ` hedge=$${hedgeResult.unrealizedPnl.toFixed(2)}unr/$${hedgeStatus.totalRealizedPnl.toFixed(2)}real` : "";
|
|
5267
|
-
console.error(
|
|
5654
|
+
console.error(chalk13.cyan(
|
|
5268
5655
|
` [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
5656
|
));
|
|
5270
5657
|
}
|
|
@@ -5273,19 +5660,36 @@ var GridLoop = class {
|
|
|
5273
5660
|
};
|
|
5274
5661
|
|
|
5275
5662
|
// src/commands/grid.ts
|
|
5276
|
-
var DIM5 =
|
|
5277
|
-
var G5 =
|
|
5278
|
-
var BOLD5 =
|
|
5279
|
-
var W5 =
|
|
5663
|
+
var DIM5 = chalk14.gray;
|
|
5664
|
+
var G5 = chalk14.green;
|
|
5665
|
+
var BOLD5 = chalk14.white.bold;
|
|
5666
|
+
var W5 = chalk14.white;
|
|
5280
5667
|
var SEP5 = () => console.log(DIM5("\u2500".repeat(60)));
|
|
5281
5668
|
function registerGridCommand(program2) {
|
|
5282
5669
|
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) => {
|
|
5670
|
+
grid.command("start").description("Start the grid trading loop").option("--capital <usd>", "Starting capital in USD", "5000").option("--cycle <seconds>", "Cycle interval in seconds", "60").option("--tokens <list>", "Comma-separated token list", "bitcoin,ethereum,solana").option("--leverage <n>", "Leverage multiplier", "5").option("--levels <n>", "Grid levels per side", "15").option("--live", "enable live execution (places real orders on Hyperliquid)").option("--asset-indices <pairs>", "comma-separated token=index pairs (e.g. bitcoin=3,ethereum=4,solana=5)").option("--strategy <address>", "on-chain strategy contract address (enables on-chain executor)").action(async (opts) => {
|
|
5284
5671
|
const capital = parseFloat(opts.capital);
|
|
5285
5672
|
const cycleMs = parseInt(opts.cycle, 10) * 1e3;
|
|
5286
5673
|
const tokens = opts.tokens.split(",").map((t) => t.trim());
|
|
5287
5674
|
const leverage = parseFloat(opts.leverage);
|
|
5288
5675
|
const levels = parseInt(opts.levels, 10);
|
|
5676
|
+
const live = !!opts.live;
|
|
5677
|
+
let assetIndices;
|
|
5678
|
+
if (live) {
|
|
5679
|
+
if (!opts.assetIndices) {
|
|
5680
|
+
throw new Error("--asset-indices required when --live (e.g. --asset-indices bitcoin=3,ethereum=4,solana=5)");
|
|
5681
|
+
}
|
|
5682
|
+
assetIndices = {};
|
|
5683
|
+
for (const pair of opts.assetIndices.split(",")) {
|
|
5684
|
+
const [tok, idx] = pair.split("=");
|
|
5685
|
+
if (!tok || !idx) throw new Error(`Bad asset-indices pair: ${pair}`);
|
|
5686
|
+
assetIndices[tok.trim()] = Number(idx);
|
|
5687
|
+
}
|
|
5688
|
+
}
|
|
5689
|
+
const strategyAddress = opts.strategy;
|
|
5690
|
+
if (strategyAddress && !live) {
|
|
5691
|
+
throw new Error("--strategy requires --live");
|
|
5692
|
+
}
|
|
5289
5693
|
const weight = 1 / tokens.length;
|
|
5290
5694
|
const tokenSplit = {};
|
|
5291
5695
|
for (const t of tokens) {
|
|
@@ -5303,6 +5707,9 @@ function registerGridCommand(program2) {
|
|
|
5303
5707
|
const loop = new GridLoop({
|
|
5304
5708
|
capital,
|
|
5305
5709
|
cycle: cycleMs,
|
|
5710
|
+
live,
|
|
5711
|
+
assetIndices,
|
|
5712
|
+
strategyAddress,
|
|
5306
5713
|
config: {
|
|
5307
5714
|
...DEFAULT_GRID_CONFIG,
|
|
5308
5715
|
tokens,
|
|
@@ -5326,7 +5733,7 @@ function registerGridCommand(program2) {
|
|
|
5326
5733
|
console.log(G5.bold(" Grid Portfolio"));
|
|
5327
5734
|
SEP5();
|
|
5328
5735
|
console.log(W5(` Allocation: $${state.totalAllocation.toLocaleString()}`));
|
|
5329
|
-
console.log(W5(` Status: ${state.paused ?
|
|
5736
|
+
console.log(W5(` Status: ${state.paused ? chalk14.red("PAUSED \u2014 " + state.pauseReason) : G5("Active")}`));
|
|
5330
5737
|
console.log(W5(` Initialized: ${new Date(state.initializedAt).toLocaleString()}`));
|
|
5331
5738
|
SEP5();
|
|
5332
5739
|
console.log();
|
|
@@ -5339,13 +5746,13 @@ function registerGridCommand(program2) {
|
|
|
5339
5746
|
const pnl = `$${g.stats.totalPnlUsd.toFixed(2)}`.padStart(10);
|
|
5340
5747
|
const todayPnl = `$${g.stats.todayPnlUsd.toFixed(2)}`.padStart(10);
|
|
5341
5748
|
const fills = String(g.stats.totalFills).padStart(6);
|
|
5342
|
-
const pnlColor = g.stats.totalPnlUsd >= 0 ? G5 :
|
|
5343
|
-
const todayColor = g.stats.todayPnlUsd >= 0 ? G5 :
|
|
5749
|
+
const pnlColor = g.stats.totalPnlUsd >= 0 ? G5 : chalk14.red;
|
|
5750
|
+
const todayColor = g.stats.todayPnlUsd >= 0 ? G5 : chalk14.red;
|
|
5344
5751
|
console.log(` ${W5(name)} ${alloc} ${rts} ${pnlColor(pnl)} ${todayColor(todayPnl)} ${fills}`);
|
|
5345
5752
|
}
|
|
5346
5753
|
console.log(DIM5(" " + "\u2500".repeat(72)));
|
|
5347
|
-
const totalPnlColor = agg.totalPnlUsd >= 0 ? G5 :
|
|
5348
|
-
const todayTotalColor = agg.todayPnlUsd >= 0 ? G5 :
|
|
5754
|
+
const totalPnlColor = agg.totalPnlUsd >= 0 ? G5 : chalk14.red;
|
|
5755
|
+
const todayTotalColor = agg.todayPnlUsd >= 0 ? G5 : chalk14.red;
|
|
5349
5756
|
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
5757
|
console.log();
|
|
5351
5758
|
});
|
|
@@ -5364,15 +5771,15 @@ async function loadXmtp() {
|
|
|
5364
5771
|
async function loadCron() {
|
|
5365
5772
|
return import("./cron-EOULYO3R.js");
|
|
5366
5773
|
}
|
|
5367
|
-
var G6 =
|
|
5368
|
-
var W6 =
|
|
5369
|
-
var DIM6 =
|
|
5370
|
-
var BOLD6 =
|
|
5371
|
-
var LABEL5 =
|
|
5774
|
+
var G6 = chalk15.green;
|
|
5775
|
+
var W6 = chalk15.white;
|
|
5776
|
+
var DIM6 = chalk15.gray;
|
|
5777
|
+
var BOLD6 = chalk15.white.bold;
|
|
5778
|
+
var LABEL5 = chalk15.green.bold;
|
|
5372
5779
|
var SEP6 = () => console.log(DIM6("\u2500".repeat(60)));
|
|
5373
5780
|
function validateAddress(value, name) {
|
|
5374
5781
|
if (!isAddress6(value)) {
|
|
5375
|
-
console.error(
|
|
5782
|
+
console.error(chalk15.red(`Invalid ${name} address: ${value}`));
|
|
5376
5783
|
process.exit(1);
|
|
5377
5784
|
}
|
|
5378
5785
|
return value;
|
|
@@ -5392,7 +5799,7 @@ program.name("sherwood").description("CLI for agent-managed investment syndicate
|
|
|
5392
5799
|
process.env.ENABLE_TESTNET = "true";
|
|
5393
5800
|
if (network !== "base") {
|
|
5394
5801
|
console.warn(
|
|
5395
|
-
|
|
5802
|
+
chalk15.yellow("[warn] --testnet ignored, --chain takes precedence")
|
|
5396
5803
|
);
|
|
5397
5804
|
} else {
|
|
5398
5805
|
network = "base-sepolia";
|
|
@@ -5400,7 +5807,7 @@ program.name("sherwood").description("CLI for agent-managed investment syndicate
|
|
|
5400
5807
|
}
|
|
5401
5808
|
setNetwork(network);
|
|
5402
5809
|
if (getNetwork() !== "base") {
|
|
5403
|
-
console.log(
|
|
5810
|
+
console.log(chalk15.yellow(`[${getNetwork()}]`));
|
|
5404
5811
|
}
|
|
5405
5812
|
});
|
|
5406
5813
|
var syndicate = program.command("syndicate");
|
|
@@ -5448,7 +5855,7 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
5448
5855
|
asset = opts.asset;
|
|
5449
5856
|
} else {
|
|
5450
5857
|
const supported = Object.keys(ASSET_SYMBOLS).join(", ");
|
|
5451
|
-
console.error(
|
|
5858
|
+
console.error(chalk15.red(` Unknown asset "${opts.asset}". Use a symbol (${supported}) or a 0x address.`));
|
|
5452
5859
|
process.exit(1);
|
|
5453
5860
|
}
|
|
5454
5861
|
} else if (nonInteractive) {
|
|
@@ -5478,7 +5885,7 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
5478
5885
|
console.log(W6(` Agent ID: #${agentIdStr}`));
|
|
5479
5886
|
console.log(W6(` Asset: ${assetSymbol} (${asset.slice(0, 10)}...)`));
|
|
5480
5887
|
console.log(W6(` Share token: ${symbol}`));
|
|
5481
|
-
console.log(W6(` Open deposits: ${openDeposits ? G6("yes") :
|
|
5888
|
+
console.log(W6(` Open deposits: ${openDeposits ? G6("yes") : chalk15.red("no (whitelist)")}`));
|
|
5482
5889
|
SEP6();
|
|
5483
5890
|
if (!nonInteractive) {
|
|
5484
5891
|
const go = await confirm({ message: G6("Deploy syndicate?"), default: true });
|
|
@@ -5505,7 +5912,7 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
5505
5912
|
metadataURI = await uploadMetadata(metadata);
|
|
5506
5913
|
spinner2.succeed(G6(`Metadata pinned: ${DIM6(metadataURI)}`));
|
|
5507
5914
|
} catch (err) {
|
|
5508
|
-
spinner2.warn(
|
|
5915
|
+
spinner2.warn(chalk15.yellow(`IPFS upload failed \u2014 using inline metadata`));
|
|
5509
5916
|
const json = JSON.stringify({ name, description, subdomain, asset: assetSymbol, openDeposits, createdBy: "@sherwoodagent/cli" });
|
|
5510
5917
|
metadataURI = `data:application/json;base64,${Buffer.from(json).toString("base64")}`;
|
|
5511
5918
|
}
|
|
@@ -5515,12 +5922,12 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
5515
5922
|
const existingInfo = await getSyndicate(existingId);
|
|
5516
5923
|
const callerAddress = getAccount().address.toLowerCase();
|
|
5517
5924
|
if (existingInfo.creator.toLowerCase() !== callerAddress) {
|
|
5518
|
-
console.error(
|
|
5925
|
+
console.error(chalk15.red(`
|
|
5519
5926
|
\u2716 Subdomain '${subdomain}' is already taken by another syndicate.
|
|
5520
5927
|
`));
|
|
5521
5928
|
process.exit(1);
|
|
5522
5929
|
}
|
|
5523
|
-
console.log(
|
|
5930
|
+
console.log(chalk15.yellow(`
|
|
5524
5931
|
\u26A0 Syndicate '${subdomain}' already exists \u2014 resuming setup...
|
|
5525
5932
|
`));
|
|
5526
5933
|
const spinner2 = ora8({ text: W6("Resuming setup..."), color: "green" }).start();
|
|
@@ -5551,7 +5958,7 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
5551
5958
|
cacheGroupId(subdomain, groupId);
|
|
5552
5959
|
}
|
|
5553
5960
|
} catch {
|
|
5554
|
-
console.warn(
|
|
5961
|
+
console.warn(chalk15.yellow("\n \u26A0 Could not set up XMTP chat group"));
|
|
5555
5962
|
}
|
|
5556
5963
|
try {
|
|
5557
5964
|
const cron = await loadCron();
|
|
@@ -5598,7 +6005,7 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
5598
6005
|
// agentAddress = creator EOA (direct execution)
|
|
5599
6006
|
);
|
|
5600
6007
|
} catch (regErr) {
|
|
5601
|
-
console.warn(
|
|
6008
|
+
console.warn(chalk15.yellow("\n \u26A0 Could not auto-register creator as agent \u2014 register manually with `syndicate add`"));
|
|
5602
6009
|
}
|
|
5603
6010
|
spinner.text = W6("Setting up chat...");
|
|
5604
6011
|
try {
|
|
@@ -5611,8 +6018,8 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
5611
6018
|
} catch {
|
|
5612
6019
|
}
|
|
5613
6020
|
} catch {
|
|
5614
|
-
console.warn(
|
|
5615
|
-
console.warn(
|
|
6021
|
+
console.warn(chalk15.yellow("\n \u26A0 Could not create XMTP chat group"));
|
|
6022
|
+
console.warn(chalk15.dim(` Recover later with: sherwood chat ${subdomain} init`));
|
|
5616
6023
|
}
|
|
5617
6024
|
try {
|
|
5618
6025
|
const cron = await loadCron();
|
|
@@ -5640,7 +6047,7 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
5640
6047
|
console.log(G6(" \u2713 Syndicate saved to ~/.sherwood/config.json (set as primary)"));
|
|
5641
6048
|
console.log();
|
|
5642
6049
|
} catch (err) {
|
|
5643
|
-
console.error(
|
|
6050
|
+
console.error(chalk15.red(`
|
|
5644
6051
|
\u2716 ${formatContractError(err)}`));
|
|
5645
6052
|
process.exit(1);
|
|
5646
6053
|
}
|
|
@@ -5690,38 +6097,38 @@ syndicate.command("list").description("List active syndicates (queries subgraph,
|
|
|
5690
6097
|
}
|
|
5691
6098
|
spinner.stop();
|
|
5692
6099
|
if (syndicates.length === 0) {
|
|
5693
|
-
console.log(
|
|
6100
|
+
console.log(chalk15.dim("No active syndicates found."));
|
|
5694
6101
|
return;
|
|
5695
6102
|
}
|
|
5696
6103
|
console.log();
|
|
5697
|
-
console.log(
|
|
6104
|
+
console.log(chalk15.bold(`Active Syndicates (${syndicates.length})`));
|
|
5698
6105
|
if (!process.env.SUBGRAPH_URL && !opts.allChains) {
|
|
5699
|
-
console.log(
|
|
6106
|
+
console.log(chalk15.dim(" (Set SUBGRAPH_URL for faster indexed queries)"));
|
|
5700
6107
|
}
|
|
5701
6108
|
if (opts.allChains) {
|
|
5702
|
-
console.log(
|
|
6109
|
+
console.log(chalk15.dim(" (Aggregated across chains via on-chain factory calls; SUBGRAPH_URL is single-chain only)"));
|
|
5703
6110
|
}
|
|
5704
|
-
console.log(
|
|
6111
|
+
console.log(chalk15.dim("\u2500".repeat(70)));
|
|
5705
6112
|
for (const s of syndicates) {
|
|
5706
6113
|
const ts = typeof s.createdAt === "string" ? Number(s.createdAt) : Number(s.createdAt);
|
|
5707
6114
|
const date = new Date(ts * 1e3).toLocaleDateString();
|
|
5708
6115
|
const ensName = s.subdomain ? `${s.subdomain}.sherwoodagent.eth` : "";
|
|
5709
|
-
const chainTag = s.chain ?
|
|
5710
|
-
console.log(` ${chainTag}#${s.id} ${
|
|
5711
|
-
if (ensName) console.log(` Vault: ${
|
|
6116
|
+
const chainTag = s.chain ? chalk15.dim(`[${s.chain}] `) : "";
|
|
6117
|
+
console.log(` ${chainTag}#${s.id} ${chalk15.bold(ensName || String(s.vault))}`);
|
|
6118
|
+
if (ensName) console.log(` Vault: ${chalk15.cyan(String(s.vault))}`);
|
|
5712
6119
|
console.log(` Creator: ${s.creator}`);
|
|
5713
6120
|
console.log(` Created: ${date}`);
|
|
5714
6121
|
if (s.totalDeposits) {
|
|
5715
6122
|
console.log(` Deposits: ${s.totalDeposits} USDC`);
|
|
5716
6123
|
}
|
|
5717
6124
|
if (s.metadataURI) {
|
|
5718
|
-
console.log(` Metadata: ${
|
|
6125
|
+
console.log(` Metadata: ${chalk15.dim(s.metadataURI)}`);
|
|
5719
6126
|
}
|
|
5720
6127
|
console.log();
|
|
5721
6128
|
}
|
|
5722
6129
|
} catch (err) {
|
|
5723
6130
|
spinner.fail("Failed to load syndicates");
|
|
5724
|
-
console.error(
|
|
6131
|
+
console.error(chalk15.red(formatContractError(err)));
|
|
5725
6132
|
process.exit(1);
|
|
5726
6133
|
}
|
|
5727
6134
|
});
|
|
@@ -5761,38 +6168,38 @@ syndicate.command("info").description("Display syndicate details by ID or subdom
|
|
|
5761
6168
|
spinner.stop();
|
|
5762
6169
|
if (crossChainHint) {
|
|
5763
6170
|
const others = crossChainHint.others.length > 0 ? ` (also on: ${crossChainHint.others.join(", ")})` : "";
|
|
5764
|
-
console.log(
|
|
5765
|
-
console.log(
|
|
6171
|
+
console.log(chalk15.yellow(` Found on chain: ${chalk15.bold(crossChainHint.network)}${others}`));
|
|
6172
|
+
console.log(chalk15.dim(` Re-run with --chain ${crossChainHint.network} to interact with this syndicate.`));
|
|
5766
6173
|
console.log();
|
|
5767
6174
|
}
|
|
5768
6175
|
if (!info.vault || info.vault === "0x0000000000000000000000000000000000000000") {
|
|
5769
|
-
console.log(
|
|
6176
|
+
console.log(chalk15.red(`Syndicate #${id} not found.`));
|
|
5770
6177
|
process.exit(1);
|
|
5771
6178
|
}
|
|
5772
6179
|
const date = new Date(Number(info.createdAt) * 1e3).toLocaleDateString();
|
|
5773
6180
|
console.log();
|
|
5774
|
-
console.log(
|
|
5775
|
-
console.log(
|
|
6181
|
+
console.log(chalk15.bold(`Syndicate #${info.id}`));
|
|
6182
|
+
console.log(chalk15.dim("\u2500".repeat(40)));
|
|
5776
6183
|
if (info.subdomain) {
|
|
5777
|
-
console.log(` ENS: ${
|
|
6184
|
+
console.log(` ENS: ${chalk15.bold(`${info.subdomain}.sherwoodagent.eth`)}`);
|
|
5778
6185
|
}
|
|
5779
|
-
console.log(` Vault: ${
|
|
6186
|
+
console.log(` Vault: ${chalk15.cyan(info.vault)}`);
|
|
5780
6187
|
console.log(` Creator: ${info.creator}`);
|
|
5781
6188
|
console.log(` Created: ${date}`);
|
|
5782
|
-
console.log(` Active: ${info.active ?
|
|
6189
|
+
console.log(` Active: ${info.active ? chalk15.green("yes") : chalk15.red("no")}`);
|
|
5783
6190
|
if (info.metadataURI) {
|
|
5784
|
-
console.log(` Metadata: ${
|
|
6191
|
+
console.log(` Metadata: ${chalk15.dim(info.metadataURI)}`);
|
|
5785
6192
|
}
|
|
5786
6193
|
if (info.subdomain) {
|
|
5787
6194
|
const xmtpGroupId = getCachedGroupId(info.subdomain);
|
|
5788
6195
|
if (xmtpGroupId) {
|
|
5789
|
-
console.log(` XMTP Group: ${
|
|
6196
|
+
console.log(` XMTP Group: ${chalk15.cyan(xmtpGroupId)}`);
|
|
5790
6197
|
}
|
|
5791
6198
|
}
|
|
5792
6199
|
setVaultAddress(info.vault);
|
|
5793
6200
|
const vaultInfo = crossChainHint ? await withNetwork(crossChainHint.network, () => getVaultInfo()) : await getVaultInfo();
|
|
5794
6201
|
console.log();
|
|
5795
|
-
console.log(
|
|
6202
|
+
console.log(chalk15.bold(" Vault Stats"));
|
|
5796
6203
|
console.log(` Total Assets: ${vaultInfo.totalAssets}`);
|
|
5797
6204
|
console.log(` Agent Count: ${vaultInfo.agentCount}`);
|
|
5798
6205
|
console.log(` Redemptions Locked: ${vaultInfo.redemptionsLocked}`);
|
|
@@ -5800,7 +6207,7 @@ syndicate.command("info").description("Display syndicate details by ID or subdom
|
|
|
5800
6207
|
console.log();
|
|
5801
6208
|
} catch (err) {
|
|
5802
6209
|
spinner.fail("Failed to load syndicate info");
|
|
5803
|
-
console.error(
|
|
6210
|
+
console.error(chalk15.red(formatContractError(err)));
|
|
5804
6211
|
process.exit(1);
|
|
5805
6212
|
}
|
|
5806
6213
|
});
|
|
@@ -5836,7 +6243,7 @@ syndicate.command("update-metadata").description("Update syndicate metadata (cre
|
|
|
5836
6243
|
console.log(DIM6(` ${getExplorerUrl(hash)}`));
|
|
5837
6244
|
} catch (err) {
|
|
5838
6245
|
spinner.fail("Metadata update failed");
|
|
5839
|
-
console.error(
|
|
6246
|
+
console.error(chalk15.red(formatContractError(err)));
|
|
5840
6247
|
process.exit(1);
|
|
5841
6248
|
}
|
|
5842
6249
|
});
|
|
@@ -5847,10 +6254,10 @@ syndicate.command("approve-depositor").description("Approve an address to deposi
|
|
|
5847
6254
|
const depositor = validateAddress(opts.depositor, "depositor");
|
|
5848
6255
|
const hash = await approveDepositor(depositor);
|
|
5849
6256
|
spinner.succeed(`Depositor approved: ${hash}`);
|
|
5850
|
-
console.log(
|
|
6257
|
+
console.log(chalk15.dim(` ${getExplorerUrl(hash)}`));
|
|
5851
6258
|
} catch (err) {
|
|
5852
6259
|
spinner.fail("Approval failed");
|
|
5853
|
-
console.error(
|
|
6260
|
+
console.error(chalk15.red(formatContractError(err)));
|
|
5854
6261
|
process.exit(1);
|
|
5855
6262
|
}
|
|
5856
6263
|
});
|
|
@@ -5861,10 +6268,10 @@ syndicate.command("remove-depositor").description("Remove an address from the de
|
|
|
5861
6268
|
const depositor = validateAddress(opts.depositor, "depositor");
|
|
5862
6269
|
const hash = await removeDepositor(depositor);
|
|
5863
6270
|
spinner.succeed(`Depositor removed: ${hash}`);
|
|
5864
|
-
console.log(
|
|
6271
|
+
console.log(chalk15.dim(` ${getExplorerUrl(hash)}`));
|
|
5865
6272
|
} catch (err) {
|
|
5866
6273
|
spinner.fail("Removal failed");
|
|
5867
|
-
console.error(
|
|
6274
|
+
console.error(chalk15.red(formatContractError(err)));
|
|
5868
6275
|
process.exit(1);
|
|
5869
6276
|
}
|
|
5870
6277
|
});
|
|
@@ -5905,7 +6312,7 @@ syndicate.command("add").description("Register an agent on a syndicate vault (cr
|
|
|
5905
6312
|
});
|
|
5906
6313
|
if (balance === 0n) {
|
|
5907
6314
|
spinner.fail("Agent wallet does not own an ERC-8004 identity NFT");
|
|
5908
|
-
console.error(
|
|
6315
|
+
console.error(chalk15.dim(" Mint one first: sherwood identity mint --name <name>"));
|
|
5909
6316
|
process.exit(1);
|
|
5910
6317
|
}
|
|
5911
6318
|
const tokenId = await client.readContract({
|
|
@@ -5921,13 +6328,13 @@ syndicate.command("add").description("Register an agent on a syndicate vault (cr
|
|
|
5921
6328
|
args: [agentWallet, 0n]
|
|
5922
6329
|
});
|
|
5923
6330
|
agentId = tokenId;
|
|
5924
|
-
console.log(
|
|
6331
|
+
console.log(chalk15.dim(` Resolved agent ID: #${agentId}`));
|
|
5925
6332
|
}
|
|
5926
6333
|
}
|
|
5927
6334
|
spinner.text = "Registering agent...";
|
|
5928
6335
|
const hash = await registerAgent(agentId, agentWallet);
|
|
5929
6336
|
spinner.succeed(`Agent registered: ${hash}`);
|
|
5930
|
-
console.log(
|
|
6337
|
+
console.log(chalk15.dim(` ${getExplorerUrl(hash)}`));
|
|
5931
6338
|
try {
|
|
5932
6339
|
const xmtp = await loadXmtp();
|
|
5933
6340
|
const xmtpClient = await xmtp.getXmtpClient();
|
|
@@ -5939,14 +6346,14 @@ syndicate.command("add").description("Register an agent on a syndicate vault (cr
|
|
|
5939
6346
|
syndicate: subdomain,
|
|
5940
6347
|
timestamp: Math.floor(Date.now() / 1e3)
|
|
5941
6348
|
});
|
|
5942
|
-
console.log(
|
|
6349
|
+
console.log(chalk15.dim(` Added to chat: ${subdomain}`));
|
|
5943
6350
|
} catch {
|
|
5944
|
-
console.warn(
|
|
5945
|
-
console.warn(
|
|
6351
|
+
console.warn(chalk15.yellow(" \u26A0 Could not add agent to chat group"));
|
|
6352
|
+
console.warn(chalk15.dim(` If no group exists, run: sherwood chat ${subdomain} init`));
|
|
5946
6353
|
}
|
|
5947
6354
|
} catch (err) {
|
|
5948
6355
|
spinner.fail("Registration failed");
|
|
5949
|
-
console.error(
|
|
6356
|
+
console.error(chalk15.red(formatContractError(err)));
|
|
5950
6357
|
process.exit(1);
|
|
5951
6358
|
}
|
|
5952
6359
|
});
|
|
@@ -5954,7 +6361,7 @@ syndicate.command("share").description("Print the shareable dashboard URL for yo
|
|
|
5954
6361
|
try {
|
|
5955
6362
|
const subdomain = opts.subdomain || getPrimarySyndicate(getChain().id)?.subdomain;
|
|
5956
6363
|
if (!subdomain) {
|
|
5957
|
-
console.error(
|
|
6364
|
+
console.error(chalk15.red(" No syndicate configured. Pass --subdomain <name> or run 'syndicate create/join' first."));
|
|
5958
6365
|
process.exit(1);
|
|
5959
6366
|
}
|
|
5960
6367
|
const syndicateInfo = await resolveSyndicate(subdomain);
|
|
@@ -5981,7 +6388,7 @@ syndicate.command("share").description("Print the shareable dashboard URL for yo
|
|
|
5981
6388
|
}
|
|
5982
6389
|
console.log();
|
|
5983
6390
|
} catch (err) {
|
|
5984
|
-
console.error(
|
|
6391
|
+
console.error(chalk15.red(` \u2716 ${formatContractError(err)}`));
|
|
5985
6392
|
process.exit(1);
|
|
5986
6393
|
}
|
|
5987
6394
|
});
|
|
@@ -6006,14 +6413,14 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
|
|
|
6006
6413
|
const hits = await searchSyndicateAcrossChains(subdomain);
|
|
6007
6414
|
spinner.fail(`Syndicate "${subdomain}" not found on ${getNetwork()}`);
|
|
6008
6415
|
if (hits.length === 0) {
|
|
6009
|
-
console.error(
|
|
6416
|
+
console.error(chalk15.red(` Not found on any supported chain.`));
|
|
6010
6417
|
} else if (hits.length === 1) {
|
|
6011
|
-
console.error(
|
|
6012
|
-
console.error(
|
|
6418
|
+
console.error(chalk15.yellow(` Found on chain: ${chalk15.bold(hits[0].network)}`));
|
|
6419
|
+
console.error(chalk15.dim(` Re-run: sherwood --chain ${hits[0].network} syndicate join --subdomain ${subdomain}`));
|
|
6013
6420
|
} else {
|
|
6014
6421
|
const chains = hits.map((h) => h.network).join(", ");
|
|
6015
|
-
console.error(
|
|
6016
|
-
console.error(
|
|
6422
|
+
console.error(chalk15.yellow(` Found on chains: ${chalk15.bold(chains)}`));
|
|
6423
|
+
console.error(chalk15.dim(` Specify which chain to join with --chain <network>.`));
|
|
6017
6424
|
}
|
|
6018
6425
|
process.exit(1);
|
|
6019
6426
|
}
|
|
@@ -6026,17 +6433,17 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
|
|
|
6026
6433
|
try {
|
|
6027
6434
|
const xmtp = await loadXmtp();
|
|
6028
6435
|
await xmtp.getXmtpClient();
|
|
6029
|
-
console.log(
|
|
6436
|
+
console.log(chalk15.dim(" XMTP identity ready"));
|
|
6030
6437
|
} catch {
|
|
6031
|
-
console.warn(
|
|
6438
|
+
console.warn(chalk15.yellow(" \u26A0 Could not initialize XMTP identity"));
|
|
6032
6439
|
}
|
|
6033
6440
|
try {
|
|
6034
6441
|
const cron = await loadCron();
|
|
6035
6442
|
const cronResult = cron.registerSyndicateCrons(subdomain, isTestnet(), getNotifyTo());
|
|
6036
6443
|
if (cronResult.isOpenClaw && cronResult.registered) {
|
|
6037
|
-
console.log(
|
|
6444
|
+
console.log(chalk15.green(" \u2713 Participation crons registered"));
|
|
6038
6445
|
} else if (!cronResult.isOpenClaw) {
|
|
6039
|
-
console.log(
|
|
6446
|
+
console.log(chalk15.dim(" Tip: Set up a scheduled process to run `sherwood session check " + subdomain + "` periodically"));
|
|
6040
6447
|
}
|
|
6041
6448
|
} catch {
|
|
6042
6449
|
}
|
|
@@ -6049,14 +6456,14 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
|
|
|
6049
6456
|
);
|
|
6050
6457
|
if (existingRequest) {
|
|
6051
6458
|
spinner.succeed("You already have a pending join request for this syndicate");
|
|
6052
|
-
console.log(
|
|
6053
|
-
console.log(
|
|
6459
|
+
console.log(chalk15.dim(` Attestation: ${existingRequest.uid}`));
|
|
6460
|
+
console.log(chalk15.dim(` Submitted: ${new Date(existingRequest.time * 1e3).toLocaleString()}`));
|
|
6054
6461
|
try {
|
|
6055
6462
|
const xmtp = await loadXmtp();
|
|
6056
6463
|
await xmtp.getXmtpClient();
|
|
6057
|
-
console.log(
|
|
6464
|
+
console.log(chalk15.dim(" XMTP identity ready"));
|
|
6058
6465
|
} catch {
|
|
6059
|
-
console.warn(
|
|
6466
|
+
console.warn(chalk15.yellow(" \u26A0 Could not initialize XMTP identity"));
|
|
6060
6467
|
}
|
|
6061
6468
|
return;
|
|
6062
6469
|
}
|
|
@@ -6077,7 +6484,7 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
|
|
|
6077
6484
|
spinner.succeed("Join request created (XMTP identity ready)");
|
|
6078
6485
|
} catch {
|
|
6079
6486
|
spinner.succeed("Join request created");
|
|
6080
|
-
console.warn(
|
|
6487
|
+
console.warn(chalk15.yellow(" \u26A0 Could not initialize XMTP identity \u2014 creator may not be able to auto-add you to chat"));
|
|
6081
6488
|
}
|
|
6082
6489
|
addSyndicate(getChain().id, { subdomain, vault: syndicate2.vault, role: "agent" });
|
|
6083
6490
|
setPrimarySyndicate(getChain().id, subdomain);
|
|
@@ -6107,7 +6514,7 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
|
|
|
6107
6514
|
console.log();
|
|
6108
6515
|
} catch (err) {
|
|
6109
6516
|
spinner.fail("Join request failed");
|
|
6110
|
-
console.error(
|
|
6517
|
+
console.error(chalk15.red(formatContractError(err)));
|
|
6111
6518
|
process.exit(1);
|
|
6112
6519
|
}
|
|
6113
6520
|
});
|
|
@@ -6175,7 +6582,7 @@ syndicate.command("requests").description("View pending join requests for a synd
|
|
|
6175
6582
|
console.log();
|
|
6176
6583
|
} catch (err) {
|
|
6177
6584
|
spinner.fail("Failed to load requests");
|
|
6178
|
-
console.error(
|
|
6585
|
+
console.error(chalk15.red(formatContractError(err)));
|
|
6179
6586
|
process.exit(1);
|
|
6180
6587
|
}
|
|
6181
6588
|
});
|
|
@@ -6250,8 +6657,8 @@ syndicate.command("approve").description("Approve an agent join request (registe
|
|
|
6250
6657
|
});
|
|
6251
6658
|
console.log(DIM6(` Added to chat: ${subdomain}`));
|
|
6252
6659
|
} catch {
|
|
6253
|
-
console.warn(
|
|
6254
|
-
console.warn(
|
|
6660
|
+
console.warn(chalk15.yellow(" \u26A0 Could not add agent to chat group"));
|
|
6661
|
+
console.warn(chalk15.dim(` If no group exists, run: sherwood chat ${subdomain} init`));
|
|
6255
6662
|
}
|
|
6256
6663
|
spinner.succeed("Agent approved and registered");
|
|
6257
6664
|
console.log();
|
|
@@ -6264,7 +6671,7 @@ syndicate.command("approve").description("Approve an agent join request (registe
|
|
|
6264
6671
|
SEP6();
|
|
6265
6672
|
} catch (err) {
|
|
6266
6673
|
spinner.fail("Approval failed");
|
|
6267
|
-
console.error(
|
|
6674
|
+
console.error(chalk15.red(formatContractError(err)));
|
|
6268
6675
|
process.exit(1);
|
|
6269
6676
|
}
|
|
6270
6677
|
});
|
|
@@ -6276,14 +6683,14 @@ syndicate.command("reject").description("Reject a join request by revoking its a
|
|
|
6276
6683
|
console.log(DIM6(` ${getEasExplorerUrl(hash)}`));
|
|
6277
6684
|
} catch (err) {
|
|
6278
6685
|
spinner.fail("Rejection failed");
|
|
6279
|
-
console.error(
|
|
6686
|
+
console.error(chalk15.red(formatContractError(err)));
|
|
6280
6687
|
process.exit(1);
|
|
6281
6688
|
}
|
|
6282
6689
|
});
|
|
6283
6690
|
syndicate.command("leave").description("Leave a syndicate \u2014 removes participation crons and session state").option("--subdomain <name>", "Syndicate subdomain to leave").action(async (opts) => {
|
|
6284
6691
|
const subdomain = opts.subdomain || getPrimarySyndicate(getChain().id)?.subdomain;
|
|
6285
6692
|
if (!subdomain) {
|
|
6286
|
-
console.error(
|
|
6693
|
+
console.error(chalk15.red(" No syndicate configured. Pass --subdomain <name>."));
|
|
6287
6694
|
process.exit(1);
|
|
6288
6695
|
}
|
|
6289
6696
|
const spinner = ora8("Cleaning up...").start();
|
|
@@ -6303,13 +6710,13 @@ syndicate.command("leave").description("Leave a syndicate \u2014 removes partici
|
|
|
6303
6710
|
}
|
|
6304
6711
|
console.log(G6(" \u2713 Session state cleared"));
|
|
6305
6712
|
console.log();
|
|
6306
|
-
console.log(
|
|
6307
|
-
console.log(
|
|
6308
|
-
console.log(
|
|
6713
|
+
console.log(chalk15.dim(" Note: This does not remove you on-chain. To exit your position:"));
|
|
6714
|
+
console.log(chalk15.dim(" sherwood vault balance \u2014 check your LP share balance"));
|
|
6715
|
+
console.log(chalk15.dim(" Redeem shares via the vault contract or dashboard"));
|
|
6309
6716
|
console.log();
|
|
6310
6717
|
} catch (err) {
|
|
6311
6718
|
spinner.fail("Leave failed");
|
|
6312
|
-
console.error(
|
|
6719
|
+
console.error(chalk15.red(err instanceof Error ? err.message : String(err)));
|
|
6313
6720
|
process.exit(1);
|
|
6314
6721
|
}
|
|
6315
6722
|
});
|
|
@@ -6360,7 +6767,7 @@ syndicate.command("set-primary").description("Set the active syndicate for CLI c
|
|
|
6360
6767
|
}
|
|
6361
6768
|
}
|
|
6362
6769
|
if (!target) {
|
|
6363
|
-
console.error(
|
|
6770
|
+
console.error(chalk15.red(" Could not resolve syndicate. Pass --subdomain or --vault."));
|
|
6364
6771
|
process.exit(1);
|
|
6365
6772
|
}
|
|
6366
6773
|
setPrimarySyndicate(chainId, target);
|
|
@@ -6368,7 +6775,7 @@ syndicate.command("set-primary").description("Set the active syndicate for CLI c
|
|
|
6368
6775
|
\u2713 Primary syndicate set to ${target}.sherwoodagent.eth
|
|
6369
6776
|
`));
|
|
6370
6777
|
} catch (err) {
|
|
6371
|
-
console.error(
|
|
6778
|
+
console.error(chalk15.red(formatContractError(err)));
|
|
6372
6779
|
process.exit(1);
|
|
6373
6780
|
}
|
|
6374
6781
|
});
|
|
@@ -6376,13 +6783,13 @@ var vaultCmd = program.command("vault");
|
|
|
6376
6783
|
vaultCmd.command("deposit").description("Deposit into a vault").option("--vault <address>", "Vault address (default: from config)").requiredOption("--amount <amount>", "Amount to deposit (in asset units)").option("--use-eth", "Auto-wrap ETH \u2192 WETH before depositing (for WETH vaults)").action(async (opts) => {
|
|
6377
6784
|
resolveVault(opts);
|
|
6378
6785
|
const decimals = await getAssetDecimals();
|
|
6379
|
-
const amount =
|
|
6786
|
+
const amount = parseUnits7(opts.amount, decimals);
|
|
6380
6787
|
try {
|
|
6381
6788
|
if (!opts.useEth) {
|
|
6382
6789
|
await preflightDeposit(amount);
|
|
6383
6790
|
}
|
|
6384
6791
|
} catch (err) {
|
|
6385
|
-
console.error(
|
|
6792
|
+
console.error(chalk15.red(`
|
|
6386
6793
|
\u2716 ${err instanceof Error ? err.message : String(err)}
|
|
6387
6794
|
`));
|
|
6388
6795
|
process.exit(1);
|
|
@@ -6397,10 +6804,10 @@ vaultCmd.command("deposit").description("Deposit into a vault").option("--vault
|
|
|
6397
6804
|
hash = await deposit(amount);
|
|
6398
6805
|
}
|
|
6399
6806
|
spinner.succeed(`Deposited: ${hash}`);
|
|
6400
|
-
console.log(
|
|
6807
|
+
console.log(chalk15.dim(` ${getExplorerUrl(hash)}`));
|
|
6401
6808
|
} catch (err) {
|
|
6402
6809
|
spinner.fail("Deposit failed");
|
|
6403
|
-
console.error(
|
|
6810
|
+
console.error(chalk15.red(formatContractError(err)));
|
|
6404
6811
|
process.exit(1);
|
|
6405
6812
|
}
|
|
6406
6813
|
});
|
|
@@ -6411,8 +6818,8 @@ vaultCmd.command("info").description("Display vault state").option("--vault <add
|
|
|
6411
6818
|
const info = await getVaultInfo();
|
|
6412
6819
|
spinner.stop();
|
|
6413
6820
|
console.log();
|
|
6414
|
-
console.log(
|
|
6415
|
-
console.log(
|
|
6821
|
+
console.log(chalk15.bold("Vault Info"));
|
|
6822
|
+
console.log(chalk15.dim("\u2500".repeat(40)));
|
|
6416
6823
|
console.log(` Address: ${info.address}`);
|
|
6417
6824
|
console.log(` Total Assets: ${info.totalAssets}`);
|
|
6418
6825
|
console.log(` Agent Count: ${info.agentCount}`);
|
|
@@ -6421,7 +6828,7 @@ vaultCmd.command("info").description("Display vault state").option("--vault <add
|
|
|
6421
6828
|
console.log();
|
|
6422
6829
|
} catch (err) {
|
|
6423
6830
|
spinner.fail("Failed to load vault info");
|
|
6424
|
-
console.error(
|
|
6831
|
+
console.error(chalk15.red(formatContractError(err)));
|
|
6425
6832
|
process.exit(1);
|
|
6426
6833
|
}
|
|
6427
6834
|
});
|
|
@@ -6432,15 +6839,15 @@ vaultCmd.command("balance").description("Show LP share balance and asset value")
|
|
|
6432
6839
|
const balance = await getBalance(opts.address);
|
|
6433
6840
|
spinner.stop();
|
|
6434
6841
|
console.log();
|
|
6435
|
-
console.log(
|
|
6436
|
-
console.log(
|
|
6842
|
+
console.log(chalk15.bold("LP Position"));
|
|
6843
|
+
console.log(chalk15.dim("\u2500".repeat(40)));
|
|
6437
6844
|
console.log(` Shares: ${balance.shares.toString()}`);
|
|
6438
6845
|
console.log(` Asset Value: ${balance.assetsValue}`);
|
|
6439
6846
|
console.log(` % of Vault: ${balance.percentOfVault}`);
|
|
6440
6847
|
console.log();
|
|
6441
6848
|
} catch (err) {
|
|
6442
6849
|
spinner.fail("Failed to load balance");
|
|
6443
|
-
console.error(
|
|
6850
|
+
console.error(chalk15.red(formatContractError(err)));
|
|
6444
6851
|
process.exit(1);
|
|
6445
6852
|
}
|
|
6446
6853
|
});
|
|
@@ -6451,22 +6858,22 @@ vaultCmd.command("redeem").description("Redeem vault shares for the underlying a
|
|
|
6451
6858
|
let shares;
|
|
6452
6859
|
try {
|
|
6453
6860
|
if (opts.shares) {
|
|
6454
|
-
shares =
|
|
6861
|
+
shares = parseUnits7(opts.shares, shareDecimals);
|
|
6455
6862
|
} else {
|
|
6456
6863
|
shares = await getShareBalance();
|
|
6457
6864
|
if (shares === 0n) {
|
|
6458
|
-
console.error(
|
|
6865
|
+
console.error(chalk15.red("\n \u2716 No shares to redeem.\n"));
|
|
6459
6866
|
process.exit(1);
|
|
6460
6867
|
}
|
|
6461
6868
|
}
|
|
6462
6869
|
} catch (err) {
|
|
6463
|
-
console.error(
|
|
6870
|
+
console.error(chalk15.red(`
|
|
6464
6871
|
\u2716 ${err instanceof Error ? err.message : String(err)}
|
|
6465
6872
|
`));
|
|
6466
6873
|
process.exit(1);
|
|
6467
6874
|
}
|
|
6468
6875
|
if (opts.receiver && !isAddress6(opts.receiver)) {
|
|
6469
|
-
console.error(
|
|
6876
|
+
console.error(chalk15.red(`
|
|
6470
6877
|
\u2716 Invalid receiver address: ${opts.receiver}
|
|
6471
6878
|
`));
|
|
6472
6879
|
process.exit(1);
|
|
@@ -6474,7 +6881,7 @@ vaultCmd.command("redeem").description("Redeem vault shares for the underlying a
|
|
|
6474
6881
|
try {
|
|
6475
6882
|
await preflightRedeem(shares);
|
|
6476
6883
|
} catch (err) {
|
|
6477
|
-
console.error(
|
|
6884
|
+
console.error(chalk15.red(`
|
|
6478
6885
|
\u2716 ${err instanceof Error ? err.message : String(err)}
|
|
6479
6886
|
`));
|
|
6480
6887
|
process.exit(1);
|
|
@@ -6487,11 +6894,11 @@ vaultCmd.command("redeem").description("Redeem vault shares for the underlying a
|
|
|
6487
6894
|
opts.receiver
|
|
6488
6895
|
);
|
|
6489
6896
|
spinner.succeed(`Redeemed: ${hash}`);
|
|
6490
|
-
console.log(
|
|
6897
|
+
console.log(chalk15.dim(` ${getExplorerUrl(hash)}`));
|
|
6491
6898
|
console.log(` Assets received: ${formatUnits5(assetsOut, assetDecimals)}`);
|
|
6492
6899
|
} catch (err) {
|
|
6493
6900
|
spinner.fail("Redeem failed");
|
|
6494
|
-
console.error(
|
|
6901
|
+
console.error(chalk15.red(formatContractError(err)));
|
|
6495
6902
|
process.exit(1);
|
|
6496
6903
|
}
|
|
6497
6904
|
});
|
|
@@ -6513,9 +6920,9 @@ try {
|
|
|
6513
6920
|
registerChatCommands(program);
|
|
6514
6921
|
} catch {
|
|
6515
6922
|
program.command("chat <name> [action] [actionArgs...]").description("Syndicate chat (XMTP) \u2014 requires @xmtp/cli").action(() => {
|
|
6516
|
-
console.error(
|
|
6517
|
-
console.error(
|
|
6518
|
-
console.error(
|
|
6923
|
+
console.error(chalk15.red("XMTP CLI not available."));
|
|
6924
|
+
console.error(chalk15.dim("Install with: npm install -g @xmtp/cli"));
|
|
6925
|
+
console.error(chalk15.dim("Or reinstall: npm i -g @sherwoodagent/cli"));
|
|
6519
6926
|
process.exit(1);
|
|
6520
6927
|
});
|
|
6521
6928
|
}
|
|
@@ -6529,7 +6936,7 @@ registerGovernorCommands(program);
|
|
|
6529
6936
|
registerGuardianCommands(program);
|
|
6530
6937
|
var { registerResearchCommands } = await import("./research-QBKISW2M.js");
|
|
6531
6938
|
registerResearchCommands(program);
|
|
6532
|
-
var { registerAgentCommands } = await import("./agent-
|
|
6939
|
+
var { registerAgentCommands } = await import("./agent-ZPAZTJG3.js");
|
|
6533
6940
|
registerAgentCommands(program);
|
|
6534
6941
|
var { registerTradeCommands } = await import("./trade-NO6CNWCA.js");
|
|
6535
6942
|
registerTradeCommands(program);
|
|
@@ -6540,58 +6947,58 @@ configCmd.command("set").description("Save settings to ~/.sherwood/config.json (
|
|
|
6540
6947
|
if (opts.privateKey) {
|
|
6541
6948
|
setPrivateKey(opts.privateKey);
|
|
6542
6949
|
const account = getAccount();
|
|
6543
|
-
console.log(
|
|
6544
|
-
console.log(
|
|
6950
|
+
console.log(chalk15.green("Private key saved to ~/.sherwood/config.json"));
|
|
6951
|
+
console.log(chalk15.dim(` Wallet: ${account.address}`));
|
|
6545
6952
|
saved = true;
|
|
6546
6953
|
}
|
|
6547
6954
|
if (opts.vault) {
|
|
6548
6955
|
const chainId = getChain().id;
|
|
6549
6956
|
setChainContract(chainId, "vault", opts.vault);
|
|
6550
|
-
console.log(
|
|
6551
|
-
console.log(
|
|
6957
|
+
console.log(chalk15.green(`Vault saved to ~/.sherwood/config.json (chain ${chainId})`));
|
|
6958
|
+
console.log(chalk15.dim(` Vault: ${opts.vault}`));
|
|
6552
6959
|
saved = true;
|
|
6553
6960
|
}
|
|
6554
6961
|
if (opts.rpc) {
|
|
6555
6962
|
const network = getNetwork();
|
|
6556
6963
|
setConfigRpcUrl(network, opts.rpc);
|
|
6557
|
-
console.log(
|
|
6558
|
-
console.log(
|
|
6964
|
+
console.log(chalk15.green(`RPC URL saved for ${network}`));
|
|
6965
|
+
console.log(chalk15.dim(` RPC: ${opts.rpc}`));
|
|
6559
6966
|
saved = true;
|
|
6560
6967
|
}
|
|
6561
6968
|
if (opts.notifyTo) {
|
|
6562
6969
|
setNotifyTo(opts.notifyTo);
|
|
6563
|
-
console.log(
|
|
6564
|
-
console.log(
|
|
6970
|
+
console.log(chalk15.green("Notify destination saved to ~/.sherwood/config.json"));
|
|
6971
|
+
console.log(chalk15.dim(` Notify to: ${opts.notifyTo}`));
|
|
6565
6972
|
saved = true;
|
|
6566
6973
|
}
|
|
6567
6974
|
if (opts.uniswapApiKey) {
|
|
6568
6975
|
setUniswapApiKey(opts.uniswapApiKey);
|
|
6569
|
-
console.log(
|
|
6976
|
+
console.log(chalk15.green("Uniswap API key saved to ~/.sherwood/config.json"));
|
|
6570
6977
|
saved = true;
|
|
6571
6978
|
}
|
|
6572
6979
|
if (opts.veniceApiKey) {
|
|
6573
6980
|
setVeniceApiKey(opts.veniceApiKey);
|
|
6574
|
-
console.log(
|
|
6981
|
+
console.log(chalk15.green("Venice API key saved to ~/.sherwood/config.json"));
|
|
6575
6982
|
saved = true;
|
|
6576
6983
|
}
|
|
6577
6984
|
if (opts.anthropicApiKey) {
|
|
6578
6985
|
setAnthropicApiKey(opts.anthropicApiKey);
|
|
6579
|
-
console.log(
|
|
6986
|
+
console.log(chalk15.green("Anthropic API key saved to ~/.sherwood/config.json"));
|
|
6580
6987
|
saved = true;
|
|
6581
6988
|
}
|
|
6582
6989
|
if (opts.xmtpGroup) {
|
|
6583
6990
|
const parts = opts.xmtpGroup.split(":");
|
|
6584
6991
|
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
|
6585
|
-
console.log(
|
|
6992
|
+
console.log(chalk15.red("Format: --xmtp-group <subdomain>:<groupId>"));
|
|
6586
6993
|
process.exit(1);
|
|
6587
6994
|
}
|
|
6588
6995
|
cacheGroupId(parts[0], parts[1]);
|
|
6589
|
-
console.log(
|
|
6590
|
-
console.log(
|
|
6996
|
+
console.log(chalk15.green(`XMTP group ID cached for ${parts[0]}`));
|
|
6997
|
+
console.log(chalk15.dim(` Group ID: ${parts[1]}`));
|
|
6591
6998
|
saved = true;
|
|
6592
6999
|
}
|
|
6593
7000
|
if (!saved) {
|
|
6594
|
-
console.log(
|
|
7001
|
+
console.log(chalk15.red("Provide at least one of: --private-key, --vault, --rpc, --notify-to, --uniswap-api-key, --venice-api-key, --anthropic-api-key, --xmtp-group"));
|
|
6595
7002
|
process.exit(1);
|
|
6596
7003
|
}
|
|
6597
7004
|
});
|
|
@@ -6602,25 +7009,25 @@ configCmd.command("show").description("Display current config for the active net
|
|
|
6602
7009
|
const config = loadConfig();
|
|
6603
7010
|
const customRpc = config.rpc?.[network];
|
|
6604
7011
|
console.log();
|
|
6605
|
-
console.log(
|
|
6606
|
-
console.log(
|
|
6607
|
-
console.log(` Network: ${
|
|
6608
|
-
console.log(` RPC: ${customRpc ?
|
|
6609
|
-
console.log(` Wallet: ${config.privateKey ?
|
|
6610
|
-
console.log(` Agent ID: ${config.agentId ??
|
|
6611
|
-
console.log(` Vault: ${contracts.vault ??
|
|
6612
|
-
console.log(` Uniswap: ${getUniswapApiKey() ?
|
|
6613
|
-
console.log(` Venice: ${getVeniceApiKey() ?
|
|
7012
|
+
console.log(chalk15.bold(`Sherwood Config`));
|
|
7013
|
+
console.log(chalk15.dim("\u2500".repeat(50)));
|
|
7014
|
+
console.log(` Network: ${chalk15.cyan(network)} (chain ${chainId})`);
|
|
7015
|
+
console.log(` RPC: ${customRpc ? chalk15.green(customRpc) : chalk15.dim("default")}`);
|
|
7016
|
+
console.log(` Wallet: ${config.privateKey ? chalk15.green("configured") : chalk15.dim("not set")}`);
|
|
7017
|
+
console.log(` Agent ID: ${config.agentId ?? chalk15.dim("not set")}`);
|
|
7018
|
+
console.log(` Vault: ${contracts.vault ?? chalk15.dim("not set")}`);
|
|
7019
|
+
console.log(` Uniswap: ${getUniswapApiKey() ? chalk15.green("API key configured") : chalk15.dim("not set")}`);
|
|
7020
|
+
console.log(` Venice: ${getVeniceApiKey() ? chalk15.green("API key configured") : chalk15.dim("not set")}`);
|
|
6614
7021
|
const groupEntries = Object.entries(config.groupCache || {}).filter(([, v]) => v);
|
|
6615
7022
|
if (groupEntries.length > 0) {
|
|
6616
7023
|
console.log();
|
|
6617
|
-
console.log(
|
|
7024
|
+
console.log(chalk15.bold(" XMTP Groups"));
|
|
6618
7025
|
for (const [sub, gid] of groupEntries) {
|
|
6619
|
-
console.log(` ${sub}: ${
|
|
7026
|
+
console.log(` ${sub}: ${chalk15.cyan(gid)}`);
|
|
6620
7027
|
}
|
|
6621
7028
|
}
|
|
6622
7029
|
console.log();
|
|
6623
|
-
console.log(
|
|
7030
|
+
console.log(chalk15.dim(" Config file: ~/.sherwood/config.json"));
|
|
6624
7031
|
console.log();
|
|
6625
7032
|
});
|
|
6626
7033
|
program.parse();
|