@sherwoodagent/cli 0.65.1 → 0.72.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/{abis-4HWN2E3H.js → abis-KP7M4IPM.js} +4 -2
- package/dist/addresses-HJDYFLWZ.js +45 -0
- package/dist/{agent-AETROOEW.js → agent-6OJKQVEG.js} +10 -10
- package/dist/{agent-AETROOEW.js.map → agent-6OJKQVEG.js.map} +1 -1
- package/dist/{chat-SL4O7Y3C.js → chat-2DKD3QPA.js} +37 -36
- package/dist/chat-2DKD3QPA.js.map +1 -0
- package/dist/{chunk-7INQASHO.js → chunk-3YUMQGJD.js} +109 -129
- package/dist/chunk-3YUMQGJD.js.map +1 -0
- package/dist/{chunk-W2ZUPR3W.js → chunk-5DWB5Y7G.js} +9 -9
- package/dist/chunk-5DWB5Y7G.js.map +1 -0
- package/dist/{chunk-D4AQBGF4.js → chunk-6SRIVGAV.js} +23 -18
- package/dist/chunk-6SRIVGAV.js.map +1 -0
- package/dist/{chunk-HE3NY4JJ.js → chunk-ASF2WTSW.js} +6 -6
- package/dist/chunk-ASF2WTSW.js.map +1 -0
- package/dist/{chunk-YZHZSBES.js → chunk-BQMOE4FJ.js} +144 -37
- package/dist/chunk-BQMOE4FJ.js.map +1 -0
- package/dist/{chunk-2W4KGGPU.js → chunk-GSC7PVEO.js} +27 -27
- package/dist/chunk-GSC7PVEO.js.map +1 -0
- package/dist/chunk-GT3KCKF6.js +124 -0
- package/dist/chunk-GT3KCKF6.js.map +1 -0
- package/dist/{chunk-RO26TXXE.js → chunk-ITZT5HD4.js} +2 -2
- package/dist/{chunk-UBAX22KA.js → chunk-OZLX666C.js} +4 -4
- package/dist/chunk-OZLX666C.js.map +1 -0
- package/dist/{chunk-OKGTNYYW.js → chunk-PDUGL4EY.js} +106 -128
- package/dist/chunk-PDUGL4EY.js.map +1 -0
- package/dist/chunk-RNASYOQT.js +111 -0
- package/dist/chunk-RNASYOQT.js.map +1 -0
- package/dist/{chunk-AQNHSYJ7.js → chunk-SVSJNZ5D.js} +6 -6
- package/dist/{chunk-YJSCIQUZ.js → chunk-UC5RMCB3.js} +6 -6
- package/dist/chunk-UC5RMCB3.js.map +1 -0
- package/dist/chunk-YLYANZMC.js +308 -0
- package/dist/chunk-YLYANZMC.js.map +1 -0
- package/dist/{chunk-2H6IGGKN.js → chunk-ZYXFKVYF.js} +26 -13
- package/dist/chunk-ZYXFKVYF.js.map +1 -0
- package/dist/{client-KBBFAMEI.js → client-K4JCNPVV.js} +4 -4
- package/dist/{config-REASKDIK.js → config-FAQO37S7.js} +10 -10
- package/dist/{cron-EOULYO3R.js → cron-QMLRWDJG.js} +19 -17
- package/dist/cron-QMLRWDJG.js.map +1 -0
- package/dist/{eas-5VIHTS3N.js → eas-ZXGUNYC5.js} +7 -7
- package/dist/{governor-UBUNIO3U.js → governor-CV5PITE3.js} +13 -13
- package/dist/index.js +1854 -1078
- package/dist/index.js.map +1 -1
- package/dist/{ipfs-IGXLLJCF.js → ipfs-VGWTUORE.js} +2 -2
- package/dist/{network-3ZU7UBDU.js → network-DHWEYXP2.js} +7 -7
- package/dist/{price-3BXZKK6B.js → price-GOX6P4NT.js} +6 -6
- package/dist/{research-OGMEJBX3.js → research-6D7NNJX4.js} +22 -22
- package/dist/research-6D7NNJX4.js.map +1 -0
- package/dist/research-EFNVHU7G.js +14 -0
- package/dist/{session-FPQGAMSS.js → session-LDLW66K3.js} +28 -28
- package/dist/session-LDLW66K3.js.map +1 -0
- package/dist/{session-XAW5UIXP.js → session-XBQY7SBJ.js} +2 -2
- package/dist/{trade-5KIGBEOP.js → trade-7NNZXP5B.js} +34 -34
- package/dist/trade-7NNZXP5B.js.map +1 -0
- package/dist/vault-SIL4QL5U.js +54 -0
- package/dist/vault-SIL4QL5U.js.map +1 -0
- package/dist/{xmtp-MR6OJ76L.js → xmtp-QNOVDLXX.js} +13 -12
- package/dist/xmtp-QNOVDLXX.js.map +1 -0
- package/package.json +4 -3
- package/dist/addresses-FXTJXDI7.js +0 -35
- package/dist/chat-SL4O7Y3C.js.map +0 -1
- package/dist/chunk-2H6IGGKN.js.map +0 -1
- package/dist/chunk-2LRC7I33.js +0 -186
- package/dist/chunk-2LRC7I33.js.map +0 -1
- package/dist/chunk-2W4KGGPU.js.map +0 -1
- package/dist/chunk-7INQASHO.js.map +0 -1
- package/dist/chunk-D4AQBGF4.js.map +0 -1
- package/dist/chunk-HE3NY4JJ.js.map +0 -1
- package/dist/chunk-OKGTNYYW.js.map +0 -1
- package/dist/chunk-UBAX22KA.js.map +0 -1
- package/dist/chunk-VAOCUQMY.js +0 -489
- package/dist/chunk-VAOCUQMY.js.map +0 -1
- package/dist/chunk-W2ZUPR3W.js.map +0 -1
- package/dist/chunk-YJSCIQUZ.js.map +0 -1
- package/dist/chunk-YZHZSBES.js.map +0 -1
- package/dist/cron-EOULYO3R.js.map +0 -1
- package/dist/research-OGMEJBX3.js.map +0 -1
- package/dist/research-XEXTHDT3.js +0 -14
- package/dist/session-FPQGAMSS.js.map +0 -1
- package/dist/trade-5KIGBEOP.js.map +0 -1
- package/dist/xmtp-MR6OJ76L.js.map +0 -1
- /package/dist/{abis-4HWN2E3H.js.map → abis-KP7M4IPM.js.map} +0 -0
- /package/dist/{addresses-FXTJXDI7.js.map → addresses-HJDYFLWZ.js.map} +0 -0
- /package/dist/{chunk-RO26TXXE.js.map → chunk-ITZT5HD4.js.map} +0 -0
- /package/dist/{chunk-AQNHSYJ7.js.map → chunk-SVSJNZ5D.js.map} +0 -0
- /package/dist/{client-KBBFAMEI.js.map → client-K4JCNPVV.js.map} +0 -0
- /package/dist/{config-REASKDIK.js.map → config-FAQO37S7.js.map} +0 -0
- /package/dist/{eas-5VIHTS3N.js.map → eas-ZXGUNYC5.js.map} +0 -0
- /package/dist/{governor-UBUNIO3U.js.map → governor-CV5PITE3.js.map} +0 -0
- /package/dist/{ipfs-IGXLLJCF.js.map → ipfs-VGWTUORE.js.map} +0 -0
- /package/dist/{network-3ZU7UBDU.js.map → network-DHWEYXP2.js.map} +0 -0
- /package/dist/{price-3BXZKK6B.js.map → price-GOX6P4NT.js.map} +0 -0
- /package/dist/{research-XEXTHDT3.js.map → research-EFNVHU7G.js.map} +0 -0
- /package/dist/{session-XAW5UIXP.js.map → session-XBQY7SBJ.js.map} +0 -0
|
@@ -1,35 +1,36 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
getFund,
|
|
3
3
|
updateMetadata
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-6SRIVGAV.js";
|
|
5
5
|
import {
|
|
6
6
|
getTextRecord,
|
|
7
|
-
|
|
7
|
+
resolveFund,
|
|
8
8
|
setTextRecord
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-GT3KCKF6.js";
|
|
10
|
+
import "./chunk-3YUMQGJD.js";
|
|
10
11
|
import {
|
|
11
12
|
ENS
|
|
12
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-YLYANZMC.js";
|
|
13
14
|
import {
|
|
14
15
|
formatContractError,
|
|
15
16
|
getAccount
|
|
16
|
-
} from "./chunk-
|
|
17
|
-
import "./chunk-
|
|
17
|
+
} from "./chunk-UC5RMCB3.js";
|
|
18
|
+
import "./chunk-RNASYOQT.js";
|
|
18
19
|
import {
|
|
19
20
|
cacheGroupId,
|
|
20
21
|
getCachedGroupId
|
|
21
|
-
} from "./chunk-
|
|
22
|
-
import "./chunk-
|
|
22
|
+
} from "./chunk-GSC7PVEO.js";
|
|
23
|
+
import "./chunk-BQMOE4FJ.js";
|
|
23
24
|
import {
|
|
24
25
|
fetchMetadata,
|
|
25
26
|
uploadMetadata
|
|
26
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-ZYXFKVYF.js";
|
|
27
28
|
|
|
28
29
|
// src/commands/chat.ts
|
|
29
30
|
import chalk from "chalk";
|
|
30
31
|
import ora from "ora";
|
|
31
32
|
async function loadXmtp() {
|
|
32
|
-
return import("./xmtp-
|
|
33
|
+
return import("./xmtp-QNOVDLXX.js");
|
|
33
34
|
}
|
|
34
35
|
function formatTimestamp(date) {
|
|
35
36
|
return `${date.getHours().toString().padStart(2, "0")}:${date.getMinutes().toString().padStart(2, "0")}`;
|
|
@@ -99,7 +100,7 @@ ${envelope.text || ""}`;
|
|
|
99
100
|
async function handleStream(name) {
|
|
100
101
|
const spinner = ora("Connecting to chat...").start();
|
|
101
102
|
try {
|
|
102
|
-
await
|
|
103
|
+
await resolveFund(name);
|
|
103
104
|
const xmtp = await loadXmtp();
|
|
104
105
|
const client = await xmtp.getXmtpClient();
|
|
105
106
|
const group = await xmtp.getGroup(client, name);
|
|
@@ -220,7 +221,7 @@ async function handleAdd(name, address) {
|
|
|
220
221
|
await xmtp.sendEnvelope(group, {
|
|
221
222
|
type: "MEMBER_JOIN",
|
|
222
223
|
from: address,
|
|
223
|
-
|
|
224
|
+
fund: name,
|
|
224
225
|
timestamp: Math.floor(Date.now() / 1e3)
|
|
225
226
|
});
|
|
226
227
|
spinner.succeed(`Member added: ${address}`);
|
|
@@ -291,12 +292,12 @@ async function handleRename(name, newName, newDescription) {
|
|
|
291
292
|
}
|
|
292
293
|
}
|
|
293
294
|
async function handlePublishMetadata(name, groupIdOpt) {
|
|
294
|
-
const spinner = ora("Updating
|
|
295
|
+
const spinner = ora("Updating fund metadata...").start();
|
|
295
296
|
try {
|
|
296
|
-
const
|
|
297
|
+
const fund = await resolveFund(name);
|
|
297
298
|
const callerAddress = getAccount().address.toLowerCase();
|
|
298
|
-
if (
|
|
299
|
-
spinner.fail("Only the
|
|
299
|
+
if (fund.creator.toLowerCase() !== callerAddress) {
|
|
300
|
+
spinner.fail("Only the fund creator can update metadata");
|
|
300
301
|
process.exit(1);
|
|
301
302
|
}
|
|
302
303
|
const xmtp = await loadXmtp();
|
|
@@ -314,13 +315,13 @@ async function handlePublishMetadata(name, groupIdOpt) {
|
|
|
314
315
|
process.exit(1);
|
|
315
316
|
}
|
|
316
317
|
spinner.text = "Fetching current metadata...";
|
|
317
|
-
const info = await
|
|
318
|
+
const info = await getFund(fund.id);
|
|
318
319
|
let metadata;
|
|
319
320
|
try {
|
|
320
321
|
metadata = await fetchMetadata(info.metadataURI);
|
|
321
322
|
} catch {
|
|
322
323
|
metadata = {
|
|
323
|
-
schema: "sherwood/
|
|
324
|
+
schema: "sherwood/fund/v1",
|
|
324
325
|
name,
|
|
325
326
|
description: "",
|
|
326
327
|
chain: "",
|
|
@@ -333,7 +334,7 @@ async function handlePublishMetadata(name, groupIdOpt) {
|
|
|
333
334
|
spinner.text = "Pinning updated metadata to IPFS...";
|
|
334
335
|
const newURI = await uploadMetadata(metadata);
|
|
335
336
|
spinner.text = "Calling factory.updateMetadata...";
|
|
336
|
-
await updateMetadata(
|
|
337
|
+
await updateMetadata(fund.id, newURI);
|
|
337
338
|
spinner.succeed(`Metadata updated \u2014 xmtpGroupId = ${groupId}`);
|
|
338
339
|
console.log(chalk.dim(` New URI: ${newURI}`));
|
|
339
340
|
console.log(
|
|
@@ -350,10 +351,10 @@ async function handlePublishMetadata(name, groupIdOpt) {
|
|
|
350
351
|
async function handlePublishEns(name, groupIdOpt) {
|
|
351
352
|
const spinner = ora("Publishing group ID to ENS...").start();
|
|
352
353
|
try {
|
|
353
|
-
const
|
|
354
|
+
const fund = await resolveFund(name);
|
|
354
355
|
const callerAddress = getAccount().address.toLowerCase();
|
|
355
|
-
if (
|
|
356
|
-
spinner.fail("Only the
|
|
356
|
+
if (fund.creator.toLowerCase() !== callerAddress) {
|
|
357
|
+
spinner.fail("Only the fund creator can write ENS records");
|
|
357
358
|
process.exit(1);
|
|
358
359
|
}
|
|
359
360
|
const xmtp = await loadXmtp();
|
|
@@ -370,7 +371,7 @@ async function handlePublishEns(name, groupIdOpt) {
|
|
|
370
371
|
);
|
|
371
372
|
process.exit(1);
|
|
372
373
|
}
|
|
373
|
-
await setTextRecord(name, "xmtpGroupId", groupId,
|
|
374
|
+
await setTextRecord(name, "xmtpGroupId", groupId, fund.vault);
|
|
374
375
|
spinner.succeed(`Published xmtpGroupId \u2192 ${groupId} on ENS`);
|
|
375
376
|
console.log(chalk.dim(" Dashboard will pick up the group on next page load."));
|
|
376
377
|
} catch (err) {
|
|
@@ -407,16 +408,16 @@ async function handlePublic(name, on) {
|
|
|
407
408
|
async function handleInit(name, force, isPublic) {
|
|
408
409
|
const spinner = ora("Initializing chat group...").start();
|
|
409
410
|
try {
|
|
410
|
-
const
|
|
411
|
+
const fund = await resolveFund(name);
|
|
411
412
|
const callerAddress = getAccount().address.toLowerCase();
|
|
412
|
-
if (
|
|
413
|
-
spinner.fail("Only the
|
|
413
|
+
if (fund.creator.toLowerCase() !== callerAddress) {
|
|
414
|
+
spinner.fail("Only the fund creator can initialize the chat group");
|
|
414
415
|
process.exit(1);
|
|
415
416
|
}
|
|
416
417
|
if (!force) {
|
|
417
418
|
const existingId = getCachedGroupId(name) || await getTextRecord(name, "xmtpGroupId");
|
|
418
419
|
if (existingId) {
|
|
419
|
-
spinner.succeed("XMTP group already exists for this
|
|
420
|
+
spinner.succeed("XMTP group already exists for this fund");
|
|
420
421
|
console.log(chalk.dim(` Group ID: ${existingId}`));
|
|
421
422
|
return;
|
|
422
423
|
}
|
|
@@ -424,17 +425,17 @@ async function handleInit(name, force, isPublic) {
|
|
|
424
425
|
spinner.text = "Creating XMTP group...";
|
|
425
426
|
const xmtp = await loadXmtp();
|
|
426
427
|
const client = await xmtp.getXmtpClient();
|
|
427
|
-
const groupId = await xmtp.
|
|
428
|
+
const groupId = await xmtp.createFundGroup(client, name, isPublic);
|
|
428
429
|
cacheGroupId(name, groupId);
|
|
429
|
-
spinner.text = "Saving group ID to
|
|
430
|
+
spinner.text = "Saving group ID to fund metadata...";
|
|
430
431
|
try {
|
|
431
|
-
const info = await
|
|
432
|
+
const info = await getFund(fund.id);
|
|
432
433
|
let metadata;
|
|
433
434
|
try {
|
|
434
435
|
metadata = await fetchMetadata(info.metadataURI);
|
|
435
436
|
} catch {
|
|
436
437
|
metadata = {
|
|
437
|
-
schema: "sherwood/
|
|
438
|
+
schema: "sherwood/fund/v1",
|
|
438
439
|
name,
|
|
439
440
|
description: "",
|
|
440
441
|
chain: "",
|
|
@@ -445,7 +446,7 @@ async function handleInit(name, force, isPublic) {
|
|
|
445
446
|
}
|
|
446
447
|
metadata.xmtpGroupId = groupId;
|
|
447
448
|
const newURI = await uploadMetadata(metadata);
|
|
448
|
-
await updateMetadata(
|
|
449
|
+
await updateMetadata(fund.id, newURI);
|
|
449
450
|
console.log(chalk.dim(`
|
|
450
451
|
Group ID saved to metadata: ${newURI}`));
|
|
451
452
|
} catch (metaErr) {
|
|
@@ -457,7 +458,7 @@ async function handleInit(name, force, isPublic) {
|
|
|
457
458
|
if (hasENS) {
|
|
458
459
|
try {
|
|
459
460
|
spinner.text = "Writing group ID to ENS...";
|
|
460
|
-
await setTextRecord(name, "xmtpGroupId", groupId,
|
|
461
|
+
await setTextRecord(name, "xmtpGroupId", groupId, fund.vault);
|
|
461
462
|
console.log(chalk.dim(" ENS text record written: xmtpGroupId"));
|
|
462
463
|
} catch {
|
|
463
464
|
console.log(chalk.dim(" ENS text record skipped (metadata fallback active)"));
|
|
@@ -476,7 +477,7 @@ async function handleInit(name, force, isPublic) {
|
|
|
476
477
|
}
|
|
477
478
|
}
|
|
478
479
|
function registerChatCommands(program) {
|
|
479
|
-
program.command("chat <name> [action] [actionArgs...]").description("
|
|
480
|
+
program.command("chat <name> [action] [actionArgs...]").description("Fund chat \u2014 stream, send, log, members, add, remove, init, public").option("--markdown", "Send as rich markdown (for send)", false).option("--stdin", "Read send message from stdin instead of CLI arg (avoids shell quoting issues with $ chars and quotes)", false).option("--limit <n>", "Number of messages to show (for log)", "20").option("--force", "Recreate group even if one exists (for init)", false).option("--public", "Enable public chat \u2014 adds dashboard spectator (for init)", false).option("--description <text>", "New description (for rename)").option("--group-id <id>", "Explicit XMTP group ID (for publish-ens)").action(async (name, action, actionArgs, opts) => {
|
|
480
481
|
switch (action) {
|
|
481
482
|
case "send": {
|
|
482
483
|
let message = actionArgs[0];
|
|
@@ -563,4 +564,4 @@ function registerChatCommands(program) {
|
|
|
563
564
|
export {
|
|
564
565
|
registerChatCommands
|
|
565
566
|
};
|
|
566
|
-
//# sourceMappingURL=chat-
|
|
567
|
+
//# sourceMappingURL=chat-2DKD3QPA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/chat.ts"],"sourcesContent":["/**\n * Chat commands — sherwood chat <fund-name> [action] [args...]\n *\n * Uses XMTP for encrypted group messaging tied to funds.\n * Shells out to the @xmtp/cli binary for all XMTP operations.\n *\n * Commander can't dispatch subcommands when the parent has a positional <name> arg\n * (it always runs the parent action). So we use manual dispatch: a single .action()\n * that routes based on the [action] argument.\n *\n * Usage:\n * sherwood chat <name> — stream messages (default)\n * sherwood chat <name> send \"hello\" — send a text message\n * sherwood chat <name> send \"hello\" --markdown — send formatted markdown\n * sherwood chat <name> react <id> <emoji> — react to a message\n * sherwood chat <name> log [--limit 50] — show recent messages\n * sherwood chat <name> members — list group members\n * sherwood chat <name> add <address> — add member (creator only)\n * sherwood chat <name> init [--force] [--public] — create XMTP group + write ENS record\n * sherwood chat <name> public on/off — toggle dashboard spectator access\n * sherwood chat <name> rename <new-name> [--description \"...\"] — update group metadata (admin only)\n * sherwood chat <name> publish-ens [--group-id <id>] — write xmtpGroupId ENS text record (creator only, recovery for failed init)\n * sherwood chat <name> publish-metadata [--group-id <id>] — backfill xmtpGroupId into IPFS metadata (creator only; use when ENS write reverts)\n */\n\nimport { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport { getAccount, formatContractError } from \"../lib/client.js\";\nimport { resolveFund, setTextRecord, getTextRecord } from \"../lib/ens.js\";\nimport { cacheGroupId, getCachedGroupId } from \"../lib/config.js\";\nimport { fetchMetadata, uploadMetadata } from \"../lib/ipfs.js\";\nimport * as factoryLib from \"../lib/factory.js\";\nimport { ENS } from \"../lib/addresses.js\";\nimport type { ChatEnvelope, MessageType } from \"../lib/types.js\";\nimport type { XmtpMessage } from \"../lib/xmtp.js\";\n\n// Lazy-load XMTP to avoid breaking non-chat commands when @xmtp/cli is missing\nasync function loadXmtp() {\n return import(\"../lib/xmtp.js\");\n}\n\n// ── Formatting ──\n\nfunction formatTimestamp(date: Date): string {\n return `${date.getHours().toString().padStart(2, \"0\")}:${date.getMinutes().toString().padStart(2, \"0\")}`;\n}\n\nfunction truncateAddress(addr: string): string {\n return `${addr.slice(0, 6)}...${addr.slice(-4)}`;\n}\n\nfunction colorByType(type: MessageType): (text: string) => string {\n switch (type) {\n case \"TRADE_EXECUTED\":\n return chalk.green;\n case \"RISK_ALERT\":\n return chalk.red;\n case \"TRADE_SIGNAL\":\n return chalk.yellow;\n case \"POSITION_UPDATE\":\n return chalk.cyan;\n case \"LP_REPORT\":\n return chalk.magenta;\n case \"X402_RESEARCH\":\n return chalk.cyan;\n case \"AGENT_REGISTERED\":\n case \"MEMBER_JOIN\":\n return chalk.blue;\n case \"RAGEQUIT_NOTICE\":\n return chalk.red;\n default:\n return chalk.white;\n }\n}\n\nfunction formatMessage(msg: XmtpMessage): string {\n const time = chalk.dim(`[${formatTimestamp(msg.sentAt)}]`);\n const sender = chalk.dim(truncateAddress(msg.senderInboxId));\n\n const text = msg.content;\n try {\n const envelope: ChatEnvelope = JSON.parse(text);\n const color = colorByType(envelope.type);\n const from = envelope.from ? truncateAddress(envelope.from) : sender;\n\n if (envelope.type === \"REACTION\") {\n const data = envelope.data as { reference?: string; emoji?: string } | undefined;\n return `${time} ${sender} reacted ${data?.emoji || \"?\"} to ${truncateAddress(data?.reference || \"?\")}`;\n }\n\n if (envelope.type === \"MESSAGE\") {\n if ((envelope.data as Record<string, unknown>)?.format === \"markdown\") {\n return `${time} ${chalk.dim(from)}\\n${envelope.text || \"\"}`;\n }\n return `${time} ${chalk.dim(from)}: ${envelope.text || \"\"}`;\n }\n\n if (envelope.type === \"AGENT_REGISTERED\") {\n return `${time} ${color(`[${envelope.type}]`)} Agent ${truncateAddress(envelope.agent?.address || \"?\")} registered`;\n }\n\n if (envelope.type === \"MEMBER_JOIN\") {\n return `${time} ${color(`[${envelope.type}]`)} ${truncateAddress(envelope.from || \"?\")} joined`;\n }\n\n if (envelope.type === \"X402_RESEARCH\") {\n const d = envelope.data as { provider?: string; queryType?: string; costUsdc?: string; attestationUid?: string } | undefined;\n const label = `${d?.provider || \"?\"} ${d?.queryType || \"?\"}`;\n return `${time} ${color(`[RESEARCH]`)} ${chalk.dim(from)}: ${label} ($${d?.costUsdc || \"?\"} USDC)`;\n }\n\n const summary = envelope.text || envelope.type;\n return `${time} ${color(`[${envelope.type}]`)} ${chalk.dim(from)}: ${summary}`;\n } catch {\n return `${time} ${sender}: ${text}`;\n }\n}\n\n// ── Action handlers ──\n\nasync function handleStream(name: string): Promise<void> {\n const spinner = ora(\"Connecting to chat...\").start();\n try {\n await resolveFund(name);\n const xmtp = await loadXmtp();\n const client = await xmtp.getXmtpClient();\n const group = await xmtp.getGroup(client, name);\n spinner.succeed(`Connected to ${name}.sherwoodagent.eth`);\n console.log(chalk.dim(\"Streaming messages... (Ctrl+C to exit)\\n\"));\n\n const cleanup = await xmtp.streamMessages(group, (msg) => {\n console.log(formatMessage(msg));\n });\n\n process.on(\"SIGINT\", async () => {\n console.log(chalk.dim(\"\\nDisconnecting...\"));\n cleanup();\n process.exit(0);\n });\n\n await new Promise(() => {});\n } catch (err) {\n spinner.fail(\"Failed to connect to chat\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\n/** Drain stdin into a single string. Used by `chat send --stdin` to bypass\n * shell expansion on dynamic messages (e.g. amounts containing `$`). */\nasync function readStdin(): Promise<string> {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n return Buffer.concat(chunks).toString('utf-8');\n}\n\nasync function handleSend(name: string, message: string, markdown: boolean): Promise<void> {\n const spinner = ora(\"Sending...\").start();\n try {\n const xmtp = await loadXmtp();\n const group = await xmtp.getGroup(\"\", name);\n\n if (markdown) {\n await xmtp.sendMarkdown(group, message);\n } else {\n const envelope: ChatEnvelope = {\n type: \"MESSAGE\",\n from: getAccount().address,\n text: message,\n timestamp: Math.floor(Date.now() / 1000),\n };\n await xmtp.sendEnvelope(group, envelope);\n }\n\n spinner.succeed(\"Message sent\");\n } catch (err) {\n spinner.fail(\"Failed to send message\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\nasync function handleReact(name: string, messageId: string, emoji: string): Promise<void> {\n const spinner = ora(\"Reacting...\").start();\n try {\n const xmtp = await loadXmtp();\n const group = await xmtp.getGroup(\"\", name);\n await xmtp.sendReaction(group, messageId, emoji);\n spinner.succeed(`Reacted ${emoji}`);\n } catch (err) {\n spinner.fail(\"Failed to send reaction\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\nasync function handleLog(name: string, limit: number): Promise<void> {\n const spinner = ora(\"Loading messages...\").start();\n try {\n const xmtp = await loadXmtp();\n const group = await xmtp.getGroup(\"\", name);\n const messages = await xmtp.getRecentMessages(group, limit);\n\n spinner.stop();\n console.log();\n console.log(chalk.bold(`Chat log: ${name}.sherwoodagent.eth`));\n console.log(chalk.dim(\"─\".repeat(50)));\n\n if (messages.length === 0) {\n console.log(chalk.dim(\" No messages yet\"));\n } else {\n for (const msg of messages.reverse()) {\n console.log(formatMessage(msg));\n }\n }\n console.log();\n } catch (err) {\n spinner.fail(\"Failed to load messages\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\nasync function handleMembers(name: string): Promise<void> {\n const spinner = ora(\"Loading members...\").start();\n try {\n const xmtp = await loadXmtp();\n const group = await xmtp.getGroup(\"\", name);\n const members = await xmtp.getMembers(group);\n\n spinner.stop();\n console.log();\n console.log(chalk.bold(`Members: ${name}.sherwoodagent.eth`));\n console.log(chalk.dim(\"─\".repeat(50)));\n\n for (const member of members) {\n const role = member.permissionLevel === \"super_admin\"\n ? chalk.yellow(\" (super admin)\")\n : member.permissionLevel === \"admin\"\n ? chalk.blue(\" (admin)\")\n : \"\";\n console.log(` ${member.inboxId}${role}`);\n }\n\n console.log(chalk.dim(`\\n Total: ${members.length} members`));\n console.log();\n } catch (err) {\n spinner.fail(\"Failed to load members\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\nasync function handleAdd(name: string, address: string): Promise<void> {\n const spinner = ora(\"Adding member...\").start();\n try {\n const xmtp = await loadXmtp();\n const group = await xmtp.getGroup(\"\", name);\n await xmtp.addMember(group, address);\n\n await xmtp.sendEnvelope(group, {\n type: \"MEMBER_JOIN\",\n from: address,\n fund: name,\n timestamp: Math.floor(Date.now() / 1000),\n });\n\n spinner.succeed(`Member added: ${address}`);\n } catch (err) {\n spinner.fail(\"Failed to add member\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\nasync function handleRemove(name: string, address: string): Promise<void> {\n const spinner = ora(\"Removing member...\").start();\n try {\n const xmtp = await loadXmtp();\n const group = await xmtp.getGroup(\"\", name);\n await xmtp.removeMember(group, address);\n spinner.succeed(`Member removed: ${address}`);\n } catch (err) {\n spinner.fail(\"Failed to remove member\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\n/**\n * Notify the spectator service to sync new groups.\n * Called after adding the spectator wallet to an XMTP group so the service\n * invalidates its cache, calls syncAll(), and discovers the new group.\n */\nasync function notifySpectatorSync(): Promise<void> {\n const spectatorUrl =\n process.env.SPECTATOR_URL || \"https://spectator.sherwood.sh\";\n try {\n const res = await fetch(`${spectatorUrl}/sync`, {\n method: \"POST\",\n signal: AbortSignal.timeout(8_000),\n });\n if (res.ok) {\n const data = (await res.json()) as { groups?: number };\n console.log(\n chalk.dim(\n ` Spectator synced — ${data.groups ?? \"?\"} groups discovered`,\n ),\n );\n }\n } catch {\n // Non-fatal — spectator will self-sync within 60 s via cache TTL\n console.log(\n chalk.dim(\" (Spectator sync skipped — will auto-discover within 60 s)\"),\n );\n }\n}\n\nasync function handleRename(\n name: string,\n newName?: string,\n newDescription?: string,\n): Promise<void> {\n if (newName === undefined && newDescription === undefined) {\n console.error(\n chalk.red(\n \"Usage: sherwood chat <name> rename [new-name] [--description \\\"...\\\"]\",\n ),\n );\n process.exit(1);\n }\n const spinner = ora(\"Updating group metadata...\").start();\n try {\n const xmtp = await loadXmtp();\n const group = await xmtp.getGroup(\"\", name);\n await xmtp.renameGroup(group, newName, newDescription);\n spinner.succeed(\n `Updated ${[\n newName !== undefined ? `name → \"${newName}\"` : null,\n newDescription !== undefined\n ? `description → \"${newDescription}\"`\n : null,\n ]\n .filter(Boolean)\n .join(\", \")}`,\n );\n } catch (err) {\n spinner.fail(\"Failed to update group metadata\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\nasync function handlePublishMetadata(\n name: string,\n groupIdOpt?: string,\n): Promise<void> {\n const spinner = ora(\"Updating fund metadata...\").start();\n try {\n const fund = await resolveFund(name);\n const callerAddress = getAccount().address.toLowerCase();\n if (fund.creator.toLowerCase() !== callerAddress) {\n spinner.fail(\"Only the fund creator can update metadata\");\n process.exit(1);\n }\n\n const xmtp = await loadXmtp();\n let groupId = groupIdOpt || getCachedGroupId(name);\n if (!groupId) {\n try {\n groupId = await xmtp.getGroup(\"\", name);\n } catch {\n // ignore\n }\n }\n if (!groupId) {\n spinner.fail(\n \"Could not determine group ID — pass --group-id <id> or run from a machine that has the group cached\",\n );\n process.exit(1);\n }\n\n spinner.text = \"Fetching current metadata...\";\n const info = await factoryLib.getFund(fund.id);\n let metadata;\n try {\n metadata = await fetchMetadata(info.metadataURI);\n } catch {\n metadata = {\n schema: \"sherwood/fund/v1\",\n name,\n description: \"\",\n chain: \"\",\n strategies: [],\n terms: {},\n links: {},\n };\n }\n metadata.xmtpGroupId = groupId;\n\n spinner.text = \"Pinning updated metadata to IPFS...\";\n const newURI = await uploadMetadata(metadata);\n\n spinner.text = \"Calling factory.updateMetadata...\";\n await factoryLib.updateMetadata(fund.id, newURI);\n\n spinner.succeed(`Metadata updated — xmtpGroupId = ${groupId}`);\n console.log(chalk.dim(` New URI: ${newURI}`));\n console.log(\n chalk.dim(\n \" Dashboard will resolve the group via metadata fallback on next page load.\",\n ),\n );\n } catch (err) {\n spinner.fail(\"Failed to update metadata\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\nasync function handlePublishEns(name: string, groupIdOpt?: string): Promise<void> {\n const spinner = ora(\"Publishing group ID to ENS...\").start();\n try {\n const fund = await resolveFund(name);\n const callerAddress = getAccount().address.toLowerCase();\n if (fund.creator.toLowerCase() !== callerAddress) {\n spinner.fail(\"Only the fund creator can write ENS records\");\n process.exit(1);\n }\n\n // Resolve group ID: explicit arg → local cache → existing ENS record (no-op)\n const xmtp = await loadXmtp();\n let groupId = groupIdOpt || getCachedGroupId(name);\n if (!groupId) {\n try {\n groupId = await xmtp.getGroup(\"\", name); // syncs DB + name-search fallback\n } catch {\n // ignore\n }\n }\n if (!groupId) {\n spinner.fail(\n \"Could not determine group ID — pass --group-id <id> or run from a machine that has the group cached\",\n );\n process.exit(1);\n }\n\n await setTextRecord(name, \"xmtpGroupId\", groupId, fund.vault);\n spinner.succeed(`Published xmtpGroupId → ${groupId} on ENS`);\n console.log(chalk.dim(\" Dashboard will pick up the group on next page load.\"));\n } catch (err) {\n spinner.fail(\"Failed to publish ENS record\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\nasync function handlePublic(name: string, on: boolean): Promise<void> {\n const xmtp = await loadXmtp();\n // Default to the canonical production spectator; `DASHBOARD_SPECTATOR_ADDRESS`\n // is an override hook for testing or future rotation. Earlier versions\n // of this command required the env var with no default — when callers\n // (cron, manual one-liners) set it to the wrong wallet, the agent's\n // own wallet got added \"as the spectator\" and the dashboard stayed\n // OFFLINE because the real spectator service was never in the group.\n // Single source of truth lives in xmtp.ts: resolveSpectatorAddress().\n const spectatorAddress = xmtp.resolveSpectatorAddress();\n const usingDefault =\n spectatorAddress === xmtp.DEFAULT_DASHBOARD_SPECTATOR_ADDRESS;\n\n const spinner = ora(`${on ? \"Enabling\" : \"Disabling\"} public chat...`).start();\n try {\n const group = await xmtp.getGroup(\"\", name);\n\n if (on) {\n await xmtp.addMember(group, spectatorAddress);\n spinner.succeed(\n `Public chat enabled — dashboard spectator added (${spectatorAddress}${usingDefault ? \", canonical\" : \", overridden via env\"})`,\n );\n await notifySpectatorSync();\n } else {\n await xmtp.removeMember(group, spectatorAddress);\n spinner.succeed(\n `Public chat disabled — dashboard spectator removed (${spectatorAddress})`,\n );\n }\n } catch (err) {\n spinner.fail(\"Failed to toggle public chat\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\nasync function handleInit(name: string, force: boolean, isPublic: boolean): Promise<void> {\n const spinner = ora(\"Initializing chat group...\").start();\n try {\n const fund = await resolveFund(name);\n const callerAddress = getAccount().address.toLowerCase();\n if (fund.creator.toLowerCase() !== callerAddress) {\n spinner.fail(\"Only the fund creator can initialize the chat group\");\n process.exit(1);\n }\n\n // Idempotency check\n if (!force) {\n const existingId = getCachedGroupId(name) || await getTextRecord(name, \"xmtpGroupId\");\n if (existingId) {\n spinner.succeed(\"XMTP group already exists for this fund\");\n console.log(chalk.dim(` Group ID: ${existingId}`));\n return;\n }\n }\n\n // Create the group\n spinner.text = \"Creating XMTP group...\";\n const xmtp = await loadXmtp();\n const client = await xmtp.getXmtpClient();\n const groupId = await xmtp.createFundGroup(client, name, isPublic);\n\n cacheGroupId(name, groupId);\n\n // Persist group ID. Strategy:\n // 1. ALWAYS write to IPFS metadata — canonical source on every chain.\n // The dashboard's resolver chain is `ENS text → metadata → null`, so\n // metadata being populated guarantees the dashboard works regardless\n // of ENS state. Necessary because the V1.5 vault has no `executeBatch`\n // (V-C3 removal), so the ENS `setText` call below currently reverts on\n // Base mainnet — without metadata, the dashboard would show OFFLINE.\n // 2. ADDITIONALLY try the ENS text record (best-effort). Fast resolution\n // path for chains where the vault has a writable executeBatch path\n // (HyperEVM today, or a future vault setEnsText helper). On failure,\n // we log a debug line and keep going — metadata fallback covers it.\n spinner.text = \"Saving group ID to fund metadata...\";\n try {\n const info = await factoryLib.getFund(fund.id);\n let metadata;\n try {\n metadata = await fetchMetadata(info.metadataURI);\n } catch {\n metadata = {\n schema: \"sherwood/fund/v1\",\n name,\n description: \"\",\n chain: \"\",\n strategies: [],\n terms: {},\n links: {},\n };\n }\n metadata.xmtpGroupId = groupId;\n const newURI = await uploadMetadata(metadata);\n await factoryLib.updateMetadata(fund.id, newURI);\n console.log(chalk.dim(`\\n Group ID saved to metadata: ${newURI}`));\n } catch (metaErr) {\n console.warn(chalk.yellow(\"\\n ⚠ Could not persist group ID to IPFS metadata\"));\n console.warn(chalk.dim(` ${metaErr instanceof Error ? metaErr.message : String(metaErr)}`));\n console.warn(chalk.dim(\" Dashboard may show OFFLINE until backfilled with `chat <name> publish-metadata`.\"));\n }\n\n // Best-effort ENS text record write. Currently reverts on Base mainnet\n // because the V1.5 vault has no owner-direct executeBatch path; metadata\n // above is the operative resolution source. Will succeed once a vault\n // setEnsText helper lands or on chains where the existing path works.\n const hasENS = ENS().L2_REGISTRY !== \"0x0000000000000000000000000000000000000000\";\n if (hasENS) {\n try {\n spinner.text = \"Writing group ID to ENS...\";\n await setTextRecord(name, \"xmtpGroupId\", groupId, fund.vault);\n console.log(chalk.dim(\" ENS text record written: xmtpGroupId\"));\n } catch {\n console.log(chalk.dim(\" ENS text record skipped (metadata fallback active)\"));\n }\n }\n\n spinner.succeed(`Chat group created for ${name}.sherwoodagent.eth`);\n console.log(chalk.dim(` Group ID: ${groupId}`));\n console.log(chalk.dim(` Stream: sherwood chat ${name}`));\n\n // Notify spectator to sync and discover the new public group\n if (isPublic) {\n await notifySpectatorSync();\n }\n } catch (err) {\n spinner.fail(\"Failed to initialize chat group\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\n// ── Command Registration ──\n\nexport function registerChatCommands(program: Command): void {\n program\n .command(\"chat <name> [action] [actionArgs...]\")\n .description(\"Fund chat — stream, send, log, members, add, remove, init, public\")\n .option(\"--markdown\", \"Send as rich markdown (for send)\", false)\n .option(\"--stdin\", \"Read send message from stdin instead of CLI arg (avoids shell quoting issues with $ chars and quotes)\", false)\n .option(\"--limit <n>\", \"Number of messages to show (for log)\", \"20\")\n .option(\"--force\", \"Recreate group even if one exists (for init)\", false)\n .option(\"--public\", \"Enable public chat — adds dashboard spectator (for init)\", false)\n .option(\"--description <text>\", \"New description (for rename)\")\n .option(\"--group-id <id>\", \"Explicit XMTP group ID (for publish-ens)\")\n .action(async (name: string, action: string | undefined, actionArgs: string[], opts: { markdown: boolean; stdin: boolean; limit: string; force: boolean; public: boolean; description?: string; groupId?: string }) => {\n switch (action) {\n case \"send\": {\n let message = actionArgs[0];\n if (opts.stdin) {\n // If stdin is a terminal, the user forgot to pipe — readStdin\n // would block forever waiting for EOF (Ctrl+D). Warn explicitly.\n if (process.stdin.isTTY) {\n console.error(chalk.yellow(\"--stdin specified but stdin is a terminal. Either pipe the message in or omit --stdin and pass it as an argument. Press Ctrl+D to send what you've typed, Ctrl+C to abort.\"));\n }\n // Read entire stdin into the message — sidesteps bash $-expansion\n // when callers (cron jobs, scripts) construct dynamic message text.\n message = await readStdin();\n if (!message.trim()) {\n console.error(chalk.red(\"--stdin given but stdin was empty\"));\n process.exit(1);\n }\n }\n if (!message) {\n console.error(chalk.red(\"Usage: sherwood chat <name> send <message> [--markdown]\\n echo \\\"$msg\\\" | sherwood chat <name> send --stdin\"));\n process.exit(1);\n }\n await handleSend(name, message, opts.markdown);\n break;\n }\n\n case \"react\": {\n const [messageId, emoji] = actionArgs;\n if (!messageId || !emoji) {\n console.error(chalk.red(\"Usage: sherwood chat <name> react <messageId> <emoji>\"));\n process.exit(1);\n }\n await handleReact(name, messageId, emoji);\n break;\n }\n\n case \"log\":\n await handleLog(name, parseInt(opts.limit, 10));\n break;\n\n case \"members\":\n await handleMembers(name);\n break;\n\n case \"add\": {\n const address = actionArgs[0];\n if (!address) {\n console.error(chalk.red(\"Usage: sherwood chat <name> add <address>\"));\n process.exit(1);\n }\n await handleAdd(name, address);\n break;\n }\n\n case \"remove\": {\n const removeAddr = actionArgs[0];\n if (!removeAddr) {\n console.error(chalk.red(\"Usage: sherwood chat <name> remove <address>\"));\n process.exit(1);\n }\n await handleRemove(name, removeAddr);\n break;\n }\n\n case \"init\":\n await handleInit(name, opts.force, opts.public);\n break;\n\n case \"rename\": {\n const newName = actionArgs[0]; // optional positional\n await handleRename(name, newName, opts.description);\n break;\n }\n\n case \"publish-ens\":\n await handlePublishEns(name, opts.groupId);\n break;\n\n case \"publish-metadata\":\n await handlePublishMetadata(name, opts.groupId);\n break;\n\n case \"public\": {\n const flag = actionArgs[0];\n if (flag !== \"on\" && flag !== \"off\") {\n console.error(chalk.red(\"Usage: sherwood chat <name> public on/off\"));\n process.exit(1);\n }\n await handlePublic(name, flag === \"on\");\n break;\n }\n\n case undefined:\n default:\n await handleStream(name);\n break;\n }\n });\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,30 +1,23 @@
|
|
|
1
1
|
import {
|
|
2
|
-
ENS,
|
|
3
|
-
SHERWOOD,
|
|
4
2
|
TOKENS
|
|
5
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-YLYANZMC.js";
|
|
6
4
|
import {
|
|
7
5
|
getAccount,
|
|
8
6
|
getPublicClient,
|
|
9
7
|
getWalletClient,
|
|
10
8
|
waitForReceipt,
|
|
11
9
|
writeContractWithRetry
|
|
12
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-UC5RMCB3.js";
|
|
13
11
|
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
getChain,
|
|
17
|
-
withNetwork
|
|
18
|
-
} from "./chunk-2LRC7I33.js";
|
|
12
|
+
getChain
|
|
13
|
+
} from "./chunk-RNASYOQT.js";
|
|
19
14
|
import {
|
|
20
15
|
getChainContracts
|
|
21
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-GSC7PVEO.js";
|
|
22
17
|
import {
|
|
23
18
|
ERC20_ABI,
|
|
24
|
-
L2_REGISTRY_ABI,
|
|
25
|
-
SYNDICATE_FACTORY_ABI,
|
|
26
19
|
SYNDICATE_VAULT_ABI
|
|
27
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-BQMOE4FJ.js";
|
|
28
21
|
|
|
29
22
|
// src/lib/vault.ts
|
|
30
23
|
import { formatUnits, parseEther } from "viem";
|
|
@@ -66,6 +59,26 @@ async function getVaultOwner(vault) {
|
|
|
66
59
|
functionName: "owner"
|
|
67
60
|
});
|
|
68
61
|
}
|
|
62
|
+
async function getAgentFeeBps(vault) {
|
|
63
|
+
const client = getPublicClient();
|
|
64
|
+
return client.readContract({
|
|
65
|
+
address: vault ?? getVaultAddress(),
|
|
66
|
+
abi: SYNDICATE_VAULT_ABI,
|
|
67
|
+
functionName: "agentFeeBps"
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
async function setAgentFeeBps(bps) {
|
|
71
|
+
const hash = await writeContractWithRetry({
|
|
72
|
+
account: getAccount(),
|
|
73
|
+
chain: getChain(),
|
|
74
|
+
address: getVaultAddress(),
|
|
75
|
+
abi: SYNDICATE_VAULT_ABI,
|
|
76
|
+
functionName: "setAgentFeeBps",
|
|
77
|
+
args: [bps]
|
|
78
|
+
});
|
|
79
|
+
await waitForReceipt(hash);
|
|
80
|
+
return hash;
|
|
81
|
+
}
|
|
69
82
|
var WETH_ABI = [
|
|
70
83
|
{
|
|
71
84
|
name: "deposit",
|
|
@@ -118,9 +131,18 @@ async function preflightDeposit(amount) {
|
|
|
118
131
|
functionName: "redemptionsLocked"
|
|
119
132
|
});
|
|
120
133
|
if (locked) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
134
|
+
const cap = await client.readContract({
|
|
135
|
+
address: vaultAddress,
|
|
136
|
+
abi: SYNDICATE_VAULT_ABI,
|
|
137
|
+
functionName: "maxDeposit",
|
|
138
|
+
args: [account.address]
|
|
139
|
+
});
|
|
140
|
+
if (cap < amount) {
|
|
141
|
+
throw new Error(
|
|
142
|
+
cap === 0n ? "Deposits are locked while a strategy is executing (Lane A unavailable for this strategy). Queue an async deposit instead:\n sherwood queue request-deposit --vault <vault> --amount <usdc>" : `Deposit exceeds the Lane A instant cap while a strategy is executing (maxDeposit ${cap.toString()} raw asset units, requested ${amount.toString()}). Reduce the amount or queue it:
|
|
143
|
+
sherwood queue request-deposit --vault <vault> --amount <usdc>`
|
|
144
|
+
);
|
|
145
|
+
}
|
|
124
146
|
}
|
|
125
147
|
const openDeposits = await client.readContract({
|
|
126
148
|
address: vaultAddress,
|
|
@@ -136,7 +158,7 @@ async function preflightDeposit(amount) {
|
|
|
136
158
|
});
|
|
137
159
|
if (!approved) {
|
|
138
160
|
throw new Error(
|
|
139
|
-
"Your address is not an approved depositor. Ask the vault creator to approve you:\n sherwood
|
|
161
|
+
"Your address is not an approved depositor. Ask the vault creator to approve you:\n sherwood fund approve-depositor --depositor " + account.address
|
|
140
162
|
);
|
|
141
163
|
}
|
|
142
164
|
}
|
|
@@ -174,7 +196,10 @@ async function depositWithEthWrap(amount) {
|
|
|
174
196
|
account,
|
|
175
197
|
chain: getChain()
|
|
176
198
|
});
|
|
177
|
-
await client.waitForTransactionReceipt({ hash: wrapHash });
|
|
199
|
+
const wrapReceipt = await client.waitForTransactionReceipt({ hash: wrapHash });
|
|
200
|
+
if (wrapReceipt.status !== "success") {
|
|
201
|
+
throw new Error(`ETH->WETH wrap reverted on-chain (tx ${wrapHash}).`);
|
|
202
|
+
}
|
|
178
203
|
}
|
|
179
204
|
return deposit(amount);
|
|
180
205
|
}
|
|
@@ -198,8 +223,17 @@ async function deposit(amount) {
|
|
|
198
223
|
functionName: "approve",
|
|
199
224
|
args: [vaultAddress, amount]
|
|
200
225
|
});
|
|
201
|
-
await waitForReceipt(approveHash);
|
|
226
|
+
const approveReceipt = await waitForReceipt(approveHash);
|
|
227
|
+
if (approveReceipt.status !== "success") {
|
|
228
|
+
throw new Error(`Asset approve reverted on-chain (tx ${approveHash}).`);
|
|
229
|
+
}
|
|
202
230
|
}
|
|
231
|
+
const sharesBefore = await client.readContract({
|
|
232
|
+
address: vaultAddress,
|
|
233
|
+
abi: SYNDICATE_VAULT_ABI,
|
|
234
|
+
functionName: "balanceOf",
|
|
235
|
+
args: [account.address]
|
|
236
|
+
});
|
|
203
237
|
const depositHash = await writeContractWithRetry({
|
|
204
238
|
account,
|
|
205
239
|
chain: getChain(),
|
|
@@ -208,7 +242,23 @@ async function deposit(amount) {
|
|
|
208
242
|
functionName: "deposit",
|
|
209
243
|
args: [amount, account.address]
|
|
210
244
|
});
|
|
211
|
-
await waitForReceipt(depositHash);
|
|
245
|
+
const receipt = await waitForReceipt(depositHash);
|
|
246
|
+
if (receipt.status !== "success") {
|
|
247
|
+
throw new Error(
|
|
248
|
+
`Deposit transaction reverted on-chain (tx ${depositHash}). No shares were minted.`
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
const sharesAfter = await client.readContract({
|
|
252
|
+
address: vaultAddress,
|
|
253
|
+
abi: SYNDICATE_VAULT_ABI,
|
|
254
|
+
functionName: "balanceOf",
|
|
255
|
+
args: [account.address]
|
|
256
|
+
});
|
|
257
|
+
if (sharesAfter <= sharesBefore) {
|
|
258
|
+
throw new Error(
|
|
259
|
+
`Deposit confirmed on-chain (tx ${depositHash}) but minted 0 shares (balance ${sharesBefore} -> ${sharesAfter}). Aborting to avoid reporting a phantom deposit.`
|
|
260
|
+
);
|
|
261
|
+
}
|
|
212
262
|
return depositHash;
|
|
213
263
|
}
|
|
214
264
|
async function preflightRedeem(shares) {
|
|
@@ -243,9 +293,18 @@ async function preflightRedeem(shares) {
|
|
|
243
293
|
functionName: "redemptionsLocked"
|
|
244
294
|
});
|
|
245
295
|
if (locked) {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
296
|
+
const cap = await client.readContract({
|
|
297
|
+
address: vaultAddress,
|
|
298
|
+
abi: SYNDICATE_VAULT_ABI,
|
|
299
|
+
functionName: "maxRedeem",
|
|
300
|
+
args: [account.address]
|
|
301
|
+
});
|
|
302
|
+
if (cap < shares) {
|
|
303
|
+
throw new Error(
|
|
304
|
+
cap === 0n ? "Redemptions are locked while a strategy is executing (Lane A unavailable for this strategy). Queue an async redeem instead:\n sherwood queue request-redeem --vault <vault> --shares <raw-shares>" : `Redeem exceeds the Lane A instant cap while a strategy is executing (maxRedeem ${cap.toString()} raw shares, requested ${shares.toString()}). Reduce the share amount or queue it:
|
|
305
|
+
sherwood queue request-redeem --vault <vault> --shares <raw-shares>`
|
|
306
|
+
);
|
|
307
|
+
}
|
|
249
308
|
}
|
|
250
309
|
const ethBalance = await client.getBalance({ address: account.address });
|
|
251
310
|
const minGas = parseEther("0.0005");
|
|
@@ -329,6 +388,18 @@ async function removeDepositor(depositor) {
|
|
|
329
388
|
await waitForReceipt(hash);
|
|
330
389
|
return hash;
|
|
331
390
|
}
|
|
391
|
+
async function approveDepositors(depositors) {
|
|
392
|
+
const hash = await writeContractWithRetry({
|
|
393
|
+
account: getAccount(),
|
|
394
|
+
chain: getChain(),
|
|
395
|
+
address: getVaultAddress(),
|
|
396
|
+
abi: SYNDICATE_VAULT_ABI,
|
|
397
|
+
functionName: "approveDepositors",
|
|
398
|
+
args: [depositors]
|
|
399
|
+
});
|
|
400
|
+
await waitForReceipt(hash);
|
|
401
|
+
return hash;
|
|
402
|
+
}
|
|
332
403
|
async function isAgent(agentAddress) {
|
|
333
404
|
const client = getPublicClient();
|
|
334
405
|
return client.readContract({
|
|
@@ -338,6 +409,15 @@ async function isAgent(agentAddress) {
|
|
|
338
409
|
args: [agentAddress]
|
|
339
410
|
});
|
|
340
411
|
}
|
|
412
|
+
async function isApprovedDepositor(depositor) {
|
|
413
|
+
const client = getPublicClient();
|
|
414
|
+
return client.readContract({
|
|
415
|
+
address: getVaultAddress(),
|
|
416
|
+
abi: SYNDICATE_VAULT_ABI,
|
|
417
|
+
functionName: "isApprovedDepositor",
|
|
418
|
+
args: [depositor]
|
|
419
|
+
});
|
|
420
|
+
}
|
|
341
421
|
async function getBalance(address) {
|
|
342
422
|
const client = getPublicClient();
|
|
343
423
|
const vaultAddress = getVaultAddress();
|
|
@@ -419,111 +499,14 @@ async function getVaultInfo() {
|
|
|
419
499
|
};
|
|
420
500
|
}
|
|
421
501
|
|
|
422
|
-
// src/lib/ens.ts
|
|
423
|
-
import { encodeFunctionData } from "viem";
|
|
424
|
-
import { namehash } from "viem/ens";
|
|
425
|
-
var ENS_DOMAIN = "sherwoodagent.eth";
|
|
426
|
-
function getFactoryAddress() {
|
|
427
|
-
return SHERWOOD().FACTORY;
|
|
428
|
-
}
|
|
429
|
-
async function resolveSyndicate(subdomain) {
|
|
430
|
-
const client = getPublicClient();
|
|
431
|
-
const factory = getFactoryAddress();
|
|
432
|
-
const syndicateId = await client.readContract({
|
|
433
|
-
address: factory,
|
|
434
|
-
abi: SYNDICATE_FACTORY_ABI,
|
|
435
|
-
functionName: "subdomainToSyndicate",
|
|
436
|
-
args: [subdomain]
|
|
437
|
-
});
|
|
438
|
-
if (syndicateId === 0n) {
|
|
439
|
-
throw new Error(`Syndicate "${subdomain}" not found`);
|
|
440
|
-
}
|
|
441
|
-
const result = await client.readContract({
|
|
442
|
-
address: factory,
|
|
443
|
-
abi: SYNDICATE_FACTORY_ABI,
|
|
444
|
-
functionName: "syndicates",
|
|
445
|
-
args: [syndicateId]
|
|
446
|
-
});
|
|
447
|
-
return {
|
|
448
|
-
id: result[0],
|
|
449
|
-
vault: result[1],
|
|
450
|
-
creator: result[2],
|
|
451
|
-
subdomain: result[6]
|
|
452
|
-
};
|
|
453
|
-
}
|
|
454
|
-
async function searchSyndicateAcrossChains(subdomain) {
|
|
455
|
-
const includeTestnets = process.env.ENABLE_TESTNET === "true";
|
|
456
|
-
const candidates = VALID_NETWORKS.filter(
|
|
457
|
-
(n) => includeTestnets || !CHAIN_REGISTRY[n].isTestnet
|
|
458
|
-
);
|
|
459
|
-
const hits = [];
|
|
460
|
-
for (const net of candidates) {
|
|
461
|
-
try {
|
|
462
|
-
const result = await withNetwork(net, () => resolveSyndicate(subdomain));
|
|
463
|
-
hits.push({ network: net, result });
|
|
464
|
-
} catch {
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
return hits;
|
|
468
|
-
}
|
|
469
|
-
async function resolveVaultSyndicate(vaultAddress) {
|
|
470
|
-
const client = getPublicClient();
|
|
471
|
-
const factory = getFactoryAddress();
|
|
472
|
-
const syndicateId = await client.readContract({
|
|
473
|
-
address: factory,
|
|
474
|
-
abi: SYNDICATE_FACTORY_ABI,
|
|
475
|
-
functionName: "vaultToSyndicate",
|
|
476
|
-
args: [vaultAddress]
|
|
477
|
-
});
|
|
478
|
-
if (syndicateId === 0n) {
|
|
479
|
-
throw new Error(`No syndicate found for vault ${vaultAddress}`);
|
|
480
|
-
}
|
|
481
|
-
const result = await client.readContract({
|
|
482
|
-
address: factory,
|
|
483
|
-
abi: SYNDICATE_FACTORY_ABI,
|
|
484
|
-
functionName: "syndicates",
|
|
485
|
-
args: [syndicateId]
|
|
486
|
-
});
|
|
487
|
-
return {
|
|
488
|
-
id: result[0],
|
|
489
|
-
vault: result[1],
|
|
490
|
-
creator: result[2],
|
|
491
|
-
subdomain: result[6]
|
|
492
|
-
};
|
|
493
|
-
}
|
|
494
|
-
function getSubdomainNode(subdomain) {
|
|
495
|
-
return namehash(`${subdomain}.${ENS_DOMAIN}`);
|
|
496
|
-
}
|
|
497
|
-
async function setTextRecord(subdomain, key, value, vaultAddress) {
|
|
498
|
-
const l2Registry = ENS().L2_REGISTRY;
|
|
499
|
-
const node = getSubdomainNode(subdomain);
|
|
500
|
-
setVaultAddress(vaultAddress);
|
|
501
|
-
const setTextData = encodeFunctionData({
|
|
502
|
-
abi: L2_REGISTRY_ABI,
|
|
503
|
-
functionName: "setText",
|
|
504
|
-
args: [node, key, value]
|
|
505
|
-
});
|
|
506
|
-
return executeBatch(
|
|
507
|
-
[{ target: l2Registry, data: setTextData, value: 0n }]
|
|
508
|
-
);
|
|
509
|
-
}
|
|
510
|
-
async function getTextRecord(subdomain, key) {
|
|
511
|
-
const client = getPublicClient();
|
|
512
|
-
const node = getSubdomainNode(subdomain);
|
|
513
|
-
return client.readContract({
|
|
514
|
-
address: ENS().L2_REGISTRY,
|
|
515
|
-
abi: L2_REGISTRY_ABI,
|
|
516
|
-
functionName: "text",
|
|
517
|
-
args: [node, key]
|
|
518
|
-
});
|
|
519
|
-
}
|
|
520
|
-
|
|
521
502
|
export {
|
|
522
503
|
setVaultAddress,
|
|
523
504
|
getVaultAddress,
|
|
524
505
|
getAssetAddress,
|
|
525
506
|
getAssetDecimals,
|
|
526
507
|
getVaultOwner,
|
|
508
|
+
getAgentFeeBps,
|
|
509
|
+
setAgentFeeBps,
|
|
527
510
|
preflightDeposit,
|
|
528
511
|
depositWithEthWrap,
|
|
529
512
|
deposit,
|
|
@@ -533,14 +516,11 @@ export {
|
|
|
533
516
|
executeBatch,
|
|
534
517
|
approveDepositor,
|
|
535
518
|
removeDepositor,
|
|
519
|
+
approveDepositors,
|
|
536
520
|
isAgent,
|
|
521
|
+
isApprovedDepositor,
|
|
537
522
|
getBalance,
|
|
538
523
|
registerAgent,
|
|
539
|
-
getVaultInfo
|
|
540
|
-
resolveSyndicate,
|
|
541
|
-
searchSyndicateAcrossChains,
|
|
542
|
-
resolveVaultSyndicate,
|
|
543
|
-
setTextRecord,
|
|
544
|
-
getTextRecord
|
|
524
|
+
getVaultInfo
|
|
545
525
|
};
|
|
546
|
-
//# sourceMappingURL=chunk-
|
|
526
|
+
//# sourceMappingURL=chunk-3YUMQGJD.js.map
|