@sherwoodagent/cli 0.83.0 → 0.83.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-I6AUSTZG.js → agent-C5XZSK7Z.js} +4 -4
- package/dist/{chat-5SI7K5FA.js → chat-FGTYCJ4B.js} +5 -5
- package/dist/{chunk-ZTR2GPZO.js → chunk-4GO7I33U.js} +2 -2
- package/dist/{chunk-UK2G5AZG.js → chunk-4WAEM2TC.js} +2 -2
- package/dist/{chunk-N5RIB6ZZ.js → chunk-AJ46XIGY.js} +2 -2
- package/dist/{chunk-4BMAFJKO.js → chunk-AOL2DJTK.js} +3 -3
- package/dist/{chunk-5BYJ3TGZ.js → chunk-AQPTBVEA.js} +19 -3
- package/dist/chunk-AQPTBVEA.js.map +1 -0
- package/dist/{chunk-7ILOT3SI.js → chunk-CCNOXDZ7.js} +2 -2
- package/dist/{chunk-MRUTBOMZ.js → chunk-G34TNE53.js} +2 -2
- package/dist/{chunk-ZEZQM42M.js → chunk-M34JVPJO.js} +2 -2
- package/dist/{chunk-I7NXKJCG.js → chunk-MBWFTXF5.js} +2 -2
- package/dist/{chunk-5O3KO7ZI.js → chunk-NKRMMFQG.js} +3 -3
- package/dist/{client-Z7LNEG2F.js → client-OPZ2ORWW.js} +2 -2
- package/dist/{eas-DWBT332K.js → eas-32PEJETG.js} +3 -3
- package/dist/{governor-ZVNTHHML.js → governor-GSCXEZWI.js} +3 -3
- package/dist/index.js +1032 -938
- package/dist/index.js.map +1 -1
- package/dist/{price-GEUCSTO4.js → price-OUL7WIXQ.js} +3 -3
- package/dist/{providers-PWUJ2AR7.js → providers-4HQHB7V5.js} +3 -3
- package/dist/{research-SW6PLO32.js → research-2RWAMWWH.js} +5 -5
- package/dist/{session-6RSOG5NE.js → session-SIOJZO6P.js} +6 -6
- package/dist/{trade-45JPCKGP.js → trade-ZEIY3MCL.js} +9 -9
- package/dist/{vault-GAIOZZPC.js → vault-VDADZFSV.js} +5 -5
- package/dist/{xmtp-A2KIQWGA.js → xmtp-2U7CSBXM.js} +3 -3
- package/package.json +5 -2
- package/scripts/pin-xmtp-bindings.cjs +26 -10
- package/dist/chunk-5BYJ3TGZ.js.map +0 -1
- /package/dist/{agent-I6AUSTZG.js.map → agent-C5XZSK7Z.js.map} +0 -0
- /package/dist/{chat-5SI7K5FA.js.map → chat-FGTYCJ4B.js.map} +0 -0
- /package/dist/{chunk-ZTR2GPZO.js.map → chunk-4GO7I33U.js.map} +0 -0
- /package/dist/{chunk-UK2G5AZG.js.map → chunk-4WAEM2TC.js.map} +0 -0
- /package/dist/{chunk-N5RIB6ZZ.js.map → chunk-AJ46XIGY.js.map} +0 -0
- /package/dist/{chunk-4BMAFJKO.js.map → chunk-AOL2DJTK.js.map} +0 -0
- /package/dist/{chunk-7ILOT3SI.js.map → chunk-CCNOXDZ7.js.map} +0 -0
- /package/dist/{chunk-MRUTBOMZ.js.map → chunk-G34TNE53.js.map} +0 -0
- /package/dist/{chunk-ZEZQM42M.js.map → chunk-M34JVPJO.js.map} +0 -0
- /package/dist/{chunk-I7NXKJCG.js.map → chunk-MBWFTXF5.js.map} +0 -0
- /package/dist/{chunk-5O3KO7ZI.js.map → chunk-NKRMMFQG.js.map} +0 -0
- /package/dist/{client-Z7LNEG2F.js.map → client-OPZ2ORWW.js.map} +0 -0
- /package/dist/{eas-DWBT332K.js.map → eas-32PEJETG.js.map} +0 -0
- /package/dist/{governor-ZVNTHHML.js.map → governor-GSCXEZWI.js.map} +0 -0
- /package/dist/{price-GEUCSTO4.js.map → price-OUL7WIXQ.js.map} +0 -0
- /package/dist/{providers-PWUJ2AR7.js.map → providers-4HQHB7V5.js.map} +0 -0
- /package/dist/{research-SW6PLO32.js.map → research-2RWAMWWH.js.map} +0 -0
- /package/dist/{session-6RSOG5NE.js.map → session-SIOJZO6P.js.map} +0 -0
- /package/dist/{trade-45JPCKGP.js.map → trade-ZEIY3MCL.js.map} +0 -0
- /package/dist/{vault-GAIOZZPC.js.map → vault-VDADZFSV.js.map} +0 -0
- /package/dist/{xmtp-A2KIQWGA.js.map → xmtp-2U7CSBXM.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
checkApiKeyValid,
|
|
16
16
|
listModels,
|
|
17
17
|
provisionApiKey
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-CCNOXDZ7.js";
|
|
19
19
|
import {
|
|
20
20
|
approveDepositor,
|
|
21
21
|
deposit,
|
|
@@ -38,8 +38,8 @@ import {
|
|
|
38
38
|
removeDepositor,
|
|
39
39
|
setAgentFeeBps,
|
|
40
40
|
setVaultAddress
|
|
41
|
-
} from "./chunk-
|
|
42
|
-
import "./chunk-
|
|
41
|
+
} from "./chunk-AOL2DJTK.js";
|
|
42
|
+
import "./chunk-NKRMMFQG.js";
|
|
43
43
|
import {
|
|
44
44
|
HYPERLIQUID_PERP_STRATEGY_ABI,
|
|
45
45
|
applySlippage,
|
|
@@ -51,7 +51,7 @@ import {
|
|
|
51
51
|
getMultiHopQuote,
|
|
52
52
|
getQuote,
|
|
53
53
|
resolveToken
|
|
54
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-AJ46XIGY.js";
|
|
55
55
|
import {
|
|
56
56
|
createApproval,
|
|
57
57
|
createJoinRequest,
|
|
@@ -62,20 +62,20 @@ import {
|
|
|
62
62
|
queryJoinRequests,
|
|
63
63
|
revokeJoinRequest,
|
|
64
64
|
stripReferrerPrefix
|
|
65
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-M34JVPJO.js";
|
|
66
66
|
import {
|
|
67
67
|
createFund,
|
|
68
68
|
getActiveFunds,
|
|
69
69
|
getFund,
|
|
70
70
|
subdomainExists,
|
|
71
71
|
updateMetadata
|
|
72
|
-
} from "./chunk-
|
|
72
|
+
} from "./chunk-4WAEM2TC.js";
|
|
73
73
|
import {
|
|
74
74
|
resolveFund,
|
|
75
75
|
resolveVaultFund,
|
|
76
76
|
searchFundAcrossChains,
|
|
77
77
|
setTextRecord
|
|
78
|
-
} from "./chunk-
|
|
78
|
+
} from "./chunk-MBWFTXF5.js";
|
|
79
79
|
import {
|
|
80
80
|
PROPOSAL_STATE,
|
|
81
81
|
PROPOSAL_STATES,
|
|
@@ -109,7 +109,7 @@ import {
|
|
|
109
109
|
setVotingPeriod,
|
|
110
110
|
settleProposal,
|
|
111
111
|
vote
|
|
112
|
-
} from "./chunk-
|
|
112
|
+
} from "./chunk-4GO7I33U.js";
|
|
113
113
|
import {
|
|
114
114
|
AERODROME,
|
|
115
115
|
AGENT_REGISTRY,
|
|
@@ -130,7 +130,7 @@ import {
|
|
|
130
130
|
sendTxWithRetry,
|
|
131
131
|
waitForReceipt,
|
|
132
132
|
writeContractWithRetry
|
|
133
|
-
} from "./chunk-
|
|
133
|
+
} from "./chunk-AQPTBVEA.js";
|
|
134
134
|
import {
|
|
135
135
|
DEFAULT_NETWORK,
|
|
136
136
|
VALID_NETWORKS,
|
|
@@ -189,9 +189,9 @@ import {
|
|
|
189
189
|
import { config as loadDotenv } from "dotenv";
|
|
190
190
|
import { createRequire } from "module";
|
|
191
191
|
import { Command, Option } from "commander";
|
|
192
|
-
import { parseUnits as
|
|
193
|
-
import
|
|
194
|
-
import
|
|
192
|
+
import { parseUnits as parseUnits11, formatUnits as formatUnits10, isAddress as isAddress12 } from "viem";
|
|
193
|
+
import chalk18 from "chalk";
|
|
194
|
+
import ora10 from "ora";
|
|
195
195
|
import { input, confirm, select } from "@inquirer/prompts";
|
|
196
196
|
|
|
197
197
|
// src/lib/generated/contract-constants.ts
|
|
@@ -228,11 +228,9 @@ function parseBigIntArg(value, name) {
|
|
|
228
228
|
}
|
|
229
229
|
|
|
230
230
|
// src/commands/strategy-template.ts
|
|
231
|
-
import {
|
|
232
|
-
import
|
|
233
|
-
import
|
|
234
|
-
import { writeFileSync, mkdirSync } from "fs";
|
|
235
|
-
import { resolve } from "path";
|
|
231
|
+
import { formatUnits as formatUnits6, isAddress as isAddress5, erc20Abi as erc20Abi3, encodeAbiParameters as encodeAbiParameters5, decodeAbiParameters as decodeAbiParameters3 } from "viem";
|
|
232
|
+
import chalk4 from "chalk";
|
|
233
|
+
import ora3 from "ora";
|
|
236
234
|
|
|
237
235
|
// src/lib/clone.ts
|
|
238
236
|
import { concat } from "viem";
|
|
@@ -290,13 +288,29 @@ var HYPERLIQUID_GRID_STRATEGY_ABI = [
|
|
|
290
288
|
}
|
|
291
289
|
];
|
|
292
290
|
|
|
293
|
-
// src/lib
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
291
|
+
// src/commands/strategy-template-lib.ts
|
|
292
|
+
import { parseUnits as parseUnits2, formatUnits as formatUnits4, erc20Abi } from "viem";
|
|
293
|
+
import chalk2 from "chalk";
|
|
294
|
+
|
|
295
|
+
// src/lib/proposer-bond-error.ts
|
|
296
|
+
import { formatUnits } from "viem";
|
|
297
|
+
var InsufficientProposerBondWood = class extends Error {
|
|
298
|
+
constructor(requiredWood, balanceWood) {
|
|
299
|
+
const need = formatUnits(requiredWood, 18);
|
|
300
|
+
const have = formatUnits(balanceWood, 18);
|
|
301
|
+
super(
|
|
302
|
+
`InsufficientProposerBondWood: wallet holds ${have} WOOD but the quoted proposer bond is ${need} WOOD. The CLI can set WOOD allowance to ProposerBondEscrow, but the wallet must HOLD the WOOD. On the Robinhood mainnet fork, the faucet grant covers a 10k owner stake plus a small-book bond (example only: ~230 WOOD for a 100 USDG book at a then-current fork price \u2014 quote via ExposureLedger.proposerBondWood; the amount scales). This is a second WOOD pull at propose, not the owner stake.`
|
|
303
|
+
);
|
|
304
|
+
this.requiredWood = requiredWood;
|
|
305
|
+
this.balanceWood = balanceWood;
|
|
306
|
+
}
|
|
307
|
+
name = "InsufficientProposerBondWood";
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
// src/commands/guardian-delegate.ts
|
|
311
|
+
import chalk from "chalk";
|
|
312
|
+
import ora from "ora";
|
|
313
|
+
import { formatUnits as formatUnits3, isAddress as isAddress2, parseUnits } from "viem";
|
|
300
314
|
|
|
301
315
|
// src/lib/calldata.ts
|
|
302
316
|
var _calldataOnly = false;
|
|
@@ -310,11 +324,6 @@ function emitCalldata(action) {
|
|
|
310
324
|
process.stdout.write(JSON.stringify(action, null, 2) + "\n");
|
|
311
325
|
}
|
|
312
326
|
|
|
313
|
-
// src/commands/guardian-delegate.ts
|
|
314
|
-
import chalk from "chalk";
|
|
315
|
-
import ora from "ora";
|
|
316
|
-
import { formatUnits as formatUnits2, isAddress as isAddress2, parseUnits } from "viem";
|
|
317
|
-
|
|
318
327
|
// ../sdk/dist/types.js
|
|
319
328
|
var CHAIN_IDS = {
|
|
320
329
|
BASE: 8453,
|
|
@@ -1349,7 +1358,7 @@ function encodeDeposit(args, chainId) {
|
|
|
1349
1358
|
txs.push(encodeApproveTx(asset, vault, maxUint256, chainId));
|
|
1350
1359
|
}
|
|
1351
1360
|
txs.push(encodeDepositTx(vault, args.assets, receiver, chainId));
|
|
1352
|
-
const minDecimal =
|
|
1361
|
+
const minDecimal = formatUnits2(args.assets, args.assetDecimals);
|
|
1353
1362
|
let description;
|
|
1354
1363
|
if (wrapped) {
|
|
1355
1364
|
description = needsApprove ? `Wrap ETH\u2192WETH, approve ${args.assetSymbol} for ${vault}, then deposit ${minDecimal} ${args.assetSymbol}.` : `Wrap ETH\u2192WETH, then deposit ${minDecimal} ${args.assetSymbol}.`;
|
|
@@ -1380,7 +1389,7 @@ function encodeDeposit(args, chainId) {
|
|
|
1380
1389
|
note: "Both txs are gated by vault state. If `redemptionsLocked()` is true the deposit reverts unless live NAV is available; check the vault read endpoint before broadcasting."
|
|
1381
1390
|
};
|
|
1382
1391
|
}
|
|
1383
|
-
function
|
|
1392
|
+
function formatUnits2(value, decimals) {
|
|
1384
1393
|
if (decimals === 0)
|
|
1385
1394
|
return value.toString();
|
|
1386
1395
|
const negative = value < 0n;
|
|
@@ -2291,8 +2300,8 @@ function registerGuardianCommands(program2) {
|
|
|
2291
2300
|
SEP();
|
|
2292
2301
|
console.log(W(` Address: ${G(target)}`));
|
|
2293
2302
|
console.log(W(` Active guardian: ${active ? G("yes") : DIM("no")}`));
|
|
2294
|
-
console.log(W(` Own stake: ${BOLD(
|
|
2295
|
-
console.log(W(` Delegated inbound: ${BOLD(
|
|
2303
|
+
console.log(W(` Own stake: ${BOLD(formatUnits3(stake, 18))} WOOD`));
|
|
2304
|
+
console.log(W(` Delegated inbound: ${BOLD(formatUnits3(delegatedIn, 18))} WOOD`));
|
|
2296
2305
|
console.log(W(` Commission: ${BOLD(`${Number(commission) / 100}%`)}`));
|
|
2297
2306
|
SEP();
|
|
2298
2307
|
console.log();
|
|
@@ -2487,7 +2496,7 @@ function registerGuardianCommands(program2) {
|
|
|
2487
2496
|
});
|
|
2488
2497
|
}
|
|
2489
2498
|
|
|
2490
|
-
// src/commands/strategy-template.ts
|
|
2499
|
+
// src/commands/strategy-template-lib.ts
|
|
2491
2500
|
var ZERO = "0x0000000000000000000000000000000000000000";
|
|
2492
2501
|
var TEMPLATES = [
|
|
2493
2502
|
{
|
|
@@ -2645,206 +2654,115 @@ function capsForCalls(calls, strategy2, maxCapital) {
|
|
|
2645
2654
|
return seen === 1 ? share + remainder : share;
|
|
2646
2655
|
});
|
|
2647
2656
|
}
|
|
2648
|
-
async function ensureProposerBondAllowance(vault) {
|
|
2657
|
+
async function ensureProposerBondAllowance(vault, requiredCoverage, asset) {
|
|
2649
2658
|
try {
|
|
2659
|
+
const publicClient = getPublicClient();
|
|
2650
2660
|
const governor = await getGovernorOf(vault);
|
|
2651
|
-
const
|
|
2661
|
+
const govViews = [
|
|
2662
|
+
{
|
|
2663
|
+
type: "function",
|
|
2664
|
+
name: "bondEscrow",
|
|
2665
|
+
inputs: [],
|
|
2666
|
+
outputs: [{ type: "address" }],
|
|
2667
|
+
stateMutability: "view"
|
|
2668
|
+
},
|
|
2669
|
+
{
|
|
2670
|
+
type: "function",
|
|
2671
|
+
name: "exposureLedger",
|
|
2672
|
+
inputs: [],
|
|
2673
|
+
outputs: [{ type: "address" }],
|
|
2674
|
+
stateMutability: "view"
|
|
2675
|
+
}
|
|
2676
|
+
];
|
|
2677
|
+
const escrow = await publicClient.readContract({
|
|
2652
2678
|
address: governor,
|
|
2653
|
-
abi:
|
|
2654
|
-
{
|
|
2655
|
-
type: "function",
|
|
2656
|
-
name: "bondEscrow",
|
|
2657
|
-
inputs: [],
|
|
2658
|
-
outputs: [{ type: "address" }],
|
|
2659
|
-
stateMutability: "view"
|
|
2660
|
-
}
|
|
2661
|
-
],
|
|
2679
|
+
abi: govViews,
|
|
2662
2680
|
functionName: "bondEscrow"
|
|
2663
2681
|
});
|
|
2664
|
-
if (!escrow || escrow ===
|
|
2665
|
-
|
|
2666
|
-
await ensureWoodAllowance(2n ** 255n, escrow, "WOOD (proposer bond)");
|
|
2667
|
-
} catch {
|
|
2668
|
-
}
|
|
2669
|
-
}
|
|
2670
|
-
function registerStrategyTemplateCommands(strategy2) {
|
|
2671
|
-
strategy2.command("list").description("List available strategy templates").action(() => {
|
|
2672
|
-
const templates = STRATEGY_TEMPLATES();
|
|
2673
|
-
const network = getNetwork();
|
|
2674
|
-
console.log();
|
|
2675
|
-
console.log(chalk2.bold("Strategy Templates"), chalk2.dim(`(${network})`));
|
|
2676
|
-
console.log(chalk2.dim("\u2500".repeat(60)));
|
|
2677
|
-
let availableCount = 0;
|
|
2678
|
-
for (const t of TEMPLATES) {
|
|
2679
|
-
const addr = templates[t.addressKey];
|
|
2680
|
-
if (addr !== ZERO) {
|
|
2681
|
-
availableCount++;
|
|
2682
|
-
console.log();
|
|
2683
|
-
console.log(` ${chalk2.bold(t.name)} (${chalk2.cyan(t.key)})`);
|
|
2684
|
-
console.log(` ${t.description}`);
|
|
2685
|
-
console.log(` Template: ${chalk2.green(addr)}`);
|
|
2686
|
-
}
|
|
2687
|
-
}
|
|
2688
|
-
if (availableCount === 0) {
|
|
2689
|
-
console.log();
|
|
2690
|
-
console.log(chalk2.yellow(" No strategy templates deployed on this network."));
|
|
2691
|
-
}
|
|
2692
|
-
const unavailable2 = TEMPLATES.filter((t) => templates[t.addressKey] === ZERO);
|
|
2693
|
-
if (unavailable2.length > 0) {
|
|
2694
|
-
console.log();
|
|
2695
|
-
console.log(chalk2.dim(` Not available on ${network}:`));
|
|
2696
|
-
for (const t of unavailable2) {
|
|
2697
|
-
console.log(chalk2.dim(` ${t.name} (${t.key})`));
|
|
2698
|
-
}
|
|
2699
|
-
}
|
|
2700
|
-
console.log();
|
|
2701
|
-
console.log(chalk2.dim("Clone a template: sherwood strategy clone <template> --vault <addr> ..."));
|
|
2702
|
-
console.log(chalk2.dim("Full proposal: sherwood strategy propose <template> --vault <addr> ..."));
|
|
2703
|
-
console.log();
|
|
2704
|
-
});
|
|
2705
|
-
strategy2.command("clone").description("Clone a strategy template and initialize it").argument("<template>", "Template: moonwell-supply, aerodrome-lp, venice-inference, wsteth-moonwell, mamo-yield, portfolio, hyperliquid-perp, hyperliquid-grid").requiredOption("--vault <address>", "Vault address").option("--amount <n>", "Asset amount to deploy").option("--min-redeem <n>", "Min asset on settlement (Moonwell)").option("--token <symbol>", "Asset token symbol (default: USDC)").option("--asset <symbol>", "Asset token (USDC, VVV, or address)").option("--agent <address>", "Agent wallet (Venice, default: your wallet)").option("--min-vvv <n>", "Min VVV from swap (Venice)").option("--single-hop", "Single-hop Aerodrome swap (Venice)").option("--token-a <address>", "Token A (Aerodrome)").option("--token-b <address>", "Token B (Aerodrome)").option("--amount-a <n>", "Token A amount (Aerodrome)").option("--amount-b <n>", "Token B amount (Aerodrome)").option("--stable", "Stable pool (Aerodrome)").option("--gauge <address>", "Gauge address (Aerodrome)").option("--lp-token <address>", "LP token address (Aerodrome)").option("--min-a-out <n>", "Min token A on settle (Aerodrome)").option("--min-b-out <n>", "Min token B on settle (Aerodrome)").option("--slippage <bps>", "Slippage tolerance in bps (wstETH, default: 500 = 5%)").option("--mamo-factory <address>", "Mamo StrategyFactory address (Mamo)").option("--tokens <list>", "Comma-separated token addresses or symbols (Portfolio)").option("--weights <list>", "Comma-separated weights in bps, must sum to 10000 (Portfolio)").option("--max-slippage <bps>", "Max slippage bps (Portfolio, default: 500)").option("--fee-tier <n>", "Pool fee tier (Portfolio, default: 3000)").option("--swap-adapter <address>", "Swap adapter address (Portfolio)").option("--feed-ids <list>", "Comma-separated Chainlink feed ids/addresses per token (Portfolio; auto-resolved from registry in push-feed mode)").option("--price-decimals <list>", "Comma-separated raw feed decimals per token (Portfolio; default 8 push / 18 Data Streams)").option("--swap-routes <list>", "Comma-separated per-token swap route spec (Portfolio). Per token: 'v3:<fee>' (mode 0), 'v4:<fee>:<tickSpacing>' (mode 2), or 'v4path:<hop>+<hop>...' (mode 3) where hop='<currency>@<fee>:<tickSpacing>', currency='eth'/'native'=ETH or 'self'=this token or an address. Empty entry = auto-detect / robinhood default.").option(
|
|
2706
|
-
"--max-drawdown-bps <bps>",
|
|
2707
|
-
"Declared loss allowance for this proposal, in bps of --amount (0-10000, default 2000 = 20%). This is the envelope voters and guardians are asked to accept, and the basis the settle-price guard sizes its tolerance from \u2014 not a formality."
|
|
2708
|
-
).option("--max-price-ages <list>", "Comma-separated per-token max Chainlink push-feed staleness in seconds (Portfolio, push mode; robinhood-mainnet equity feeds default to 345600 = 96h, crypto/stable feeds to the contract default). Empty entry = registry default.").option("--leverage <number>", "Leverage multiplier (Hyperliquid Perp, default: 10)").option("--asset-index <number>", "Perp asset index (Hyperliquid Perp, default: 0 for BTC)").option("--max-position <amount>", "Max position size in USD (Hyperliquid Perp, default: 100000)").option("--max-trades-per-day <n>", "Max trades per day (Hyperliquid Perp, default: 50)").option("--max-order-size <amount>", "Max single-order size in asset units (Hyperliquid Grid, default: 10000)").option("--max-orders-per-tick <n>", "Max orders per tick (Hyperliquid Grid, default: 20)").option("--asset-indices <list>", "Comma-separated perp asset indices (Hyperliquid Grid, default: 0)").action(async (templateKey, opts) => {
|
|
2709
|
-
const vault = opts.vault;
|
|
2710
|
-
if (!isAddress3(vault)) {
|
|
2711
|
-
console.error(chalk2.red("Invalid vault address"));
|
|
2712
|
-
process.exit(1);
|
|
2713
|
-
}
|
|
2714
|
-
const { def, address: templateAddr } = resolveTemplate(templateKey);
|
|
2715
|
-
const cloneSpinner = ora2(`Cloning ${def.name} template...`).start();
|
|
2716
|
-
let clone;
|
|
2717
|
-
let cloneHash;
|
|
2682
|
+
if (!escrow || escrow === ZERO) return;
|
|
2683
|
+
let quotedBond = 0n;
|
|
2718
2684
|
try {
|
|
2719
|
-
const
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
console.log(chalk2.dim(` Tx: ${getExplorerUrl(cloneHash)}`));
|
|
2724
|
-
} catch (err) {
|
|
2725
|
-
cloneSpinner.fail("Clone failed");
|
|
2726
|
-
console.error(chalk2.red(formatContractError(err)));
|
|
2727
|
-
process.exit(1);
|
|
2728
|
-
}
|
|
2729
|
-
const initSpinner = ora2("Initializing strategy...").start();
|
|
2730
|
-
try {
|
|
2731
|
-
const { initData } = await resolveAndBuildInitData(templateKey, opts, vault);
|
|
2732
|
-
const account = getAccount();
|
|
2733
|
-
const initHash = await writeContractWithRetry({
|
|
2734
|
-
account,
|
|
2735
|
-
chain: getChain(),
|
|
2736
|
-
address: clone,
|
|
2737
|
-
abi: BASE_STRATEGY_ABI,
|
|
2738
|
-
functionName: "initialize",
|
|
2739
|
-
args: [vault, account.address, initData]
|
|
2685
|
+
const ledger = await publicClient.readContract({
|
|
2686
|
+
address: governor,
|
|
2687
|
+
abi: govViews,
|
|
2688
|
+
functionName: "exposureLedger"
|
|
2740
2689
|
});
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
address: clone,
|
|
2759
|
-
abi: templateKey === "hyperliquid-grid" ? HYPERLIQUID_GRID_STRATEGY_ABI : HYPERLIQUID_PERP_STRATEGY_ABI,
|
|
2760
|
-
functionName: "finalizeForHyperCore",
|
|
2761
|
-
args: [0n, 1, 0n]
|
|
2762
|
-
// token=USDC, variant=FirstStorageSlot(1), nonce unused
|
|
2690
|
+
if (ledger && ledger !== ZERO && requiredCoverage > 0n) {
|
|
2691
|
+
quotedBond = await publicClient.readContract({
|
|
2692
|
+
address: ledger,
|
|
2693
|
+
abi: [
|
|
2694
|
+
{
|
|
2695
|
+
type: "function",
|
|
2696
|
+
name: "proposerBondWood",
|
|
2697
|
+
inputs: [
|
|
2698
|
+
{ name: "asset", type: "address" },
|
|
2699
|
+
{ name: "requiredCoverage", type: "uint256" }
|
|
2700
|
+
],
|
|
2701
|
+
outputs: [{ type: "uint256" }],
|
|
2702
|
+
stateMutability: "view"
|
|
2703
|
+
}
|
|
2704
|
+
],
|
|
2705
|
+
functionName: "proposerBondWood",
|
|
2706
|
+
args: [asset, requiredCoverage]
|
|
2763
2707
|
});
|
|
2764
|
-
await waitForReceipt(hcHash);
|
|
2765
|
-
hcSpinner.succeed("HyperCore registration sent (active after next block)");
|
|
2766
|
-
} catch (err) {
|
|
2767
|
-
hcSpinner.warn("HyperCore registration failed \u2014 run finalizeForHyperCore manually");
|
|
2768
|
-
console.error(chalk2.yellow(formatContractError(err)));
|
|
2769
2708
|
}
|
|
2709
|
+
} catch {
|
|
2770
2710
|
}
|
|
2771
|
-
console.log(
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
}
|
|
2786
|
-
if (!isAddress3(vault)) {
|
|
2787
|
-
console.error(chalk2.red("Invalid vault address"));
|
|
2788
|
-
process.exit(1);
|
|
2789
|
-
}
|
|
2790
|
-
resolveTemplate(templateKey);
|
|
2791
|
-
const publicClient = getPublicClient();
|
|
2792
|
-
const currentVault = await publicClient.readContract({
|
|
2793
|
-
address: clone,
|
|
2794
|
-
abi: BASE_STRATEGY_ABI,
|
|
2795
|
-
functionName: "vault"
|
|
2796
|
-
});
|
|
2797
|
-
if (currentVault !== "0x0000000000000000000000000000000000000000") {
|
|
2798
|
-
console.error(chalk2.red(`Clone already initialized (vault: ${currentVault})`));
|
|
2799
|
-
process.exit(1);
|
|
2800
|
-
}
|
|
2801
|
-
const initSpinner = ora2("Initializing strategy clone...").start();
|
|
2802
|
-
try {
|
|
2803
|
-
const { initData } = await resolveAndBuildInitData(templateKey, opts, vault);
|
|
2804
|
-
const account = getAccount();
|
|
2805
|
-
const initHash = await writeContractWithRetry({
|
|
2806
|
-
account,
|
|
2807
|
-
chain: getChain(),
|
|
2808
|
-
address: clone,
|
|
2809
|
-
abi: BASE_STRATEGY_ABI,
|
|
2810
|
-
functionName: "initialize",
|
|
2811
|
-
args: [vault, account.address, initData]
|
|
2711
|
+
console.log(
|
|
2712
|
+
chalk2.dim(
|
|
2713
|
+
` Quoted proposer bond: ${formatUnits4(quotedBond, 18)} WOOD (via ExposureLedger.proposerBondWood; ~1% of extractable value at default proposerBondBps, WOOD-denominated and price-dependent \u2014 not a fixed WOOD amount)`
|
|
2714
|
+
)
|
|
2715
|
+
);
|
|
2716
|
+
console.log(chalk2.dim(` Proposer bond escrow: ${escrow} \u2014 ensuring WOOD allowance`));
|
|
2717
|
+
if (quotedBond > 0n) {
|
|
2718
|
+
const wood2 = SHERWOOD().WOOD_TOKEN;
|
|
2719
|
+
const holder = getAccount().address;
|
|
2720
|
+
const balance = await publicClient.readContract({
|
|
2721
|
+
address: wood2,
|
|
2722
|
+
abi: erc20Abi,
|
|
2723
|
+
functionName: "balanceOf",
|
|
2724
|
+
args: [holder]
|
|
2812
2725
|
});
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
throw new Error("Initialize transaction reverted on-chain");
|
|
2726
|
+
if (balance < quotedBond) {
|
|
2727
|
+
throw new InsufficientProposerBondWood(quotedBond, balance);
|
|
2816
2728
|
}
|
|
2817
|
-
initSpinner.succeed("Initialized");
|
|
2818
|
-
console.log(chalk2.dim(` Tx: ${getExplorerUrl(initHash)}`));
|
|
2819
|
-
} catch (err) {
|
|
2820
|
-
initSpinner.fail("Initialize failed");
|
|
2821
|
-
console.error(chalk2.red(formatContractError(err)));
|
|
2822
|
-
process.exit(1);
|
|
2823
2729
|
}
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2730
|
+
await ensureWoodAllowance(2n ** 255n, escrow, "WOOD (proposer bond)");
|
|
2731
|
+
} catch (err) {
|
|
2732
|
+
if (err instanceof InsufficientProposerBondWood) throw err;
|
|
2733
|
+
}
|
|
2734
|
+
}
|
|
2735
|
+
|
|
2736
|
+
// src/commands/strategy-propose.ts
|
|
2737
|
+
import { formatUnits as formatUnits5, isAddress as isAddress4, erc20Abi as erc20Abi2 } from "viem";
|
|
2738
|
+
import chalk3 from "chalk";
|
|
2739
|
+
import ora2 from "ora";
|
|
2740
|
+
import { writeFileSync, mkdirSync } from "fs";
|
|
2741
|
+
import { resolve } from "path";
|
|
2742
|
+
|
|
2743
|
+
// src/lib/batch.ts
|
|
2744
|
+
function formatBatch(calls) {
|
|
2745
|
+
return calls.map((call, i) => {
|
|
2746
|
+
const selector = call.data.slice(0, 10);
|
|
2747
|
+
return ` ${i + 1}. ${call.target} :: ${selector}... (${call.value > 0n ? call.value + " wei" : "no value"})`;
|
|
2748
|
+
}).join("\n");
|
|
2749
|
+
}
|
|
2750
|
+
|
|
2751
|
+
// src/commands/strategy-propose.ts
|
|
2752
|
+
function registerStrategyProposeCommands(strategy2) {
|
|
2835
2753
|
strategy2.command("propose").description("Clone + init + build calls + submit governance proposal (all-in-one)").argument("<template>", "Template: moonwell-supply, aerodrome-lp, venice-inference, wsteth-moonwell, mamo-yield, portfolio, hyperliquid-perp, hyperliquid-grid").requiredOption("--vault <address>", "Vault address").option("--write-calls <dir>", "Write execute/settle JSON to directory (skip proposal submission)").option("--name <name>", "Proposal name").option("--description <text>", "Proposal description").option("--duration <duration>", "Strategy duration (7d, 24h, etc.)").option("--proposer <address>", "Proposer/agent address (required for --calldata-only \u2014 the external signer)").option("--metadata-uri <uri>", "Metadata URI (required for --calldata-only \u2014 IPFS pin needs a signer)").option("--salt <bytes32>", "CREATE2 salt for the deterministic clone (--calldata-only; default: derived from inputs)").option("--amount <n>", "Asset amount to deploy").option("--min-redeem <n>", "Min asset on settlement (Moonwell)").option("--token <symbol>", "Asset token symbol (default: USDC)").option("--asset <symbol>", "Asset token (USDC, VVV, or address)").option("--agent <address>", "Agent wallet (Venice, default: your wallet)").option("--min-vvv <n>", "Min VVV from swap (Venice)").option("--single-hop", "Single-hop Aerodrome swap (Venice)").option("--token-a <address>", "Token A (Aerodrome)").option("--token-b <address>", "Token B (Aerodrome)").option("--amount-a <n>", "Token A amount (Aerodrome)").option("--amount-b <n>", "Token B amount (Aerodrome)").option("--stable", "Stable pool (Aerodrome)").option("--gauge <address>", "Gauge address (Aerodrome)").option("--lp-token <address>", "LP token address (Aerodrome)").option("--min-a-out <n>", "Min token A on settle (Aerodrome)").option("--min-b-out <n>", "Min token B on settle (Aerodrome)").option("--slippage <bps>", "Slippage tolerance in bps (wstETH, default: 500 = 5%)").option("--mamo-factory <address>", "Mamo StrategyFactory address (Mamo)").option("--tokens <list>", "Comma-separated token addresses or symbols (Portfolio)").option("--weights <list>", "Comma-separated weights in bps, must sum to 10000 (Portfolio)").option("--max-slippage <bps>", "Max slippage bps (Portfolio, default: 500)").option("--fee-tier <n>", "Pool fee tier (Portfolio, default: 3000)").option("--swap-adapter <address>", "Swap adapter address (Portfolio)").option("--feed-ids <list>", "Comma-separated Chainlink feed ids/addresses per token (Portfolio; auto-resolved from registry in push-feed mode)").option("--price-decimals <list>", "Comma-separated raw feed decimals per token (Portfolio; default 8 push / 18 Data Streams)").option("--swap-routes <list>", "Comma-separated per-token swap route spec (Portfolio). Per token: 'v3:<fee>' (mode 0), 'v4:<fee>:<tickSpacing>' (mode 2), or 'v4path:<hop>+<hop>...' (mode 3) where hop='<currency>@<fee>:<tickSpacing>', currency='eth'/'native'=ETH or 'self'=this token or an address. Empty entry = auto-detect / robinhood default.").option(
|
|
2836
2754
|
"--max-drawdown-bps <bps>",
|
|
2837
2755
|
"Declared loss allowance for this proposal, in bps of --amount (0-10000, default 2000 = 20%). This is the envelope voters and guardians are asked to accept, and the basis the settle-price guard sizes its tolerance from \u2014 not a formality."
|
|
2838
2756
|
).option("--max-price-ages <list>", "Comma-separated per-token max Chainlink push-feed staleness in seconds (Portfolio, push mode; robinhood-mainnet equity feeds default to 345600 = 96h, crypto/stable feeds to the contract default). Empty entry = registry default.").option("--leverage <number>", "Leverage multiplier (Hyperliquid Perp, default: 10)").option("--asset-index <number>", "Perp asset index (Hyperliquid Perp, default: 0 for BTC)").option("--max-position <amount>", "Max position size in USD (Hyperliquid Perp, default: 100000)").option("--max-trades-per-day <n>", "Max trades per day (Hyperliquid Perp, default: 50)").option("--max-order-size <amount>", "Max single-order size in asset units (Hyperliquid Grid, default: 10000)").option("--max-orders-per-tick <n>", "Max orders per tick (Hyperliquid Grid, default: 20)").option("--asset-indices <list>", "Comma-separated perp asset indices (Hyperliquid Grid, default: 0)").action(async (templateKey, opts) => {
|
|
2839
2757
|
const vault = opts.vault;
|
|
2840
|
-
if (!
|
|
2841
|
-
console.error(
|
|
2758
|
+
if (!isAddress4(vault)) {
|
|
2759
|
+
console.error(chalk3.red("Invalid vault address"));
|
|
2842
2760
|
process.exit(1);
|
|
2843
2761
|
}
|
|
2844
2762
|
const { def, address: templateAddr } = resolveTemplate(templateKey);
|
|
2845
2763
|
const maxDrawdownBps = opts.maxDrawdownBps === void 0 ? 2e3 : Number(opts.maxDrawdownBps);
|
|
2846
2764
|
if (!Number.isInteger(maxDrawdownBps) || maxDrawdownBps < 0 || maxDrawdownBps > 1e4) {
|
|
2847
|
-
console.error(
|
|
2765
|
+
console.error(chalk3.red(` --max-drawdown-bps must be an integer 0-10000 (got ${opts.maxDrawdownBps})`));
|
|
2848
2766
|
process.exit(1);
|
|
2849
2767
|
}
|
|
2850
2768
|
if (isCalldataOnly()) {
|
|
@@ -2853,12 +2771,12 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
2853
2771
|
if (!opts.metadataUri) missing.push("--metadata-uri <uri> (IPFS pin needs a signer)");
|
|
2854
2772
|
if (!opts.duration) missing.push("--duration <d>");
|
|
2855
2773
|
if (missing.length > 0) {
|
|
2856
|
-
console.error(
|
|
2774
|
+
console.error(chalk3.red(`--calldata-only requires: ${missing.join(", ")}`));
|
|
2857
2775
|
process.exit(1);
|
|
2858
2776
|
}
|
|
2859
2777
|
const proposer = opts.proposer;
|
|
2860
|
-
if (!
|
|
2861
|
-
console.error(
|
|
2778
|
+
if (!isAddress4(proposer)) {
|
|
2779
|
+
console.error(chalk3.red("Invalid --proposer address"));
|
|
2862
2780
|
process.exit(1);
|
|
2863
2781
|
}
|
|
2864
2782
|
try {
|
|
@@ -2878,15 +2796,15 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
2878
2796
|
]);
|
|
2879
2797
|
if (!isRegistered) {
|
|
2880
2798
|
console.error(
|
|
2881
|
-
|
|
2799
|
+
chalk3.red(
|
|
2882
2800
|
`--proposer ${proposer} is not a registered agent on ${vault}; cloneAndInitDeterministic and propose will both revert.`
|
|
2883
2801
|
)
|
|
2884
2802
|
);
|
|
2885
|
-
console.error(
|
|
2803
|
+
console.error(chalk3.yellow(` Register first: sherwood fund approve --wallet ${proposer}`));
|
|
2886
2804
|
process.exit(1);
|
|
2887
2805
|
}
|
|
2888
2806
|
if (paused) {
|
|
2889
|
-
console.error(
|
|
2807
|
+
console.error(chalk3.red(`Vault ${vault} is paused \u2014 the proposal would revert.`));
|
|
2890
2808
|
process.exit(1);
|
|
2891
2809
|
}
|
|
2892
2810
|
for (const check of computeProposalAssetChecks(templateKey, opts)) {
|
|
@@ -2894,32 +2812,32 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
2894
2812
|
const [balance, decimals] = await Promise.all([
|
|
2895
2813
|
publicClient.readContract({
|
|
2896
2814
|
address: check.asset,
|
|
2897
|
-
abi:
|
|
2815
|
+
abi: erc20Abi2,
|
|
2898
2816
|
functionName: "balanceOf",
|
|
2899
2817
|
args: [vault]
|
|
2900
2818
|
}),
|
|
2901
2819
|
publicClient.readContract({
|
|
2902
2820
|
address: check.asset,
|
|
2903
|
-
abi:
|
|
2821
|
+
abi: erc20Abi2,
|
|
2904
2822
|
functionName: "decimals"
|
|
2905
2823
|
})
|
|
2906
2824
|
]);
|
|
2907
2825
|
if (balance < check.amount) {
|
|
2908
2826
|
console.error(
|
|
2909
|
-
|
|
2910
|
-
`Vault ${vault} holds ${
|
|
2827
|
+
chalk3.red(
|
|
2828
|
+
`Vault ${vault} holds ${formatUnits5(balance, decimals)} ${check.label} but the proposal needs ${formatUnits5(check.amount, decimals)} ${check.label}.`
|
|
2911
2829
|
)
|
|
2912
2830
|
);
|
|
2913
|
-
console.error(
|
|
2831
|
+
console.error(chalk3.yellow(` Hint: --amount is in human units (e.g. "10" = 10 ${check.label}), not raw decimals.`));
|
|
2914
2832
|
process.exit(1);
|
|
2915
2833
|
}
|
|
2916
2834
|
}
|
|
2917
2835
|
} catch (err) {
|
|
2918
|
-
console.error(
|
|
2836
|
+
console.error(chalk3.red(`Preflight read failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
2919
2837
|
process.exit(1);
|
|
2920
2838
|
}
|
|
2921
2839
|
if (!opts.agent) opts.agent = proposer;
|
|
2922
|
-
const { parseDuration: parseDuration3, getGovernorOf: getGovernorOf2 } = await import("./governor-
|
|
2840
|
+
const { parseDuration: parseDuration3, getGovernorOf: getGovernorOf2 } = await import("./governor-GSCXEZWI.js");
|
|
2923
2841
|
const strategyDuration2 = parseDuration3(opts.duration);
|
|
2924
2842
|
const chainId = getChain().id;
|
|
2925
2843
|
try {
|
|
@@ -2970,7 +2888,7 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
2970
2888
|
note: "Broadcast SEQUENTIALLY: send tx 1 (cloneAndInitDeterministic) and WAIT for it to confirm, then send tx 2 (propose). If tx 1 reverts \u2014 proposer not a registered agent, proposer != tx-1 signer, template not allowlisted, or vault unregistered \u2014 do NOT send tx 2: it would create a proposal referencing an undeployed strategy. Send tx 1 from the --proposer wallet (the factory requires proposer == msg.sender, and msg.sender must be a vault owner/agent). The clone address is pinned by (factory, template, vault, salt) and is already baked into tx 2 + the execute/settle calls. The factory binds the salt to the vault and only the vault's owner/agents may deploy there, so the predicted address can't be front-run."
|
|
2971
2889
|
});
|
|
2972
2890
|
} catch (err) {
|
|
2973
|
-
console.error(
|
|
2891
|
+
console.error(chalk3.red(err instanceof Error ? err.message : String(err)));
|
|
2974
2892
|
process.exit(1);
|
|
2975
2893
|
}
|
|
2976
2894
|
return;
|
|
@@ -2988,7 +2906,7 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
2988
2906
|
if (!isRegistered) {
|
|
2989
2907
|
preflightSpinner.fail("Preflight failed");
|
|
2990
2908
|
console.error(
|
|
2991
|
-
|
|
2909
|
+
chalk3.red(
|
|
2992
2910
|
` Signer ${account.address} is not a registered agent on ${vault}.
|
|
2993
2911
|
Register first via: sherwood fund approve --wallet ${account.address}`
|
|
2994
2912
|
)
|
|
@@ -3002,7 +2920,7 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
3002
2920
|
});
|
|
3003
2921
|
if (paused) {
|
|
3004
2922
|
preflightSpinner.fail("Preflight failed");
|
|
3005
|
-
console.error(
|
|
2923
|
+
console.error(chalk3.red(` Vault ${vault} is paused. Cannot propose.`));
|
|
3006
2924
|
process.exit(1);
|
|
3007
2925
|
}
|
|
3008
2926
|
const checks = computeProposalAssetChecks(templateKey, opts);
|
|
@@ -3011,25 +2929,25 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
3011
2929
|
const [balance, decimals] = await Promise.all([
|
|
3012
2930
|
publicClient.readContract({
|
|
3013
2931
|
address: check.asset,
|
|
3014
|
-
abi:
|
|
2932
|
+
abi: erc20Abi2,
|
|
3015
2933
|
functionName: "balanceOf",
|
|
3016
2934
|
args: [vault]
|
|
3017
2935
|
}),
|
|
3018
2936
|
publicClient.readContract({
|
|
3019
2937
|
address: check.asset,
|
|
3020
|
-
abi:
|
|
2938
|
+
abi: erc20Abi2,
|
|
3021
2939
|
functionName: "decimals"
|
|
3022
2940
|
})
|
|
3023
2941
|
]);
|
|
3024
2942
|
if (balance < check.amount) {
|
|
3025
2943
|
preflightSpinner.fail("Preflight failed");
|
|
3026
2944
|
console.error(
|
|
3027
|
-
|
|
3028
|
-
` Vault ${vault} holds ${
|
|
2945
|
+
chalk3.red(
|
|
2946
|
+
` Vault ${vault} holds ${formatUnits5(balance, decimals)} ${check.label} but proposal requires ${formatUnits5(check.amount, decimals)} ${check.label}.`
|
|
3029
2947
|
)
|
|
3030
2948
|
);
|
|
3031
2949
|
console.error(
|
|
3032
|
-
|
|
2950
|
+
chalk3.yellow(
|
|
3033
2951
|
` Hint: --amount is in human units (e.g. "10" = 10 ${check.label}), not raw decimals.`
|
|
3034
2952
|
)
|
|
3035
2953
|
);
|
|
@@ -3039,7 +2957,7 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
3039
2957
|
preflightSpinner.succeed("Preflight OK");
|
|
3040
2958
|
} catch (err) {
|
|
3041
2959
|
preflightSpinner.fail("Preflight failed");
|
|
3042
|
-
console.error(
|
|
2960
|
+
console.error(chalk3.red(formatContractError(err)));
|
|
3043
2961
|
process.exit(1);
|
|
3044
2962
|
}
|
|
3045
2963
|
const cloneSpinner = ora2(`Cloning ${def.name} template...`).start();
|
|
@@ -3047,11 +2965,11 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
3047
2965
|
try {
|
|
3048
2966
|
const result = await cloneTemplate(templateAddr);
|
|
3049
2967
|
clone = result.clone;
|
|
3050
|
-
cloneSpinner.succeed(`Cloned: ${
|
|
3051
|
-
console.log(
|
|
2968
|
+
cloneSpinner.succeed(`Cloned: ${chalk3.green(clone)}`);
|
|
2969
|
+
console.log(chalk3.dim(` Tx: ${getExplorerUrl(result.hash)}`));
|
|
3052
2970
|
} catch (err) {
|
|
3053
2971
|
cloneSpinner.fail("Clone failed");
|
|
3054
|
-
console.error(
|
|
2972
|
+
console.error(chalk3.red(formatContractError(err)));
|
|
3055
2973
|
process.exit(1);
|
|
3056
2974
|
}
|
|
3057
2975
|
const initSpinner = ora2("Initializing strategy...").start();
|
|
@@ -3078,7 +2996,7 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
3078
2996
|
initSpinner.succeed("Initialized");
|
|
3079
2997
|
} catch (err) {
|
|
3080
2998
|
initSpinner.fail("Initialize failed");
|
|
3081
|
-
console.error(
|
|
2999
|
+
console.error(chalk3.red(formatContractError(err)));
|
|
3082
3000
|
process.exit(1);
|
|
3083
3001
|
}
|
|
3084
3002
|
if (templateKey === "hyperliquid-grid" || templateKey === "hyperliquid-perp") {
|
|
@@ -3097,9 +3015,9 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
3097
3015
|
hcSpinner.succeed("HyperCore registration sent (active after next block)");
|
|
3098
3016
|
} catch (err) {
|
|
3099
3017
|
hcSpinner.fail("HyperCore registration failed \u2014 aborting proposal submission");
|
|
3100
|
-
console.error(
|
|
3018
|
+
console.error(chalk3.red(formatContractError(err)));
|
|
3101
3019
|
console.error(
|
|
3102
|
-
|
|
3020
|
+
chalk3.yellow(
|
|
3103
3021
|
`Retry by calling finalizeForHyperCore(0, 1, 0) on ${clone}, then re-run propose.`
|
|
3104
3022
|
)
|
|
3105
3023
|
);
|
|
@@ -3114,9 +3032,9 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
3114
3032
|
extraApprovals
|
|
3115
3033
|
);
|
|
3116
3034
|
console.log();
|
|
3117
|
-
console.log(
|
|
3035
|
+
console.log(chalk3.bold(`Execute calls (${executeCalls.length}):`));
|
|
3118
3036
|
console.log(formatBatch(executeCalls));
|
|
3119
|
-
console.log(
|
|
3037
|
+
console.log(chalk3.bold(`Settle calls (${settleCalls.length}):`));
|
|
3120
3038
|
console.log(formatBatch(settleCalls));
|
|
3121
3039
|
if (opts.writeCalls) {
|
|
3122
3040
|
const dir = resolve(opts.writeCalls);
|
|
@@ -3126,33 +3044,33 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
3126
3044
|
writeFileSync(execPath, serializeCalls(executeCalls));
|
|
3127
3045
|
writeFileSync(settlePath, serializeCalls(settleCalls));
|
|
3128
3046
|
console.log();
|
|
3129
|
-
console.log(
|
|
3130
|
-
console.log(
|
|
3131
|
-
console.log(
|
|
3047
|
+
console.log(chalk3.green(`Execute calls: ${execPath}`));
|
|
3048
|
+
console.log(chalk3.green(`Settle calls: ${settlePath}`));
|
|
3049
|
+
console.log(chalk3.green(`Clone address: ${clone}`));
|
|
3132
3050
|
console.log();
|
|
3133
|
-
console.log(
|
|
3134
|
-
console.log(
|
|
3135
|
-
console.log(
|
|
3136
|
-
console.log(
|
|
3137
|
-
console.log(
|
|
3138
|
-
console.log(
|
|
3139
|
-
console.log(
|
|
3051
|
+
console.log(chalk3.dim("Submit with:"));
|
|
3052
|
+
console.log(chalk3.dim(` sherwood proposal create \\`));
|
|
3053
|
+
console.log(chalk3.dim(` --vault ${vault} \\`));
|
|
3054
|
+
console.log(chalk3.dim(` --name "..." --description "..." \\`));
|
|
3055
|
+
console.log(chalk3.dim(` --duration 7d \\`));
|
|
3056
|
+
console.log(chalk3.dim(` --execute-calls ${execPath} \\`));
|
|
3057
|
+
console.log(chalk3.dim(` --settle-calls ${settlePath}`));
|
|
3140
3058
|
if (templateKey === "venice-inference") {
|
|
3141
3059
|
console.log();
|
|
3142
|
-
console.log(
|
|
3143
|
-
console.log(
|
|
3144
|
-
console.log(
|
|
3060
|
+
console.log(chalk3.yellow("Reminder: before settlement, agent must approve repayment:"));
|
|
3061
|
+
console.log(chalk3.yellow(` asset.approve(${clone}, <repaymentAmount>)`));
|
|
3062
|
+
console.log(chalk3.yellow(" Agent can update repayment via strategy.updateParams(newRepayment, 0, 0)"));
|
|
3145
3063
|
}
|
|
3146
3064
|
console.log();
|
|
3147
3065
|
return;
|
|
3148
3066
|
}
|
|
3149
3067
|
if (!opts.name || !opts.duration) {
|
|
3150
|
-
console.error(
|
|
3068
|
+
console.error(chalk3.red("Missing --name or --duration. Use --write-calls to skip proposal submission."));
|
|
3151
3069
|
process.exit(1);
|
|
3152
3070
|
}
|
|
3153
|
-
const { propose: propose2 } = await import("./governor-
|
|
3071
|
+
const { propose: propose2 } = await import("./governor-GSCXEZWI.js");
|
|
3154
3072
|
const { pinJSON } = await import("./ipfs-EJ7IHXCU.js");
|
|
3155
|
-
const { parseDuration: parseDuration2 } = await import("./governor-
|
|
3073
|
+
const { parseDuration: parseDuration2 } = await import("./governor-GSCXEZWI.js");
|
|
3156
3074
|
const strategyDuration = parseDuration2(opts.duration);
|
|
3157
3075
|
const metaSpinner = ora2("Pinning metadata to IPFS...").start();
|
|
3158
3076
|
let metadataURI;
|
|
@@ -3184,16 +3102,24 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
3184
3102
|
metadataURI = `data:application/json;base64,${Buffer.from(inline).toString("base64")}`;
|
|
3185
3103
|
}
|
|
3186
3104
|
try {
|
|
3187
|
-
const { getAgentFeeBps: getAgentFeeBps2 } = await import("./vault-
|
|
3105
|
+
const { getAgentFeeBps: getAgentFeeBps2 } = await import("./vault-VDADZFSV.js");
|
|
3188
3106
|
const feeBps = await getAgentFeeBps2(vault);
|
|
3189
3107
|
console.log(
|
|
3190
|
-
|
|
3108
|
+
chalk3.yellow(
|
|
3191
3109
|
` Agent fee for this proposal: ${Number(feeBps) / 100}% (the vault's current agentFeeBps). Run \`sherwood fund set-agent-fee --bps <bps>\` first to change it.`
|
|
3192
3110
|
)
|
|
3193
3111
|
);
|
|
3194
3112
|
} catch {
|
|
3195
3113
|
}
|
|
3196
|
-
|
|
3114
|
+
try {
|
|
3115
|
+
await ensureProposerBondAllowance(vault, assetAmount, asset);
|
|
3116
|
+
} catch (err) {
|
|
3117
|
+
if (err instanceof InsufficientProposerBondWood) {
|
|
3118
|
+
console.error(chalk3.red(err.message));
|
|
3119
|
+
process.exit(1);
|
|
3120
|
+
}
|
|
3121
|
+
throw err;
|
|
3122
|
+
}
|
|
3197
3123
|
const proposeSpinner = ora2("Submitting proposal...").start();
|
|
3198
3124
|
try {
|
|
3199
3125
|
const executeCallCaps = capsForCalls(executeCalls, clone, assetAmount);
|
|
@@ -3211,33 +3137,202 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
3211
3137
|
);
|
|
3212
3138
|
proposeSpinner.succeed(`Proposal #${proposalId} created \u2014 strategy=${clone} set on proposal`);
|
|
3213
3139
|
console.log(`Proposal #${proposalId}`);
|
|
3214
|
-
console.log(
|
|
3215
|
-
console.log(
|
|
3140
|
+
console.log(chalk3.dim(` Tx: ${getExplorerUrl(hash)}`));
|
|
3141
|
+
console.log(chalk3.dim(` Clone: ${clone}`));
|
|
3216
3142
|
} catch (err) {
|
|
3217
3143
|
proposeSpinner.fail("Proposal failed");
|
|
3218
|
-
console.error(
|
|
3144
|
+
console.error(chalk3.red(formatContractError(err)));
|
|
3219
3145
|
process.exit(1);
|
|
3220
3146
|
}
|
|
3221
3147
|
if (templateKey === "venice-inference") {
|
|
3222
3148
|
console.log();
|
|
3223
|
-
console.log(
|
|
3224
|
-
console.log(
|
|
3225
|
-
console.log(
|
|
3226
|
-
console.log(
|
|
3227
|
-
console.log(
|
|
3149
|
+
console.log(chalk3.yellow("Next steps:"));
|
|
3150
|
+
console.log(chalk3.yellow(" 1. After execution: sherwood venice provision"));
|
|
3151
|
+
console.log(chalk3.yellow(" 2. Use inference: sherwood venice infer --model <id> --prompt '...'"));
|
|
3152
|
+
console.log(chalk3.yellow(" 3. Before settlement: approve repayment (principal + profit):"));
|
|
3153
|
+
console.log(chalk3.yellow(` asset.approve(${clone}, <repaymentAmount>)`));
|
|
3154
|
+
}
|
|
3155
|
+
console.log();
|
|
3156
|
+
});
|
|
3157
|
+
}
|
|
3158
|
+
|
|
3159
|
+
// src/commands/strategy-template.ts
|
|
3160
|
+
function registerStrategyTemplateCommands(strategy2) {
|
|
3161
|
+
strategy2.command("list").description("List available strategy templates").action(() => {
|
|
3162
|
+
const templates = STRATEGY_TEMPLATES();
|
|
3163
|
+
const network = getNetwork();
|
|
3164
|
+
console.log();
|
|
3165
|
+
console.log(chalk4.bold("Strategy Templates"), chalk4.dim(`(${network})`));
|
|
3166
|
+
console.log(chalk4.dim("\u2500".repeat(60)));
|
|
3167
|
+
let availableCount = 0;
|
|
3168
|
+
for (const t of TEMPLATES) {
|
|
3169
|
+
const addr = templates[t.addressKey];
|
|
3170
|
+
if (addr !== ZERO) {
|
|
3171
|
+
availableCount++;
|
|
3172
|
+
console.log();
|
|
3173
|
+
console.log(` ${chalk4.bold(t.name)} (${chalk4.cyan(t.key)})`);
|
|
3174
|
+
console.log(` ${t.description}`);
|
|
3175
|
+
console.log(` Template: ${chalk4.green(addr)}`);
|
|
3176
|
+
}
|
|
3177
|
+
}
|
|
3178
|
+
if (availableCount === 0) {
|
|
3179
|
+
console.log();
|
|
3180
|
+
console.log(chalk4.yellow(" No strategy templates deployed on this network."));
|
|
3181
|
+
}
|
|
3182
|
+
const unavailable2 = TEMPLATES.filter((t) => templates[t.addressKey] === ZERO);
|
|
3183
|
+
if (unavailable2.length > 0) {
|
|
3184
|
+
console.log();
|
|
3185
|
+
console.log(chalk4.dim(` Not available on ${network}:`));
|
|
3186
|
+
for (const t of unavailable2) {
|
|
3187
|
+
console.log(chalk4.dim(` ${t.name} (${t.key})`));
|
|
3188
|
+
}
|
|
3228
3189
|
}
|
|
3229
3190
|
console.log();
|
|
3191
|
+
console.log(chalk4.dim("Clone a template: sherwood strategy clone <template> --vault <addr> ..."));
|
|
3192
|
+
console.log(chalk4.dim("Full proposal: sherwood strategy propose <template> --vault <addr> ..."));
|
|
3193
|
+
console.log();
|
|
3230
3194
|
});
|
|
3195
|
+
strategy2.command("clone").description("Clone a strategy template and initialize it").argument("<template>", "Template: moonwell-supply, aerodrome-lp, venice-inference, wsteth-moonwell, mamo-yield, portfolio, hyperliquid-perp, hyperliquid-grid").requiredOption("--vault <address>", "Vault address").option("--amount <n>", "Asset amount to deploy").option("--min-redeem <n>", "Min asset on settlement (Moonwell)").option("--token <symbol>", "Asset token symbol (default: USDC)").option("--asset <symbol>", "Asset token (USDC, VVV, or address)").option("--agent <address>", "Agent wallet (Venice, default: your wallet)").option("--min-vvv <n>", "Min VVV from swap (Venice)").option("--single-hop", "Single-hop Aerodrome swap (Venice)").option("--token-a <address>", "Token A (Aerodrome)").option("--token-b <address>", "Token B (Aerodrome)").option("--amount-a <n>", "Token A amount (Aerodrome)").option("--amount-b <n>", "Token B amount (Aerodrome)").option("--stable", "Stable pool (Aerodrome)").option("--gauge <address>", "Gauge address (Aerodrome)").option("--lp-token <address>", "LP token address (Aerodrome)").option("--min-a-out <n>", "Min token A on settle (Aerodrome)").option("--min-b-out <n>", "Min token B on settle (Aerodrome)").option("--slippage <bps>", "Slippage tolerance in bps (wstETH, default: 500 = 5%)").option("--mamo-factory <address>", "Mamo StrategyFactory address (Mamo)").option("--tokens <list>", "Comma-separated token addresses or symbols (Portfolio)").option("--weights <list>", "Comma-separated weights in bps, must sum to 10000 (Portfolio)").option("--max-slippage <bps>", "Max slippage bps (Portfolio, default: 500)").option("--fee-tier <n>", "Pool fee tier (Portfolio, default: 3000)").option("--swap-adapter <address>", "Swap adapter address (Portfolio)").option("--feed-ids <list>", "Comma-separated Chainlink feed ids/addresses per token (Portfolio; auto-resolved from registry in push-feed mode)").option("--price-decimals <list>", "Comma-separated raw feed decimals per token (Portfolio; default 8 push / 18 Data Streams)").option("--swap-routes <list>", "Comma-separated per-token swap route spec (Portfolio). Per token: 'v3:<fee>' (mode 0), 'v4:<fee>:<tickSpacing>' (mode 2), or 'v4path:<hop>+<hop>...' (mode 3) where hop='<currency>@<fee>:<tickSpacing>', currency='eth'/'native'=ETH or 'self'=this token or an address. Empty entry = auto-detect / robinhood default.").option(
|
|
3196
|
+
"--max-drawdown-bps <bps>",
|
|
3197
|
+
"Declared loss allowance for this proposal, in bps of --amount (0-10000, default 2000 = 20%). This is the envelope voters and guardians are asked to accept, and the basis the settle-price guard sizes its tolerance from \u2014 not a formality."
|
|
3198
|
+
).option("--max-price-ages <list>", "Comma-separated per-token max Chainlink push-feed staleness in seconds (Portfolio, push mode; robinhood-mainnet equity feeds default to 345600 = 96h, crypto/stable feeds to the contract default). Empty entry = registry default.").option("--leverage <number>", "Leverage multiplier (Hyperliquid Perp, default: 10)").option("--asset-index <number>", "Perp asset index (Hyperliquid Perp, default: 0 for BTC)").option("--max-position <amount>", "Max position size in USD (Hyperliquid Perp, default: 100000)").option("--max-trades-per-day <n>", "Max trades per day (Hyperliquid Perp, default: 50)").option("--max-order-size <amount>", "Max single-order size in asset units (Hyperliquid Grid, default: 10000)").option("--max-orders-per-tick <n>", "Max orders per tick (Hyperliquid Grid, default: 20)").option("--asset-indices <list>", "Comma-separated perp asset indices (Hyperliquid Grid, default: 0)").action(async (templateKey, opts) => {
|
|
3199
|
+
const vault = opts.vault;
|
|
3200
|
+
if (!isAddress5(vault)) {
|
|
3201
|
+
console.error(chalk4.red("Invalid vault address"));
|
|
3202
|
+
process.exit(1);
|
|
3203
|
+
}
|
|
3204
|
+
const { def, address: templateAddr } = resolveTemplate(templateKey);
|
|
3205
|
+
const cloneSpinner = ora3(`Cloning ${def.name} template...`).start();
|
|
3206
|
+
let clone;
|
|
3207
|
+
let cloneHash;
|
|
3208
|
+
try {
|
|
3209
|
+
const result = await cloneTemplate(templateAddr);
|
|
3210
|
+
clone = result.clone;
|
|
3211
|
+
cloneHash = result.hash;
|
|
3212
|
+
cloneSpinner.succeed(`Cloned: ${chalk4.green(clone)}`);
|
|
3213
|
+
console.log(chalk4.dim(` Tx: ${getExplorerUrl(cloneHash)}`));
|
|
3214
|
+
} catch (err) {
|
|
3215
|
+
cloneSpinner.fail("Clone failed");
|
|
3216
|
+
console.error(chalk4.red(formatContractError(err)));
|
|
3217
|
+
process.exit(1);
|
|
3218
|
+
}
|
|
3219
|
+
const initSpinner = ora3("Initializing strategy...").start();
|
|
3220
|
+
try {
|
|
3221
|
+
const { initData } = await resolveAndBuildInitData(templateKey, opts, vault);
|
|
3222
|
+
const account = getAccount();
|
|
3223
|
+
const initHash = await writeContractWithRetry({
|
|
3224
|
+
account,
|
|
3225
|
+
chain: getChain(),
|
|
3226
|
+
address: clone,
|
|
3227
|
+
abi: BASE_STRATEGY_ABI,
|
|
3228
|
+
functionName: "initialize",
|
|
3229
|
+
args: [vault, account.address, initData]
|
|
3230
|
+
});
|
|
3231
|
+
const receipt = await waitForReceipt(initHash);
|
|
3232
|
+
if (receipt.status === "reverted") {
|
|
3233
|
+
throw new Error("Initialize transaction reverted on-chain");
|
|
3234
|
+
}
|
|
3235
|
+
initSpinner.succeed("Initialized");
|
|
3236
|
+
} catch (err) {
|
|
3237
|
+
initSpinner.fail("Initialize failed");
|
|
3238
|
+
console.error(chalk4.red(formatContractError(err)));
|
|
3239
|
+
process.exit(1);
|
|
3240
|
+
}
|
|
3241
|
+
if (templateKey === "hyperliquid-grid" || templateKey === "hyperliquid-perp") {
|
|
3242
|
+
const hcSpinner = ora3("Registering clone with HyperCore...").start();
|
|
3243
|
+
try {
|
|
3244
|
+
const account = getAccount();
|
|
3245
|
+
const hcHash = await writeContractWithRetry({
|
|
3246
|
+
account,
|
|
3247
|
+
chain: getChain(),
|
|
3248
|
+
address: clone,
|
|
3249
|
+
abi: templateKey === "hyperliquid-grid" ? HYPERLIQUID_GRID_STRATEGY_ABI : HYPERLIQUID_PERP_STRATEGY_ABI,
|
|
3250
|
+
functionName: "finalizeForHyperCore",
|
|
3251
|
+
args: [0n, 1, 0n]
|
|
3252
|
+
// token=USDC, variant=FirstStorageSlot(1), nonce unused
|
|
3253
|
+
});
|
|
3254
|
+
await waitForReceipt(hcHash);
|
|
3255
|
+
hcSpinner.succeed("HyperCore registration sent (active after next block)");
|
|
3256
|
+
} catch (err) {
|
|
3257
|
+
hcSpinner.warn("HyperCore registration failed \u2014 run finalizeForHyperCore manually");
|
|
3258
|
+
console.error(chalk4.yellow(formatContractError(err)));
|
|
3259
|
+
}
|
|
3260
|
+
}
|
|
3261
|
+
console.log();
|
|
3262
|
+
console.log(chalk4.bold("Strategy clone ready:"), chalk4.green(clone));
|
|
3263
|
+
console.log(chalk4.dim("Use this address in your proposal batch calls."));
|
|
3264
|
+
console.log();
|
|
3265
|
+
});
|
|
3266
|
+
strategy2.command("init").description("Initialize an already-deployed but uninitialized strategy clone").argument("<template>", "Template: moonwell-supply, aerodrome-lp, venice-inference, wsteth-moonwell, mamo-yield, portfolio, hyperliquid-perp, hyperliquid-grid").requiredOption("--clone <address>", "Clone address to initialize").requiredOption("--vault <address>", "Vault address").option("--amount <n>", "Asset amount to deploy").option("--min-redeem <n>", "Min asset on settlement (Moonwell)").option("--token <symbol>", "Asset token symbol (default: USDC)").option("--asset <symbol>", "Asset token (USDC, VVV, or address)").option("--agent <address>", "Agent wallet (Venice, default: your wallet)").option("--min-vvv <n>", "Min VVV from swap (Venice)").option("--single-hop", "Single-hop Aerodrome swap (Venice)").option("--token-a <address>", "Token A (Aerodrome)").option("--token-b <address>", "Token B (Aerodrome)").option("--amount-a <n>", "Token A amount (Aerodrome)").option("--amount-b <n>", "Token B amount (Aerodrome)").option("--stable", "Stable pool (Aerodrome)").option("--gauge <address>", "Gauge address (Aerodrome)").option("--lp-token <address>", "LP token address (Aerodrome)").option("--min-a-out <n>", "Min token A on settle (Aerodrome)").option("--min-b-out <n>", "Min token B on settle (Aerodrome)").option("--slippage <bps>", "Slippage tolerance in bps (wstETH, default: 500 = 5%)").option("--mamo-factory <address>", "Mamo StrategyFactory address (Mamo)").option("--tokens <list>", "Comma-separated token addresses or symbols (Portfolio)").option("--weights <list>", "Comma-separated weights in bps, must sum to 10000 (Portfolio)").option("--max-slippage <bps>", "Max slippage bps (Portfolio, default: 500)").option("--fee-tier <n>", "Pool fee tier (Portfolio, default: 3000)").option("--swap-adapter <address>", "Swap adapter address (Portfolio)").option("--feed-ids <list>", "Comma-separated Chainlink feed ids/addresses per token (Portfolio; auto-resolved from registry in push-feed mode)").option("--price-decimals <list>", "Comma-separated raw feed decimals per token (Portfolio; default 8 push / 18 Data Streams)").option("--swap-routes <list>", "Comma-separated per-token swap route spec (Portfolio). Per token: 'v3:<fee>' (mode 0), 'v4:<fee>:<tickSpacing>' (mode 2), or 'v4path:<hop>+<hop>...' (mode 3) where hop='<currency>@<fee>:<tickSpacing>', currency='eth'/'native'=ETH or 'self'=this token or an address. Empty entry = auto-detect / robinhood default.").option(
|
|
3267
|
+
"--max-drawdown-bps <bps>",
|
|
3268
|
+
"Declared loss allowance for this proposal, in bps of --amount (0-10000, default 2000 = 20%). This is the envelope voters and guardians are asked to accept, and the basis the settle-price guard sizes its tolerance from \u2014 not a formality."
|
|
3269
|
+
).option("--max-price-ages <list>", "Comma-separated per-token max Chainlink push-feed staleness in seconds (Portfolio, push mode; robinhood-mainnet equity feeds default to 345600 = 96h, crypto/stable feeds to the contract default). Empty entry = registry default.").option("--leverage <number>", "Leverage multiplier (Hyperliquid Perp, default: 10)").option("--asset-index <number>", "Perp asset index (Hyperliquid Perp, default: 0 for BTC)").option("--max-position <amount>", "Max position size in USD (Hyperliquid Perp, default: 100000)").option("--max-trades-per-day <n>", "Max trades per day (Hyperliquid Perp, default: 50)").option("--max-order-size <amount>", "Max single-order size in asset units (Hyperliquid Grid, default: 10000)").option("--max-orders-per-tick <n>", "Max orders per tick (Hyperliquid Grid, default: 20)").option("--asset-indices <list>", "Comma-separated perp asset indices (Hyperliquid Grid, default: 0)").action(async (templateKey, opts) => {
|
|
3270
|
+
const clone = opts.clone;
|
|
3271
|
+
const vault = opts.vault;
|
|
3272
|
+
if (!isAddress5(clone)) {
|
|
3273
|
+
console.error(chalk4.red("Invalid clone address"));
|
|
3274
|
+
process.exit(1);
|
|
3275
|
+
}
|
|
3276
|
+
if (!isAddress5(vault)) {
|
|
3277
|
+
console.error(chalk4.red("Invalid vault address"));
|
|
3278
|
+
process.exit(1);
|
|
3279
|
+
}
|
|
3280
|
+
resolveTemplate(templateKey);
|
|
3281
|
+
const publicClient = getPublicClient();
|
|
3282
|
+
const currentVault = await publicClient.readContract({
|
|
3283
|
+
address: clone,
|
|
3284
|
+
abi: BASE_STRATEGY_ABI,
|
|
3285
|
+
functionName: "vault"
|
|
3286
|
+
});
|
|
3287
|
+
if (currentVault !== "0x0000000000000000000000000000000000000000") {
|
|
3288
|
+
console.error(chalk4.red(`Clone already initialized (vault: ${currentVault})`));
|
|
3289
|
+
process.exit(1);
|
|
3290
|
+
}
|
|
3291
|
+
const initSpinner = ora3("Initializing strategy clone...").start();
|
|
3292
|
+
try {
|
|
3293
|
+
const { initData } = await resolveAndBuildInitData(templateKey, opts, vault);
|
|
3294
|
+
const account = getAccount();
|
|
3295
|
+
const initHash = await writeContractWithRetry({
|
|
3296
|
+
account,
|
|
3297
|
+
chain: getChain(),
|
|
3298
|
+
address: clone,
|
|
3299
|
+
abi: BASE_STRATEGY_ABI,
|
|
3300
|
+
functionName: "initialize",
|
|
3301
|
+
args: [vault, account.address, initData]
|
|
3302
|
+
});
|
|
3303
|
+
const receipt = await waitForReceipt(initHash);
|
|
3304
|
+
if (receipt.status === "reverted") {
|
|
3305
|
+
throw new Error("Initialize transaction reverted on-chain");
|
|
3306
|
+
}
|
|
3307
|
+
initSpinner.succeed("Initialized");
|
|
3308
|
+
console.log(chalk4.dim(` Tx: ${getExplorerUrl(initHash)}`));
|
|
3309
|
+
} catch (err) {
|
|
3310
|
+
initSpinner.fail("Initialize failed");
|
|
3311
|
+
console.error(chalk4.red(formatContractError(err)));
|
|
3312
|
+
process.exit(1);
|
|
3313
|
+
}
|
|
3314
|
+
const verifiedVault = await publicClient.readContract({
|
|
3315
|
+
address: clone,
|
|
3316
|
+
abi: BASE_STRATEGY_ABI,
|
|
3317
|
+
functionName: "vault"
|
|
3318
|
+
});
|
|
3319
|
+
console.log();
|
|
3320
|
+
console.log(chalk4.bold("Clone initialized:"), chalk4.green(clone));
|
|
3321
|
+
console.log(` Vault: ${chalk4.green(verifiedVault)}`);
|
|
3322
|
+
console.log(` Proposer: ${chalk4.green(getAccount().address)}`);
|
|
3323
|
+
console.log();
|
|
3324
|
+
});
|
|
3325
|
+
registerStrategyProposeCommands(strategy2);
|
|
3231
3326
|
strategy2.command("status").description("Show portfolio strategy status \u2014 allocations, balances, drift, PnL (Portfolio only)").argument("<clone>", "Strategy clone address").option("--json", "Output machine-readable JSON for agent consumption").action(async (cloneArg, opts) => {
|
|
3232
|
-
if (!
|
|
3233
|
-
console.error(
|
|
3327
|
+
if (!isAddress5(cloneArg)) {
|
|
3328
|
+
console.error(chalk4.red("Invalid clone address"));
|
|
3234
3329
|
process.exit(1);
|
|
3235
3330
|
}
|
|
3236
3331
|
const clone = cloneArg;
|
|
3237
3332
|
const publicClient = getPublicClient();
|
|
3238
3333
|
const network = getNetwork();
|
|
3239
3334
|
const jsonMode = !!opts.json;
|
|
3240
|
-
const spinner = jsonMode ? null :
|
|
3335
|
+
const spinner = jsonMode ? null : ora3("Reading strategy state...").start();
|
|
3241
3336
|
try {
|
|
3242
3337
|
const [stateRaw, vault, proposer, assetAddr, totalAmount, maxSlippage, allocations, swapExtraData] = await Promise.all([
|
|
3243
3338
|
publicClient.readContract({ address: clone, abi: PORTFOLIO_STRATEGY_ABI, functionName: "state" }),
|
|
@@ -3252,24 +3347,24 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
3252
3347
|
const stateNames = ["Pending", "Executed", "Settled"];
|
|
3253
3348
|
const stateStr = stateNames[Number(stateRaw)] || `Unknown(${stateRaw})`;
|
|
3254
3349
|
const [assetDecimals, assetSymbol] = await Promise.all([
|
|
3255
|
-
publicClient.readContract({ address: assetAddr, abi:
|
|
3256
|
-
publicClient.readContract({ address: assetAddr, abi:
|
|
3350
|
+
publicClient.readContract({ address: assetAddr, abi: erc20Abi3, functionName: "decimals" }),
|
|
3351
|
+
publicClient.readContract({ address: assetAddr, abi: erc20Abi3, functionName: "symbol" })
|
|
3257
3352
|
]);
|
|
3258
3353
|
const tokenReads = allocations.map((a) => Promise.all([
|
|
3259
|
-
publicClient.readContract({ address: a.token, abi:
|
|
3260
|
-
publicClient.readContract({ address: a.token, abi:
|
|
3261
|
-
publicClient.readContract({ address: a.token, abi:
|
|
3354
|
+
publicClient.readContract({ address: a.token, abi: erc20Abi3, functionName: "balanceOf", args: [clone] }),
|
|
3355
|
+
publicClient.readContract({ address: a.token, abi: erc20Abi3, functionName: "symbol" }),
|
|
3356
|
+
publicClient.readContract({ address: a.token, abi: erc20Abi3, functionName: "decimals" })
|
|
3262
3357
|
]));
|
|
3263
3358
|
const tokenData = await Promise.all(tokenReads);
|
|
3264
3359
|
const assetBalance = await publicClient.readContract({
|
|
3265
3360
|
address: assetAddr,
|
|
3266
|
-
abi:
|
|
3361
|
+
abi: erc20Abi3,
|
|
3267
3362
|
functionName: "balanceOf",
|
|
3268
3363
|
args: [clone]
|
|
3269
3364
|
});
|
|
3270
3365
|
const feeTiers = swapExtraData.map((data) => {
|
|
3271
3366
|
try {
|
|
3272
|
-
const [fee] =
|
|
3367
|
+
const [fee] = decodeAbiParameters3([{ type: "uint24" }], data);
|
|
3273
3368
|
return Number(fee) > 0 && Number(fee) <= 1e5 ? Number(fee) : 3e3;
|
|
3274
3369
|
} catch {
|
|
3275
3370
|
return 3e3;
|
|
@@ -3278,7 +3373,7 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
3278
3373
|
let prices = [];
|
|
3279
3374
|
let priceSource = null;
|
|
3280
3375
|
try {
|
|
3281
|
-
const { getTokenPricesInAsset } = await import("./price-
|
|
3376
|
+
const { getTokenPricesInAsset } = await import("./price-OUL7WIXQ.js");
|
|
3282
3377
|
prices = await getTokenPricesInAsset({
|
|
3283
3378
|
tokens: allocations.map((a, i) => ({
|
|
3284
3379
|
token: a.token,
|
|
@@ -3292,13 +3387,13 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
3292
3387
|
} catch {
|
|
3293
3388
|
}
|
|
3294
3389
|
const pricesAvailable = prices.some((p) => p !== null);
|
|
3295
|
-
const assetBalanceNum = Number(
|
|
3390
|
+
const assetBalanceNum = Number(formatUnits6(assetBalance, assetDecimals));
|
|
3296
3391
|
let totalPortfolioValue = assetBalanceNum;
|
|
3297
3392
|
let totalInvested = 0n;
|
|
3298
3393
|
const allocStatuses = allocations.map((alloc, i) => {
|
|
3299
3394
|
const [balance, symbol, decimals] = tokenData[i];
|
|
3300
|
-
const balanceNum = Number(
|
|
3301
|
-
const investedNum = Number(
|
|
3395
|
+
const balanceNum = Number(formatUnits6(balance, decimals));
|
|
3396
|
+
const investedNum = Number(formatUnits6(alloc.investedAmount, assetDecimals));
|
|
3302
3397
|
totalInvested += alloc.investedAmount;
|
|
3303
3398
|
const price = prices[i];
|
|
3304
3399
|
let currentValue = null;
|
|
@@ -3311,9 +3406,9 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
3311
3406
|
symbol,
|
|
3312
3407
|
decimals,
|
|
3313
3408
|
targetWeightBps: Number(alloc.targetWeightBps),
|
|
3314
|
-
balance:
|
|
3409
|
+
balance: formatUnits6(balance, decimals),
|
|
3315
3410
|
balanceRaw: balance.toString(),
|
|
3316
|
-
investedAmount:
|
|
3411
|
+
investedAmount: formatUnits6(alloc.investedAmount, assetDecimals),
|
|
3317
3412
|
currentPrice: price?.price ?? null,
|
|
3318
3413
|
currentValue,
|
|
3319
3414
|
actualWeightBps: null,
|
|
@@ -3337,7 +3432,7 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
3337
3432
|
}
|
|
3338
3433
|
}
|
|
3339
3434
|
}
|
|
3340
|
-
const totalInvestedNum = Number(
|
|
3435
|
+
const totalInvestedNum = Number(formatUnits6(totalInvested, assetDecimals));
|
|
3341
3436
|
const totalPnl = pricesAvailable ? totalPortfolioValue - totalInvestedNum : null;
|
|
3342
3437
|
const totalPnlPct = totalPnl !== null && totalInvestedNum > 0 ? totalPnl / totalInvestedNum * 100 : null;
|
|
3343
3438
|
spinner?.succeed("Strategy state loaded");
|
|
@@ -3349,9 +3444,9 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
3349
3444
|
state: stateStr,
|
|
3350
3445
|
network,
|
|
3351
3446
|
asset: { address: assetAddr, symbol: assetSymbol, decimals: assetDecimals },
|
|
3352
|
-
totalDeployed:
|
|
3447
|
+
totalDeployed: formatUnits6(totalAmount, assetDecimals),
|
|
3353
3448
|
maxSlippageBps: Number(maxSlippage),
|
|
3354
|
-
assetBalance:
|
|
3449
|
+
assetBalance: formatUnits6(assetBalance, assetDecimals),
|
|
3355
3450
|
pricesAvailable,
|
|
3356
3451
|
priceSource,
|
|
3357
3452
|
portfolio: {
|
|
@@ -3367,87 +3462,87 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
3367
3462
|
process.stdout.write(JSON.stringify(result) + "\n");
|
|
3368
3463
|
return;
|
|
3369
3464
|
}
|
|
3370
|
-
const stateColor = stateStr === "Executed" ?
|
|
3465
|
+
const stateColor = stateStr === "Executed" ? chalk4.green : stateStr === "Settled" ? chalk4.blue : chalk4.yellow;
|
|
3371
3466
|
console.log();
|
|
3372
|
-
console.log(
|
|
3373
|
-
console.log(
|
|
3374
|
-
console.log(` Clone: ${
|
|
3375
|
-
console.log(` Vault: ${
|
|
3376
|
-
console.log(` Proposer: ${
|
|
3467
|
+
console.log(chalk4.bold("Portfolio Strategy Status"));
|
|
3468
|
+
console.log(chalk4.dim("\u2500".repeat(70)));
|
|
3469
|
+
console.log(` Clone: ${chalk4.cyan(clone)}`);
|
|
3470
|
+
console.log(` Vault: ${chalk4.cyan(vault)}`);
|
|
3471
|
+
console.log(` Proposer: ${chalk4.cyan(proposer)}`);
|
|
3377
3472
|
console.log(` State: ${stateColor(stateStr)}`);
|
|
3378
3473
|
console.log(` Asset: ${assetSymbol} (${assetAddr})`);
|
|
3379
|
-
console.log(` Deployed: ${
|
|
3474
|
+
console.log(` Deployed: ${formatUnits6(totalAmount, assetDecimals)} ${assetSymbol}`);
|
|
3380
3475
|
console.log(` Max Slip: ${Number(maxSlippage) / 100}%`);
|
|
3381
|
-
console.log(` Asset held: ${
|
|
3476
|
+
console.log(` Asset held: ${formatUnits6(assetBalance, assetDecimals)} ${assetSymbol}`);
|
|
3382
3477
|
if (pricesAvailable) {
|
|
3383
3478
|
const pvStr = totalPortfolioValue.toFixed(6);
|
|
3384
3479
|
const pnlStr = totalPnl !== null ? (totalPnl >= 0 ? "+" : "") + totalPnl.toFixed(6) : "n/a";
|
|
3385
3480
|
const pnlPctStr = totalPnlPct !== null ? (totalPnlPct >= 0 ? "+" : "") + totalPnlPct.toFixed(1) + "%" : "";
|
|
3386
|
-
const pnlColor = totalPnl !== null && totalPnl >= 0 ?
|
|
3481
|
+
const pnlColor = totalPnl !== null && totalPnl >= 0 ? chalk4.green : chalk4.red;
|
|
3387
3482
|
console.log(` Value: ${pvStr} ${assetSymbol} | PnL: ${pnlColor(`${pnlStr} (${pnlPctStr})`)}`);
|
|
3388
3483
|
console.log(` Price src: ${priceSource}`);
|
|
3389
3484
|
}
|
|
3390
3485
|
console.log();
|
|
3391
|
-
console.log(
|
|
3392
|
-
console.log(
|
|
3486
|
+
console.log(chalk4.bold("Allocations"));
|
|
3487
|
+
console.log(chalk4.dim("\u2500".repeat(70)));
|
|
3393
3488
|
if (pricesAvailable) {
|
|
3394
3489
|
console.log(
|
|
3395
|
-
|
|
3490
|
+
chalk4.dim(" Token".padEnd(10)) + chalk4.dim("Target".padEnd(9)) + chalk4.dim("Actual".padEnd(9)) + chalk4.dim("Drift".padEnd(8)) + chalk4.dim("Value".padEnd(16)) + chalk4.dim("PnL".padEnd(16)) + chalk4.dim("Balance")
|
|
3396
3491
|
);
|
|
3397
3492
|
for (const a of allocStatuses) {
|
|
3398
3493
|
const targetStr = (a.targetWeightBps / 100).toFixed(1) + "%";
|
|
3399
3494
|
const actualStr = a.actualWeightBps !== null ? (a.actualWeightBps / 100).toFixed(1) + "%" : "\u2014";
|
|
3400
3495
|
const driftStr = a.driftBps !== null ? (a.driftBps >= 0 ? "+" : "") + a.driftBps : "\u2014";
|
|
3401
|
-
const driftColor = a.driftBps !== null ? Math.abs(a.driftBps) > 500 ?
|
|
3496
|
+
const driftColor = a.driftBps !== null ? Math.abs(a.driftBps) > 500 ? chalk4.red : Math.abs(a.driftBps) > 200 ? chalk4.yellow : chalk4.dim : chalk4.dim;
|
|
3402
3497
|
const valueStr = a.currentValue !== null ? a.currentValue.toFixed(6) : "\u2014";
|
|
3403
3498
|
const pnlPctStr = a.pnlPct !== null ? (a.pnlPct >= 0 ? "+" : "") + a.pnlPct.toFixed(1) + "%" : "\u2014";
|
|
3404
|
-
const pnlColor = a.pnlPct !== null ? a.pnlPct >= 0 ?
|
|
3499
|
+
const pnlColor = a.pnlPct !== null ? a.pnlPct >= 0 ? chalk4.green : chalk4.red : chalk4.dim;
|
|
3405
3500
|
console.log(
|
|
3406
|
-
` ${
|
|
3501
|
+
` ${chalk4.bold(a.symbol.padEnd(8))}${targetStr.padEnd(9)}${actualStr.padEnd(9)}${driftColor(driftStr.toString().padEnd(8))}${valueStr.padEnd(16)}${pnlColor(pnlPctStr.padEnd(16))}${a.balance}`
|
|
3407
3502
|
);
|
|
3408
3503
|
}
|
|
3409
3504
|
} else {
|
|
3410
|
-
console.log(
|
|
3505
|
+
console.log(chalk4.dim(" Prices unavailable \u2014 showing raw balances only."));
|
|
3411
3506
|
console.log();
|
|
3412
3507
|
console.log(
|
|
3413
|
-
|
|
3508
|
+
chalk4.dim(" Token".padEnd(12)) + chalk4.dim("Weight".padEnd(10)) + chalk4.dim("Invested".padEnd(18)) + chalk4.dim("Balance".padEnd(18)) + chalk4.dim("Address")
|
|
3414
3509
|
);
|
|
3415
3510
|
for (const a of allocStatuses) {
|
|
3416
3511
|
const weightPct = (a.targetWeightBps / 100).toFixed(1) + "%";
|
|
3417
3512
|
console.log(
|
|
3418
|
-
` ${
|
|
3513
|
+
` ${chalk4.bold(a.symbol.padEnd(10))}${weightPct.padEnd(10)}${a.investedAmount.padEnd(18)}${a.balance.padEnd(18)}${chalk4.dim(a.token)}`
|
|
3419
3514
|
);
|
|
3420
3515
|
}
|
|
3421
3516
|
}
|
|
3422
|
-
console.log(
|
|
3423
|
-
console.log(` Total invested: ${
|
|
3517
|
+
console.log(chalk4.dim("\u2500".repeat(70)));
|
|
3518
|
+
console.log(` Total invested: ${formatUnits6(totalInvested, assetDecimals)} ${assetSymbol}`);
|
|
3424
3519
|
if (pricesAvailable && maxDriftToken) {
|
|
3425
3520
|
const absMaxDrift = Math.abs(maxDriftBps);
|
|
3426
|
-
const driftColor = absMaxDrift > 500 ?
|
|
3521
|
+
const driftColor = absMaxDrift > 500 ? chalk4.red : absMaxDrift > 200 ? chalk4.yellow : chalk4.green;
|
|
3427
3522
|
console.log(` Max drift: ${driftColor(`${maxDriftBps > 0 ? "+" : ""}${maxDriftBps} bps`)} (${maxDriftToken})`);
|
|
3428
3523
|
}
|
|
3429
3524
|
if (stateStr === "Executed") {
|
|
3430
3525
|
console.log();
|
|
3431
|
-
console.log(
|
|
3432
|
-
console.log(
|
|
3526
|
+
console.log(chalk4.dim("Rebalance: sherwood strategy rebalance " + clone));
|
|
3527
|
+
console.log(chalk4.dim("With new weights: sherwood strategy rebalance " + clone + " --new-weights 2500,2500,2000,1500,1500"));
|
|
3433
3528
|
}
|
|
3434
3529
|
console.log();
|
|
3435
3530
|
} catch (err) {
|
|
3436
3531
|
spinner?.fail("Failed to read strategy state");
|
|
3437
|
-
console.error(
|
|
3532
|
+
console.error(chalk4.red(formatContractError(err)));
|
|
3438
3533
|
process.exit(1);
|
|
3439
3534
|
}
|
|
3440
3535
|
});
|
|
3441
3536
|
strategy2.command("rebalance").description("Rebalance a portfolio strategy \u2014 sell all positions, re-buy at target weights (Portfolio only)").argument("<clone>", "Strategy clone address").option("--new-weights <list>", "Comma-separated new weights in bps (must sum to 10000). Updates weights before rebalancing.").option("--max-slippage <bps>", "New max slippage bps (used with --new-weights)").option("--dry-run", "Show what would happen without executing").action(async (cloneArg, opts) => {
|
|
3442
|
-
if (!
|
|
3443
|
-
console.error(
|
|
3537
|
+
if (!isAddress5(cloneArg)) {
|
|
3538
|
+
console.error(chalk4.red("Invalid clone address"));
|
|
3444
3539
|
process.exit(1);
|
|
3445
3540
|
}
|
|
3446
3541
|
const clone = cloneArg;
|
|
3447
3542
|
const publicClient = getPublicClient();
|
|
3448
3543
|
const account = getAccount();
|
|
3449
3544
|
const chain = getChain();
|
|
3450
|
-
const verifySpinner =
|
|
3545
|
+
const verifySpinner = ora3("Verifying strategy state...").start();
|
|
3451
3546
|
try {
|
|
3452
3547
|
const [stateRaw, proposer, assetAddr, allocations] = await Promise.all([
|
|
3453
3548
|
publicClient.readContract({ address: clone, abi: PORTFOLIO_STRATEGY_ABI, functionName: "state" }),
|
|
@@ -3465,56 +3560,56 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
3465
3560
|
process.exit(1);
|
|
3466
3561
|
}
|
|
3467
3562
|
const [assetDecimals, assetSymbol] = await Promise.all([
|
|
3468
|
-
publicClient.readContract({ address: assetAddr, abi:
|
|
3469
|
-
publicClient.readContract({ address: assetAddr, abi:
|
|
3563
|
+
publicClient.readContract({ address: assetAddr, abi: erc20Abi3, functionName: "decimals" }),
|
|
3564
|
+
publicClient.readContract({ address: assetAddr, abi: erc20Abi3, functionName: "symbol" })
|
|
3470
3565
|
]);
|
|
3471
3566
|
const tokenReads = allocations.map((a) => Promise.all([
|
|
3472
|
-
publicClient.readContract({ address: a.token, abi:
|
|
3473
|
-
publicClient.readContract({ address: a.token, abi:
|
|
3474
|
-
publicClient.readContract({ address: a.token, abi:
|
|
3567
|
+
publicClient.readContract({ address: a.token, abi: erc20Abi3, functionName: "balanceOf", args: [clone] }),
|
|
3568
|
+
publicClient.readContract({ address: a.token, abi: erc20Abi3, functionName: "symbol" }),
|
|
3569
|
+
publicClient.readContract({ address: a.token, abi: erc20Abi3, functionName: "decimals" })
|
|
3475
3570
|
]));
|
|
3476
3571
|
const tokenData = await Promise.all(tokenReads);
|
|
3477
3572
|
verifySpinner.succeed("Strategy is Executed \u2014 ready to rebalance");
|
|
3478
3573
|
console.log();
|
|
3479
|
-
console.log(
|
|
3480
|
-
console.log(
|
|
3574
|
+
console.log(chalk4.bold("Current Allocations"));
|
|
3575
|
+
console.log(chalk4.dim("\u2500".repeat(50)));
|
|
3481
3576
|
for (let i = 0; i < allocations.length; i++) {
|
|
3482
3577
|
const alloc = allocations[i];
|
|
3483
3578
|
const [balance, symbol, decimals] = tokenData[i];
|
|
3484
3579
|
const weightPct = (Number(alloc.targetWeightBps) / 100).toFixed(1) + "%";
|
|
3485
3580
|
console.log(
|
|
3486
|
-
` ${
|
|
3581
|
+
` ${chalk4.bold(symbol.padEnd(8))} ${weightPct.padEnd(8)} bal: ${formatUnits6(balance, decimals)}`
|
|
3487
3582
|
);
|
|
3488
3583
|
}
|
|
3489
3584
|
let newWeightsBps;
|
|
3490
3585
|
if (opts.newWeights) {
|
|
3491
3586
|
newWeightsBps = opts.newWeights.split(",").map((w) => Number(w.trim()));
|
|
3492
3587
|
if (newWeightsBps.length !== allocations.length) {
|
|
3493
|
-
console.error(
|
|
3588
|
+
console.error(chalk4.red(`--new-weights must have ${allocations.length} values (one per token)`));
|
|
3494
3589
|
process.exit(1);
|
|
3495
3590
|
}
|
|
3496
3591
|
const weightSum = newWeightsBps.reduce((a, b) => a + b, 0);
|
|
3497
3592
|
if (weightSum !== 1e4) {
|
|
3498
|
-
console.error(
|
|
3593
|
+
console.error(chalk4.red(`Weights must sum to 10000, got ${weightSum}`));
|
|
3499
3594
|
process.exit(1);
|
|
3500
3595
|
}
|
|
3501
3596
|
console.log();
|
|
3502
|
-
console.log(
|
|
3503
|
-
console.log(
|
|
3597
|
+
console.log(chalk4.bold("New Target Weights"));
|
|
3598
|
+
console.log(chalk4.dim("\u2500".repeat(50)));
|
|
3504
3599
|
for (let i = 0; i < allocations.length; i++) {
|
|
3505
3600
|
const [, symbol] = tokenData[i];
|
|
3506
3601
|
const oldPct = (Number(allocations[i].targetWeightBps) / 100).toFixed(1);
|
|
3507
3602
|
const newPct = (newWeightsBps[i] / 100).toFixed(1);
|
|
3508
|
-
const arrow = oldPct !== newPct ?
|
|
3509
|
-
console.log(` ${
|
|
3603
|
+
const arrow = oldPct !== newPct ? chalk4.yellow("\u2192") : chalk4.dim("\u2192");
|
|
3604
|
+
console.log(` ${chalk4.bold(symbol.padEnd(8))} ${oldPct}% ${arrow} ${newPct}%`);
|
|
3510
3605
|
}
|
|
3511
3606
|
}
|
|
3512
3607
|
if (opts.dryRun) {
|
|
3513
3608
|
console.log();
|
|
3514
|
-
console.log(
|
|
3515
|
-
console.log(
|
|
3516
|
-
console.log(
|
|
3517
|
-
console.log(
|
|
3609
|
+
console.log(chalk4.yellow("Dry run \u2014 no transactions sent."));
|
|
3610
|
+
console.log(chalk4.dim("The rebalance would:"));
|
|
3611
|
+
console.log(chalk4.dim(" 1. Sell all current token positions back to " + assetSymbol));
|
|
3612
|
+
console.log(chalk4.dim(" 2. Re-buy at " + (newWeightsBps ? "new" : "current") + " target weights"));
|
|
3518
3613
|
console.log();
|
|
3519
3614
|
return;
|
|
3520
3615
|
}
|
|
@@ -3525,9 +3620,9 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
3525
3620
|
abi: PORTFOLIO_STRATEGY_ABI,
|
|
3526
3621
|
functionName: "getSwapExtraData"
|
|
3527
3622
|
});
|
|
3528
|
-
const updateSpinner =
|
|
3623
|
+
const updateSpinner = ora3("Updating target weights...").start();
|
|
3529
3624
|
try {
|
|
3530
|
-
const innerData =
|
|
3625
|
+
const innerData = encodeAbiParameters5(
|
|
3531
3626
|
[{ type: "uint256[]" }, { type: "uint256" }, { type: "bytes[]" }],
|
|
3532
3627
|
[newWeightsBps.map((w) => BigInt(w)), BigInt(maxSlip), swapData]
|
|
3533
3628
|
);
|
|
@@ -3542,14 +3637,14 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
3542
3637
|
const receipt = await waitForReceipt(updateHash);
|
|
3543
3638
|
if (receipt.status === "reverted") throw new Error("updateParams reverted");
|
|
3544
3639
|
updateSpinner.succeed("Target weights updated");
|
|
3545
|
-
console.log(
|
|
3640
|
+
console.log(chalk4.dim(` Tx: ${getExplorerUrl(updateHash)}`));
|
|
3546
3641
|
} catch (err) {
|
|
3547
3642
|
updateSpinner.fail("Failed to update weights");
|
|
3548
|
-
console.error(
|
|
3643
|
+
console.error(chalk4.red(formatContractError(err)));
|
|
3549
3644
|
process.exit(1);
|
|
3550
3645
|
}
|
|
3551
3646
|
}
|
|
3552
|
-
const rebalanceSpinner =
|
|
3647
|
+
const rebalanceSpinner = ora3("Rebalancing portfolio (sell all \u2192 re-buy at targets)...").start();
|
|
3553
3648
|
try {
|
|
3554
3649
|
const rebalanceHash = await writeContractWithRetry({
|
|
3555
3650
|
account,
|
|
@@ -3561,44 +3656,44 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
3561
3656
|
const receipt = await waitForReceipt(rebalanceHash);
|
|
3562
3657
|
if (receipt.status === "reverted") throw new Error("rebalance() reverted on-chain");
|
|
3563
3658
|
rebalanceSpinner.succeed("Portfolio rebalanced");
|
|
3564
|
-
console.log(
|
|
3659
|
+
console.log(chalk4.dim(` Tx: ${getExplorerUrl(rebalanceHash)}`));
|
|
3565
3660
|
} catch (err) {
|
|
3566
3661
|
rebalanceSpinner.fail("Rebalance failed");
|
|
3567
|
-
console.error(
|
|
3662
|
+
console.error(chalk4.red(formatContractError(err)));
|
|
3568
3663
|
process.exit(1);
|
|
3569
3664
|
}
|
|
3570
|
-
const postSpinner =
|
|
3665
|
+
const postSpinner = ora3("Reading updated allocations...").start();
|
|
3571
3666
|
const postAllocations = await publicClient.readContract({
|
|
3572
3667
|
address: clone,
|
|
3573
3668
|
abi: PORTFOLIO_STRATEGY_ABI,
|
|
3574
3669
|
functionName: "getAllocations"
|
|
3575
3670
|
});
|
|
3576
3671
|
const postReads = postAllocations.map((a) => Promise.all([
|
|
3577
|
-
publicClient.readContract({ address: a.token, abi:
|
|
3578
|
-
publicClient.readContract({ address: a.token, abi:
|
|
3579
|
-
publicClient.readContract({ address: a.token, abi:
|
|
3672
|
+
publicClient.readContract({ address: a.token, abi: erc20Abi3, functionName: "balanceOf", args: [clone] }),
|
|
3673
|
+
publicClient.readContract({ address: a.token, abi: erc20Abi3, functionName: "symbol" }),
|
|
3674
|
+
publicClient.readContract({ address: a.token, abi: erc20Abi3, functionName: "decimals" })
|
|
3580
3675
|
]));
|
|
3581
3676
|
const postData = await Promise.all(postReads);
|
|
3582
3677
|
postSpinner.succeed("Updated allocations");
|
|
3583
3678
|
console.log();
|
|
3584
|
-
console.log(
|
|
3585
|
-
console.log(
|
|
3679
|
+
console.log(chalk4.bold("Post-Rebalance Allocations"));
|
|
3680
|
+
console.log(chalk4.dim("\u2500".repeat(50)));
|
|
3586
3681
|
for (let i = 0; i < postAllocations.length; i++) {
|
|
3587
3682
|
const alloc = postAllocations[i];
|
|
3588
3683
|
const [balance, symbol, decimals] = postData[i];
|
|
3589
3684
|
const weightPct = (Number(alloc.targetWeightBps) / 100).toFixed(1) + "%";
|
|
3590
|
-
const investedStr =
|
|
3685
|
+
const investedStr = formatUnits6(alloc.investedAmount, assetDecimals);
|
|
3591
3686
|
console.log(
|
|
3592
|
-
` ${
|
|
3687
|
+
` ${chalk4.bold(symbol.padEnd(8))} ${weightPct.padEnd(8)} invested: ${investedStr.padEnd(14)} bal: ${formatUnits6(balance, decimals)}`
|
|
3593
3688
|
);
|
|
3594
3689
|
}
|
|
3595
3690
|
console.log();
|
|
3596
|
-
console.log(
|
|
3691
|
+
console.log(chalk4.green("\u2713 Rebalance complete"));
|
|
3597
3692
|
console.log();
|
|
3598
3693
|
} catch (err) {
|
|
3599
3694
|
if (err.code === "ERR_MODULE_NOT_FOUND" || err instanceof TypeError) throw err;
|
|
3600
3695
|
verifySpinner.fail("Pre-flight check failed");
|
|
3601
|
-
console.error(
|
|
3696
|
+
console.error(chalk4.red(formatContractError(err)));
|
|
3602
3697
|
process.exit(1);
|
|
3603
3698
|
}
|
|
3604
3699
|
});
|
|
@@ -3671,9 +3766,9 @@ async function getActiveFunds2(creator) {
|
|
|
3671
3766
|
}
|
|
3672
3767
|
|
|
3673
3768
|
// src/commands/venice.ts
|
|
3674
|
-
import { formatUnits as
|
|
3675
|
-
import
|
|
3676
|
-
import
|
|
3769
|
+
import { formatUnits as formatUnits7, isAddress as isAddress6, parseUnits as parseUnits5 } from "viem";
|
|
3770
|
+
import chalk5 from "chalk";
|
|
3771
|
+
import ora4 from "ora";
|
|
3677
3772
|
import { readFileSync } from "fs";
|
|
3678
3773
|
import { createInterface } from "readline/promises";
|
|
3679
3774
|
function registerVeniceCommands(program2) {
|
|
@@ -3681,7 +3776,7 @@ function registerVeniceCommands(program2) {
|
|
|
3681
3776
|
venice.command("provision").description("Self-provision a Venice API key (requires sVVV in wallet)").action(async () => {
|
|
3682
3777
|
const account = getAccount();
|
|
3683
3778
|
const client = getPublicClient();
|
|
3684
|
-
const checkSpinner =
|
|
3779
|
+
const checkSpinner = ora4("Checking sVVV balance...").start();
|
|
3685
3780
|
try {
|
|
3686
3781
|
const sVvvBalance = await client.readContract({
|
|
3687
3782
|
address: VENICE().STAKING,
|
|
@@ -3691,33 +3786,33 @@ function registerVeniceCommands(program2) {
|
|
|
3691
3786
|
});
|
|
3692
3787
|
if (sVvvBalance === 0n) {
|
|
3693
3788
|
checkSpinner.fail("No sVVV found in wallet");
|
|
3694
|
-
console.log(
|
|
3695
|
-
console.log(
|
|
3789
|
+
console.log(chalk5.yellow(" Your wallet must hold staked VVV (sVVV) to provision a Venice API key."));
|
|
3790
|
+
console.log(chalk5.yellow(" Use the VeniceInferenceStrategy via a proposal to distribute sVVV to agents."));
|
|
3696
3791
|
process.exit(1);
|
|
3697
3792
|
}
|
|
3698
|
-
checkSpinner.succeed(`sVVV balance: ${
|
|
3793
|
+
checkSpinner.succeed(`sVVV balance: ${formatUnits7(sVvvBalance, 18)}`);
|
|
3699
3794
|
} catch (err) {
|
|
3700
3795
|
checkSpinner.fail("Failed to check sVVV balance");
|
|
3701
|
-
console.error(
|
|
3796
|
+
console.error(chalk5.red(formatContractError(err)));
|
|
3702
3797
|
process.exit(1);
|
|
3703
3798
|
}
|
|
3704
|
-
const keySpinner =
|
|
3799
|
+
const keySpinner = ora4("Provisioning Venice API key...").start();
|
|
3705
3800
|
try {
|
|
3706
3801
|
const apiKey = await provisionApiKey();
|
|
3707
3802
|
keySpinner.succeed("Venice API key provisioned");
|
|
3708
|
-
console.log(
|
|
3709
|
-
console.log(
|
|
3803
|
+
console.log(chalk5.dim(` Key: ${apiKey.slice(0, 8)}...${apiKey.slice(-4)}`));
|
|
3804
|
+
console.log(chalk5.dim(" Saved to ~/.sherwood/config.json"));
|
|
3710
3805
|
try {
|
|
3711
|
-
const { createVeniceProvisionAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-
|
|
3806
|
+
const { createVeniceProvisionAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-32PEJETG.js");
|
|
3712
3807
|
const { uid } = await createVeniceProvisionAttestation(account.address);
|
|
3713
3808
|
if (uid !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
|
|
3714
|
-
console.log(
|
|
3809
|
+
console.log(chalk5.dim(` Attested: ${getEasScanUrl2(uid)}`));
|
|
3715
3810
|
}
|
|
3716
3811
|
} catch {
|
|
3717
3812
|
}
|
|
3718
3813
|
} catch (err) {
|
|
3719
3814
|
keySpinner.fail("Failed to provision API key");
|
|
3720
|
-
console.error(
|
|
3815
|
+
console.error(chalk5.red(formatContractError(err)));
|
|
3721
3816
|
process.exit(1);
|
|
3722
3817
|
}
|
|
3723
3818
|
});
|
|
@@ -3726,8 +3821,8 @@ function registerVeniceCommands(program2) {
|
|
|
3726
3821
|
const client = getPublicClient();
|
|
3727
3822
|
const chain = getChain();
|
|
3728
3823
|
const veniceAddrs = VENICE();
|
|
3729
|
-
const amount =
|
|
3730
|
-
const balSpinner =
|
|
3824
|
+
const amount = parseUnits5(opts.amount, 18);
|
|
3825
|
+
const balSpinner = ora4("Checking VVV balance...").start();
|
|
3731
3826
|
try {
|
|
3732
3827
|
const vvvBalance = await client.readContract({
|
|
3733
3828
|
address: veniceAddrs.VVV,
|
|
@@ -3736,16 +3831,16 @@ function registerVeniceCommands(program2) {
|
|
|
3736
3831
|
args: [account.address]
|
|
3737
3832
|
});
|
|
3738
3833
|
if (vvvBalance < amount) {
|
|
3739
|
-
balSpinner.fail(`Insufficient VVV: have ${
|
|
3834
|
+
balSpinner.fail(`Insufficient VVV: have ${formatUnits7(vvvBalance, 18)}, need ${opts.amount}`);
|
|
3740
3835
|
process.exit(1);
|
|
3741
3836
|
}
|
|
3742
|
-
balSpinner.succeed(`VVV balance: ${
|
|
3837
|
+
balSpinner.succeed(`VVV balance: ${formatUnits7(vvvBalance, 18)}`);
|
|
3743
3838
|
} catch (err) {
|
|
3744
3839
|
balSpinner.fail("Failed to check VVV balance");
|
|
3745
|
-
console.error(
|
|
3840
|
+
console.error(chalk5.red(formatContractError(err)));
|
|
3746
3841
|
process.exit(1);
|
|
3747
3842
|
}
|
|
3748
|
-
const approveSpinner =
|
|
3843
|
+
const approveSpinner = ora4("Approving VVV for staking contract...").start();
|
|
3749
3844
|
try {
|
|
3750
3845
|
const approveHash = await writeContractWithRetry({
|
|
3751
3846
|
account,
|
|
@@ -3759,10 +3854,10 @@ function registerVeniceCommands(program2) {
|
|
|
3759
3854
|
approveSpinner.succeed("VVV approved");
|
|
3760
3855
|
} catch (err) {
|
|
3761
3856
|
approveSpinner.fail("Approve failed");
|
|
3762
|
-
console.error(
|
|
3857
|
+
console.error(chalk5.red(formatContractError(err)));
|
|
3763
3858
|
process.exit(1);
|
|
3764
3859
|
}
|
|
3765
|
-
const stakeSpinner =
|
|
3860
|
+
const stakeSpinner = ora4(`Staking ${opts.amount} VVV...`).start();
|
|
3766
3861
|
try {
|
|
3767
3862
|
const stakeHash = await writeContractWithRetry({
|
|
3768
3863
|
account,
|
|
@@ -3779,11 +3874,11 @@ function registerVeniceCommands(program2) {
|
|
|
3779
3874
|
functionName: "balanceOf",
|
|
3780
3875
|
args: [account.address]
|
|
3781
3876
|
});
|
|
3782
|
-
stakeSpinner.succeed(`Staked ${opts.amount} VVV \u2192 ${
|
|
3783
|
-
console.log(
|
|
3877
|
+
stakeSpinner.succeed(`Staked ${opts.amount} VVV \u2192 ${formatUnits7(sVvvBalance, 18)} sVVV`);
|
|
3878
|
+
console.log(chalk5.dim(" You can now run: sherwood venice provision"));
|
|
3784
3879
|
} catch (err) {
|
|
3785
3880
|
stakeSpinner.fail("Stake failed");
|
|
3786
|
-
console.error(
|
|
3881
|
+
console.error(chalk5.red(formatContractError(err)));
|
|
3787
3882
|
process.exit(1);
|
|
3788
3883
|
}
|
|
3789
3884
|
});
|
|
@@ -3792,13 +3887,13 @@ function registerVeniceCommands(program2) {
|
|
|
3792
3887
|
const client = getPublicClient();
|
|
3793
3888
|
const chain = getChain();
|
|
3794
3889
|
const { STAKING, DIEM } = VENICE();
|
|
3795
|
-
const amount =
|
|
3890
|
+
const amount = parseUnits5(opts.amount, 18);
|
|
3796
3891
|
const slippageBps = BigInt(opts.slippage ?? "500");
|
|
3797
3892
|
if (slippageBps < 0n || slippageBps > 10000n) {
|
|
3798
|
-
console.error(
|
|
3893
|
+
console.error(chalk5.red(`Invalid slippage bps: ${opts.slippage} (must be 0\u201310000)`));
|
|
3799
3894
|
process.exit(1);
|
|
3800
3895
|
}
|
|
3801
|
-
const balSpinner =
|
|
3896
|
+
const balSpinner = ora4("Checking sVVV balance...").start();
|
|
3802
3897
|
let sVvvBalance;
|
|
3803
3898
|
try {
|
|
3804
3899
|
sVvvBalance = await client.readContract({
|
|
@@ -3809,18 +3904,18 @@ function registerVeniceCommands(program2) {
|
|
|
3809
3904
|
});
|
|
3810
3905
|
} catch (err) {
|
|
3811
3906
|
balSpinner.fail("Failed to read sVVV balance");
|
|
3812
|
-
console.error(
|
|
3907
|
+
console.error(chalk5.red(formatContractError(err)));
|
|
3813
3908
|
process.exit(1);
|
|
3814
3909
|
}
|
|
3815
3910
|
if (sVvvBalance < amount) {
|
|
3816
3911
|
balSpinner.fail(
|
|
3817
|
-
`Insufficient sVVV: have ${
|
|
3912
|
+
`Insufficient sVVV: have ${formatUnits7(sVvvBalance, 18)}, need ${opts.amount}`
|
|
3818
3913
|
);
|
|
3819
|
-
console.log(
|
|
3914
|
+
console.log(chalk5.yellow(" Stake VVV first (see VeniceInferenceStrategy proposal flow)."));
|
|
3820
3915
|
process.exit(1);
|
|
3821
3916
|
}
|
|
3822
|
-
balSpinner.succeed(`sVVV balance: ${
|
|
3823
|
-
const quoteSpinner =
|
|
3917
|
+
balSpinner.succeed(`sVVV balance: ${formatUnits7(sVvvBalance, 18)}`);
|
|
3918
|
+
const quoteSpinner = ora4("Quoting DIEM output...").start();
|
|
3824
3919
|
let expected;
|
|
3825
3920
|
try {
|
|
3826
3921
|
expected = await client.readContract({
|
|
@@ -3831,7 +3926,7 @@ function registerVeniceCommands(program2) {
|
|
|
3831
3926
|
});
|
|
3832
3927
|
} catch (err) {
|
|
3833
3928
|
quoteSpinner.fail("Failed to quote DIEM output");
|
|
3834
|
-
console.error(
|
|
3929
|
+
console.error(chalk5.red(formatContractError(err)));
|
|
3835
3930
|
process.exit(1);
|
|
3836
3931
|
}
|
|
3837
3932
|
if (expected === 0n) {
|
|
@@ -3840,9 +3935,9 @@ function registerVeniceCommands(program2) {
|
|
|
3840
3935
|
}
|
|
3841
3936
|
const minOut = expected * (10000n - slippageBps) / 10000n;
|
|
3842
3937
|
quoteSpinner.succeed(
|
|
3843
|
-
`Expected: ${
|
|
3938
|
+
`Expected: ${formatUnits7(expected, 18)} DIEM (minOut @ ${slippageBps}bps: ${formatUnits7(minOut, 18)})`
|
|
3844
3939
|
);
|
|
3845
|
-
const MIN_SPENDABLE_DIEM =
|
|
3940
|
+
const MIN_SPENDABLE_DIEM = parseUnits5("0.1", 18);
|
|
3846
3941
|
let currentDiem = 0n;
|
|
3847
3942
|
try {
|
|
3848
3943
|
currentDiem = await client.readContract({
|
|
@@ -3858,27 +3953,27 @@ function registerVeniceCommands(program2) {
|
|
|
3858
3953
|
const shortfall = MIN_SPENDABLE_DIEM - projectedDiem;
|
|
3859
3954
|
const sVvvNeeded = (amount * shortfall + expected - 1n) / expected;
|
|
3860
3955
|
console.log();
|
|
3861
|
-
console.log(
|
|
3862
|
-
console.log(
|
|
3863
|
-
console.log(
|
|
3864
|
-
console.log(
|
|
3865
|
-
console.log(
|
|
3866
|
-
console.log(
|
|
3956
|
+
console.log(chalk5.red(" \u26A0 Venice requires \u2265 0.1 DIEM minimum before ANY DIEM credit"));
|
|
3957
|
+
console.log(chalk5.red(" becomes spendable on inference calls."));
|
|
3958
|
+
console.log(chalk5.red(` Projected DIEM after mint: ${formatUnits7(projectedDiem, 18)} (short by ${formatUnits7(shortfall, 18)})`));
|
|
3959
|
+
console.log(chalk5.red(` Approx additional sVVV needed: ${formatUnits7(sVvvNeeded, 18)}`));
|
|
3960
|
+
console.log(chalk5.dim(" Below threshold, this DIEM sits idle \u2014 consider acquiring"));
|
|
3961
|
+
console.log(chalk5.dim(" more sVVV first, or use x402 USDC pay-per-request."));
|
|
3867
3962
|
}
|
|
3868
3963
|
console.log();
|
|
3869
|
-
console.log(
|
|
3870
|
-
console.log(
|
|
3871
|
-
console.log(
|
|
3872
|
-
console.log(
|
|
3873
|
-
console.log(
|
|
3874
|
-
console.log(
|
|
3964
|
+
console.log(chalk5.yellow(" \u26A0 Locked sVVV cannot be unstaked directly. To recover it you must"));
|
|
3965
|
+
console.log(chalk5.yellow(" first call `staking.burnDiem(amount)` (burns DIEM, unlocks sVVV"));
|
|
3966
|
+
console.log(chalk5.yellow(" at your average mint rate), then `initiateUnstake` + wait the"));
|
|
3967
|
+
console.log(chalk5.yellow(" 7-day staking cooldown, then `finalizeUnstake`."));
|
|
3968
|
+
console.log(chalk5.dim(" x402 alternative (pay-per-request in USDC, no DIEM lock):"));
|
|
3969
|
+
console.log(chalk5.dim(" https://docs.venice.ai/overview/guides/generating-api-key-agent#paying-for-inference"));
|
|
3875
3970
|
console.log();
|
|
3876
3971
|
if (!opts.yes) {
|
|
3877
3972
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
3878
3973
|
try {
|
|
3879
3974
|
const answer = (await rl.question(` Lock ${opts.amount} sVVV for DIEM credits? [y/N] `)).trim().toLowerCase();
|
|
3880
3975
|
if (answer !== "y" && answer !== "yes") {
|
|
3881
|
-
console.log(
|
|
3976
|
+
console.log(chalk5.dim(" Aborted."));
|
|
3882
3977
|
process.exit(0);
|
|
3883
3978
|
}
|
|
3884
3979
|
} finally {
|
|
@@ -3886,7 +3981,7 @@ function registerVeniceCommands(program2) {
|
|
|
3886
3981
|
}
|
|
3887
3982
|
}
|
|
3888
3983
|
const diemBefore = currentDiem;
|
|
3889
|
-
const mintSpinner =
|
|
3984
|
+
const mintSpinner = ora4(`Minting DIEM from ${opts.amount} sVVV...`).start();
|
|
3890
3985
|
try {
|
|
3891
3986
|
const hash = await writeContractWithRetry({
|
|
3892
3987
|
account,
|
|
@@ -3905,28 +4000,28 @@ function registerVeniceCommands(program2) {
|
|
|
3905
4000
|
});
|
|
3906
4001
|
const delta = diemAfter - diemBefore;
|
|
3907
4002
|
mintSpinner.succeed(
|
|
3908
|
-
`Minted ${
|
|
4003
|
+
`Minted ${formatUnits7(delta > 0n ? delta : diemAfter, 18)} DIEM` + (delta > 0n ? ` (balance: ${formatUnits7(diemAfter, 18)})` : "")
|
|
3909
4004
|
);
|
|
3910
|
-
console.log(
|
|
3911
|
-
console.log(
|
|
3912
|
-
console.log(
|
|
3913
|
-
console.log(
|
|
3914
|
-
console.log(
|
|
4005
|
+
console.log(chalk5.dim(` Tx: ${hash}`));
|
|
4006
|
+
console.log(chalk5.yellow(" Note: Venice requires \u2265 0.1 DIEM to spend ANY DIEM credit, and"));
|
|
4007
|
+
console.log(chalk5.yellow(" refreshes daily allocation at 00:00 UTC. Below threshold,"));
|
|
4008
|
+
console.log(chalk5.yellow(" inference will continue to 402."));
|
|
4009
|
+
console.log(chalk5.dim(' Once over threshold: sherwood venice infer --model <id> --prompt "..."'));
|
|
3915
4010
|
} catch (err) {
|
|
3916
4011
|
mintSpinner.fail("mintDiem failed");
|
|
3917
|
-
console.error(
|
|
4012
|
+
console.error(chalk5.red(formatContractError(err)));
|
|
3918
4013
|
process.exit(1);
|
|
3919
4014
|
}
|
|
3920
4015
|
});
|
|
3921
4016
|
venice.command("status").description("Show Venice inference status: sVVV balances, DIEM, API key").requiredOption("--vault <address>", "Vault address").action(async (opts) => {
|
|
3922
4017
|
const vaultAddress = opts.vault;
|
|
3923
|
-
if (!
|
|
3924
|
-
console.error(
|
|
4018
|
+
if (!isAddress6(vaultAddress)) {
|
|
4019
|
+
console.error(chalk5.red(`Invalid vault address: ${opts.vault}`));
|
|
3925
4020
|
process.exit(1);
|
|
3926
4021
|
}
|
|
3927
4022
|
const client = getPublicClient();
|
|
3928
4023
|
const account = getAccount();
|
|
3929
|
-
const spinner =
|
|
4024
|
+
const spinner = ora4("Loading Venice status...").start();
|
|
3930
4025
|
try {
|
|
3931
4026
|
const [assetAddress, totalDeposited, agents] = await Promise.all([
|
|
3932
4027
|
client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "asset" }),
|
|
@@ -3980,34 +4075,34 @@ function registerVeniceCommands(program2) {
|
|
|
3980
4075
|
spinner.stop();
|
|
3981
4076
|
const profit = assetBalance > totalDeposited ? assetBalance - totalDeposited : 0n;
|
|
3982
4077
|
console.log();
|
|
3983
|
-
console.log(
|
|
3984
|
-
console.log(
|
|
3985
|
-
console.log(
|
|
3986
|
-
console.log(` Profit available: ${
|
|
3987
|
-
console.log(` VVV (unstaked): ${
|
|
3988
|
-
console.log(
|
|
4078
|
+
console.log(chalk5.bold("Venice Inference Status"));
|
|
4079
|
+
console.log(chalk5.dim("\u2500".repeat(50)));
|
|
4080
|
+
console.log(chalk5.bold("\n Vault"));
|
|
4081
|
+
console.log(` Profit available: ${formatUnits7(profit, assetDecimals)} ${assetSymbol}`);
|
|
4082
|
+
console.log(` VVV (unstaked): ${formatUnits7(vaultVvvBalance, 18)}`);
|
|
4083
|
+
console.log(chalk5.bold("\n Agent sVVV Balances"));
|
|
3989
4084
|
for (const { agent, balance } of agentBalances) {
|
|
3990
4085
|
const isMe = agent.toLowerCase() === account.address.toLowerCase();
|
|
3991
|
-
const label = isMe ?
|
|
3992
|
-
console.log(` ${label}: ${
|
|
3993
|
-
}
|
|
3994
|
-
console.log(
|
|
3995
|
-
console.log(` sVVV: ${
|
|
3996
|
-
console.log(` Pending rewards: ${
|
|
3997
|
-
console.log(` DIEM (credits): ${
|
|
3998
|
-
console.log(
|
|
3999
|
-
console.log(
|
|
4000
|
-
console.log(` Key: ${apiKey ? `${apiKey.slice(0, 8)}...${apiKey.slice(-4)}` :
|
|
4001
|
-
console.log(` Status: ${apiKeyValid ?
|
|
4086
|
+
const label = isMe ? chalk5.green(`${agent} (you)`) : agent;
|
|
4087
|
+
console.log(` ${label}: ${formatUnits7(balance, 18)} sVVV`);
|
|
4088
|
+
}
|
|
4089
|
+
console.log(chalk5.bold("\n Your Wallet"));
|
|
4090
|
+
console.log(` sVVV: ${formatUnits7(mySvvv, 18)}`);
|
|
4091
|
+
console.log(` Pending rewards: ${formatUnits7(myPending, 18)} VVV`);
|
|
4092
|
+
console.log(` DIEM (credits): ${formatUnits7(myDiem, 18)}`);
|
|
4093
|
+
console.log(chalk5.dim(" (mint via `sherwood venice mint-diem --amount <sVVV>`)"));
|
|
4094
|
+
console.log(chalk5.bold("\n Venice API"));
|
|
4095
|
+
console.log(` Key: ${apiKey ? `${apiKey.slice(0, 8)}...${apiKey.slice(-4)}` : chalk5.dim("not provisioned")}`);
|
|
4096
|
+
console.log(` Status: ${apiKeyValid ? chalk5.green("valid") : chalk5.red("invalid/missing")}`);
|
|
4002
4097
|
console.log();
|
|
4003
4098
|
} catch (err) {
|
|
4004
4099
|
spinner.fail("Failed to load status");
|
|
4005
|
-
console.error(
|
|
4100
|
+
console.error(chalk5.red(formatContractError(err)));
|
|
4006
4101
|
process.exit(1);
|
|
4007
4102
|
}
|
|
4008
4103
|
});
|
|
4009
4104
|
venice.command("models").description("List available Venice inference models").action(async () => {
|
|
4010
|
-
const spinner =
|
|
4105
|
+
const spinner = ora4("Fetching Venice models...").start();
|
|
4011
4106
|
try {
|
|
4012
4107
|
const models = await listModels();
|
|
4013
4108
|
spinner.succeed(`${models.length} models available`);
|
|
@@ -4018,7 +4113,7 @@ function registerVeniceCommands(program2) {
|
|
|
4018
4113
|
console.log();
|
|
4019
4114
|
} catch (err) {
|
|
4020
4115
|
spinner.fail("Failed to list models");
|
|
4021
|
-
console.error(
|
|
4116
|
+
console.error(chalk5.red(formatContractError(err)));
|
|
4022
4117
|
process.exit(1);
|
|
4023
4118
|
}
|
|
4024
4119
|
});
|
|
@@ -4038,13 +4133,13 @@ ${data}
|
|
|
4038
4133
|
|
|
4039
4134
|
${opts.prompt}`;
|
|
4040
4135
|
} catch (err) {
|
|
4041
|
-
console.error(
|
|
4042
|
-
console.error(
|
|
4136
|
+
console.error(chalk5.red(`Failed to read data file: ${opts.data}`));
|
|
4137
|
+
console.error(chalk5.red(formatContractError(err)));
|
|
4043
4138
|
process.exit(1);
|
|
4044
4139
|
}
|
|
4045
4140
|
}
|
|
4046
4141
|
messages.push({ role: "user", content: userContent });
|
|
4047
|
-
const spinner =
|
|
4142
|
+
const spinner = ora4(`Running inference (${opts.model})...`).start();
|
|
4048
4143
|
try {
|
|
4049
4144
|
const result = await chatCompletion({
|
|
4050
4145
|
model: opts.model,
|
|
@@ -4061,10 +4156,10 @@ ${opts.prompt}`;
|
|
|
4061
4156
|
console.log();
|
|
4062
4157
|
console.log(result.content);
|
|
4063
4158
|
console.log();
|
|
4064
|
-
console.log(
|
|
4159
|
+
console.log(chalk5.dim(`Model: ${result.model} | Tokens: ${result.usage.promptTokens} in, ${result.usage.completionTokens} out, ${result.usage.totalTokens} total`));
|
|
4065
4160
|
}
|
|
4066
4161
|
try {
|
|
4067
|
-
const { createVeniceInferenceAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-
|
|
4162
|
+
const { createVeniceInferenceAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-32PEJETG.js");
|
|
4068
4163
|
const { keccak256: keccak2563, toHex, isAddress: isAddr } = await import("viem");
|
|
4069
4164
|
const { getChainContracts: getChainContracts2 } = await import("./config-ZGFS4I7K.js");
|
|
4070
4165
|
const { getChain: getActiveChain } = await import("./network-HCFCNZUI.js");
|
|
@@ -4078,27 +4173,27 @@ ${opts.prompt}`;
|
|
|
4078
4173
|
vaultRecipient
|
|
4079
4174
|
);
|
|
4080
4175
|
if (uid !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
|
|
4081
|
-
console.log(
|
|
4176
|
+
console.log(chalk5.dim(`Attested: ${getEasScanUrl2(uid)}`));
|
|
4082
4177
|
}
|
|
4083
4178
|
} catch {
|
|
4084
4179
|
}
|
|
4085
4180
|
} catch (err) {
|
|
4086
4181
|
spinner.fail("Inference failed");
|
|
4087
|
-
console.error(
|
|
4182
|
+
console.error(chalk5.red(formatContractError(err)));
|
|
4088
4183
|
process.exit(1);
|
|
4089
4184
|
}
|
|
4090
4185
|
});
|
|
4091
4186
|
}
|
|
4092
4187
|
|
|
4093
4188
|
// src/commands/allowance.ts
|
|
4094
|
-
import { parseUnits as
|
|
4095
|
-
import
|
|
4096
|
-
import
|
|
4189
|
+
import { parseUnits as parseUnits7, formatUnits as formatUnits8, isAddress as isAddress7 } from "viem";
|
|
4190
|
+
import chalk6 from "chalk";
|
|
4191
|
+
import ora5 from "ora";
|
|
4097
4192
|
|
|
4098
4193
|
// src/strategies/allowance-disburse.ts
|
|
4099
|
-
import { encodeFunctionData as encodeFunctionData8, parseUnits as
|
|
4194
|
+
import { encodeFunctionData as encodeFunctionData8, parseUnits as parseUnits6 } from "viem";
|
|
4100
4195
|
function buildDisburseBatch(config, vaultAddress, agents, assetAddress, assetDecimals, minUsdc, swapPath) {
|
|
4101
|
-
const assetAmount =
|
|
4196
|
+
const assetAmount = parseUnits6(config.amount, assetDecimals);
|
|
4102
4197
|
const isUsdc = assetAddress.toLowerCase() === TOKENS().USDC.toLowerCase();
|
|
4103
4198
|
const isWeth = assetAddress.toLowerCase() === TOKENS().WETH.toLowerCase();
|
|
4104
4199
|
const calls = [];
|
|
@@ -4172,18 +4267,18 @@ function registerAllowanceCommands(program2) {
|
|
|
4172
4267
|
const allowance = program2.command("allowance").description("Disburse vault profits to agent wallets");
|
|
4173
4268
|
allowance.command("disburse").description("Swap vault profits \u2192 USDC \u2192 distribute to all agent operator wallets").requiredOption("--vault <address>", "Vault address").requiredOption("--amount <amount>", "Deposit token amount to convert & distribute (e.g. 500)").option("--fee <tier>", "Fee tier for asset \u2192 USDC swap (500, 3000, 10000)", "3000").option("--slippage <bps>", "Slippage tolerance in bps", "100").option("--execute", "Unavailable \u2014 the vault has no owner-direct batch path (V-C3); fails with guidance", false).action(async (opts) => {
|
|
4174
4269
|
const vaultAddress = opts.vault;
|
|
4175
|
-
if (!
|
|
4176
|
-
console.error(
|
|
4270
|
+
if (!isAddress7(vaultAddress)) {
|
|
4271
|
+
console.error(chalk6.red(`Invalid vault address: ${opts.vault}`));
|
|
4177
4272
|
process.exit(1);
|
|
4178
4273
|
}
|
|
4179
4274
|
if (opts.execute) {
|
|
4180
4275
|
console.error(
|
|
4181
|
-
|
|
4276
|
+
chalk6.red(
|
|
4182
4277
|
"allowance disburse --execute is not available: the vault no longer exposes owner-direct batch execution (executeBatch was removed on-chain, V-C3)."
|
|
4183
4278
|
)
|
|
4184
4279
|
);
|
|
4185
4280
|
console.error(
|
|
4186
|
-
|
|
4281
|
+
chalk6.dim(
|
|
4187
4282
|
"Vault funds can only move through governor-approved batches (executeGovernorBatch via the proposal lifecycle). Run without --execute to simulate the batch."
|
|
4188
4283
|
)
|
|
4189
4284
|
);
|
|
@@ -4191,12 +4286,12 @@ function registerAllowanceCommands(program2) {
|
|
|
4191
4286
|
}
|
|
4192
4287
|
const fee = Number(opts.fee);
|
|
4193
4288
|
if (!VALID_FEES.includes(fee)) {
|
|
4194
|
-
console.error(
|
|
4289
|
+
console.error(chalk6.red(`Invalid fee tier. Valid: ${VALID_FEES.join(", ")}`));
|
|
4195
4290
|
process.exit(1);
|
|
4196
4291
|
}
|
|
4197
4292
|
const slippageBps = Number(opts.slippage);
|
|
4198
4293
|
const client = getPublicClient();
|
|
4199
|
-
const spinner =
|
|
4294
|
+
const spinner = ora5("Reading vault state...").start();
|
|
4200
4295
|
let assetAddress;
|
|
4201
4296
|
let assetDecimals;
|
|
4202
4297
|
let assetSymbol;
|
|
@@ -4217,25 +4312,25 @@ function registerAllowanceCommands(program2) {
|
|
|
4217
4312
|
spinner.stop();
|
|
4218
4313
|
} catch (err) {
|
|
4219
4314
|
spinner.fail("Failed to read vault state");
|
|
4220
|
-
console.error(
|
|
4315
|
+
console.error(chalk6.red(formatContractError(err)));
|
|
4221
4316
|
process.exit(1);
|
|
4222
4317
|
}
|
|
4223
4318
|
if (agents.length === 0) {
|
|
4224
|
-
console.error(
|
|
4319
|
+
console.error(chalk6.red("No agents registered in vault. Register agents first."));
|
|
4225
4320
|
process.exit(1);
|
|
4226
4321
|
}
|
|
4227
|
-
const requestedAmount =
|
|
4322
|
+
const requestedAmount = parseUnits7(opts.amount, assetDecimals);
|
|
4228
4323
|
const profit = assetBalance > totalDeposited ? assetBalance - totalDeposited : 0n;
|
|
4229
4324
|
const isUsdc = assetAddress.toLowerCase() === TOKENS().USDC.toLowerCase();
|
|
4230
4325
|
const isWeth = assetAddress.toLowerCase() === TOKENS().WETH.toLowerCase();
|
|
4231
4326
|
console.log();
|
|
4232
|
-
console.log(
|
|
4233
|
-
console.log(
|
|
4327
|
+
console.log(chalk6.bold("Allowance Disburse"));
|
|
4328
|
+
console.log(chalk6.dim("\u2500".repeat(40)));
|
|
4234
4329
|
console.log(` Asset: ${assetSymbol} (${assetDecimals} decimals)`);
|
|
4235
4330
|
console.log(` Amount: ${opts.amount} ${assetSymbol}`);
|
|
4236
|
-
console.log(` Vault balance: ${
|
|
4237
|
-
console.log(` Net deposited (est.): ${
|
|
4238
|
-
console.log(` Profit: ${
|
|
4331
|
+
console.log(` Vault balance: ${formatUnits8(assetBalance, assetDecimals)} ${assetSymbol}`);
|
|
4332
|
+
console.log(` Net deposited (est.): ${formatUnits8(totalDeposited, assetDecimals)} ${assetSymbol}`);
|
|
4333
|
+
console.log(` Profit: ${formatUnits8(profit, assetDecimals)} ${assetSymbol}`);
|
|
4239
4334
|
console.log(` Agents: ${agents.length} (USDC will be split equally)`);
|
|
4240
4335
|
if (!isUsdc) {
|
|
4241
4336
|
console.log(` Routing: ${isWeth ? `WETH \u2192 USDC (fee ${fee})` : `${assetSymbol} \u2192 WETH \u2192 USDC (fee ${fee})`}`);
|
|
@@ -4244,8 +4339,8 @@ function registerAllowanceCommands(program2) {
|
|
|
4244
4339
|
console.log(` Vault: ${vaultAddress}`);
|
|
4245
4340
|
console.log();
|
|
4246
4341
|
if (requestedAmount > profit) {
|
|
4247
|
-
console.warn(
|
|
4248
|
-
console.warn(
|
|
4342
|
+
console.warn(chalk6.yellow(` Warning: amount (${opts.amount}) exceeds available profit (${formatUnits8(profit, assetDecimals)})`));
|
|
4343
|
+
console.warn(chalk6.yellow(" This will use deposited capital, not just profits."));
|
|
4249
4344
|
console.log();
|
|
4250
4345
|
}
|
|
4251
4346
|
let minUsdc;
|
|
@@ -4253,7 +4348,7 @@ function registerAllowanceCommands(program2) {
|
|
|
4253
4348
|
if (isUsdc) {
|
|
4254
4349
|
minUsdc = requestedAmount;
|
|
4255
4350
|
} else {
|
|
4256
|
-
const quoteSpinner =
|
|
4351
|
+
const quoteSpinner = ora5("Fetching Uniswap quote...").start();
|
|
4257
4352
|
try {
|
|
4258
4353
|
let amountOut;
|
|
4259
4354
|
if (isWeth) {
|
|
@@ -4278,17 +4373,17 @@ function registerAllowanceCommands(program2) {
|
|
|
4278
4373
|
}
|
|
4279
4374
|
minUsdc = applySlippage(amountOut, slippageBps);
|
|
4280
4375
|
quoteSpinner.succeed(
|
|
4281
|
-
`Quote: ${
|
|
4376
|
+
`Quote: ${formatUnits8(amountOut, 6)} USDC (min: ${formatUnits8(minUsdc, 6)}, per agent: ${formatUnits8(minUsdc / BigInt(agents.length), 6)})`
|
|
4282
4377
|
);
|
|
4283
4378
|
} catch (err) {
|
|
4284
4379
|
quoteSpinner.fail("Failed to fetch quote");
|
|
4285
|
-
console.error(
|
|
4380
|
+
console.error(chalk6.red(formatContractError(err)));
|
|
4286
4381
|
process.exit(1);
|
|
4287
4382
|
}
|
|
4288
4383
|
}
|
|
4289
4384
|
const perAgent = minUsdc / BigInt(agents.length);
|
|
4290
4385
|
if (isUsdc) {
|
|
4291
|
-
console.log(
|
|
4386
|
+
console.log(chalk6.dim(` Per agent: ${formatUnits8(perAgent, 6)} USDC`));
|
|
4292
4387
|
console.log();
|
|
4293
4388
|
}
|
|
4294
4389
|
const config = {
|
|
@@ -4298,24 +4393,24 @@ function registerAllowanceCommands(program2) {
|
|
|
4298
4393
|
};
|
|
4299
4394
|
const calls = buildDisburseBatch(config, vaultAddress, agents, assetAddress, assetDecimals, minUsdc, swapPath);
|
|
4300
4395
|
console.log();
|
|
4301
|
-
console.log(
|
|
4396
|
+
console.log(chalk6.bold(`Batch calls (${calls.length}):`));
|
|
4302
4397
|
console.log(formatBatch(calls));
|
|
4303
4398
|
console.log();
|
|
4304
4399
|
console.log();
|
|
4305
4400
|
console.log(
|
|
4306
|
-
|
|
4401
|
+
chalk6.yellow(
|
|
4307
4402
|
"Dry run complete. On-chain execution is unavailable \u2014 the vault has no owner-direct batch path (V-C3); disbursement requires a governor proposal."
|
|
4308
4403
|
)
|
|
4309
4404
|
);
|
|
4310
4405
|
});
|
|
4311
4406
|
allowance.command("status").description("Show vault profit and agent USDC balances").requiredOption("--vault <address>", "Vault address").action(async (opts) => {
|
|
4312
4407
|
const vaultAddress = opts.vault;
|
|
4313
|
-
if (!
|
|
4314
|
-
console.error(
|
|
4408
|
+
if (!isAddress7(vaultAddress)) {
|
|
4409
|
+
console.error(chalk6.red(`Invalid vault address: ${opts.vault}`));
|
|
4315
4410
|
process.exit(1);
|
|
4316
4411
|
}
|
|
4317
4412
|
const client = getPublicClient();
|
|
4318
|
-
const spinner =
|
|
4413
|
+
const spinner = ora5("Loading allowance status...").start();
|
|
4319
4414
|
try {
|
|
4320
4415
|
const [assetAddress, totalDeposited, agents] = await Promise.all([
|
|
4321
4416
|
client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "asset" }),
|
|
@@ -4342,23 +4437,23 @@ function registerAllowanceCommands(program2) {
|
|
|
4342
4437
|
const profit = assetBalance > totalDeposited ? assetBalance - totalDeposited : 0n;
|
|
4343
4438
|
const account = getAccount();
|
|
4344
4439
|
console.log();
|
|
4345
|
-
console.log(
|
|
4346
|
-
console.log(
|
|
4347
|
-
console.log(
|
|
4440
|
+
console.log(chalk6.bold("Allowance Status"));
|
|
4441
|
+
console.log(chalk6.dim("\u2500".repeat(50)));
|
|
4442
|
+
console.log(chalk6.bold("\n Vault"));
|
|
4348
4443
|
console.log(` Asset: ${assetSymbol}`);
|
|
4349
|
-
console.log(` Balance: ${
|
|
4350
|
-
console.log(` Net deposited (est.): ${
|
|
4351
|
-
console.log(` Profit: ${
|
|
4352
|
-
console.log(
|
|
4444
|
+
console.log(` Balance: ${formatUnits8(assetBalance, assetDecimals)} ${assetSymbol}`);
|
|
4445
|
+
console.log(` Net deposited (est.): ${formatUnits8(totalDeposited, assetDecimals)} ${assetSymbol}`);
|
|
4446
|
+
console.log(` Profit: ${formatUnits8(profit, assetDecimals)} ${assetSymbol}`);
|
|
4447
|
+
console.log(chalk6.bold("\n Agent USDC Balances"));
|
|
4353
4448
|
for (const { agent, balance } of agentBalances) {
|
|
4354
4449
|
const isMe = agent.toLowerCase() === account.address.toLowerCase();
|
|
4355
|
-
const label = isMe ?
|
|
4356
|
-
console.log(` ${label}: ${
|
|
4450
|
+
const label = isMe ? chalk6.green(`${agent} (you)`) : agent;
|
|
4451
|
+
console.log(` ${label}: ${formatUnits8(balance, 6)} USDC`);
|
|
4357
4452
|
}
|
|
4358
4453
|
console.log();
|
|
4359
4454
|
} catch (err) {
|
|
4360
4455
|
spinner.fail("Failed to load status");
|
|
4361
|
-
console.error(
|
|
4456
|
+
console.error(chalk6.red(formatContractError(err)));
|
|
4362
4457
|
process.exit(1);
|
|
4363
4458
|
}
|
|
4364
4459
|
});
|
|
@@ -4366,14 +4461,14 @@ function registerAllowanceCommands(program2) {
|
|
|
4366
4461
|
|
|
4367
4462
|
// src/commands/identity.ts
|
|
4368
4463
|
import { generatePrivateKey } from "viem/accounts";
|
|
4369
|
-
import
|
|
4370
|
-
import
|
|
4464
|
+
import chalk7 from "chalk";
|
|
4465
|
+
import ora6 from "ora";
|
|
4371
4466
|
import { SDK } from "agent0-sdk";
|
|
4372
4467
|
|
|
4373
4468
|
// src/lib/virtuals.ts
|
|
4374
4469
|
import { execFile } from "child_process";
|
|
4375
4470
|
import { promisify } from "util";
|
|
4376
|
-
import { createPublicClient, http, recoverMessageAddress, isAddress as
|
|
4471
|
+
import { createPublicClient, http, recoverMessageAddress, isAddress as isAddress8 } from "viem";
|
|
4377
4472
|
import { base, baseSepolia } from "viem/chains";
|
|
4378
4473
|
var execFileAsync = promisify(execFile);
|
|
4379
4474
|
var BINDING_VERSION = "v1";
|
|
@@ -4460,7 +4555,7 @@ function parseVidTag(message) {
|
|
|
4460
4555
|
if (message.indexOf("[vid:") !== message.lastIndexOf("[vid:")) return null;
|
|
4461
4556
|
const match = message.match(VID_TAG_RE);
|
|
4462
4557
|
if (!match) return null;
|
|
4463
|
-
if (!
|
|
4558
|
+
if (!isAddress8(match[4])) return null;
|
|
4464
4559
|
return {
|
|
4465
4560
|
version: match[1],
|
|
4466
4561
|
issuingChainId: parseInt(match[2], 10),
|
|
@@ -4567,7 +4662,7 @@ async function acpWalletAddress() {
|
|
|
4567
4662
|
if (!json) return null;
|
|
4568
4663
|
for (const key of ["address", "evmAddress", "evm", "wallet"]) {
|
|
4569
4664
|
const value = json[key];
|
|
4570
|
-
if (typeof value === "string" &&
|
|
4665
|
+
if (typeof value === "string" && isAddress8(value)) return value;
|
|
4571
4666
|
}
|
|
4572
4667
|
return null;
|
|
4573
4668
|
}
|
|
@@ -4626,11 +4721,11 @@ function registerIdentityCommands(program2) {
|
|
|
4626
4721
|
const account = getAccount();
|
|
4627
4722
|
const existingId = getAgentId();
|
|
4628
4723
|
if (existingId) {
|
|
4629
|
-
console.log(
|
|
4630
|
-
console.log(
|
|
4724
|
+
console.log(chalk7.yellow(`You already have an agent identity saved: #${existingId}`));
|
|
4725
|
+
console.log(chalk7.dim(" Minting a new one anyway. The old ID is not affected."));
|
|
4631
4726
|
console.log();
|
|
4632
4727
|
}
|
|
4633
|
-
const spinner =
|
|
4728
|
+
const spinner = ora6("Initializing Agent0 SDK...").start();
|
|
4634
4729
|
try {
|
|
4635
4730
|
const sdk = getAgent0SDK();
|
|
4636
4731
|
spinner.text = "Creating agent profile...";
|
|
@@ -4642,22 +4737,22 @@ function registerIdentityCommands(program2) {
|
|
|
4642
4737
|
const agentId = agent.agentId;
|
|
4643
4738
|
if (!agentId) {
|
|
4644
4739
|
spinner.warn("Identity registered but could not read agentId");
|
|
4645
|
-
console.log(
|
|
4740
|
+
console.log(chalk7.dim(" Check the transaction on the explorer."));
|
|
4646
4741
|
return;
|
|
4647
4742
|
}
|
|
4648
4743
|
const tokenId = Number(agentId.includes(":") ? agentId.split(":")[1] : agentId);
|
|
4649
4744
|
setAgentId(tokenId);
|
|
4650
4745
|
spinner.succeed(`Agent identity registered: #${tokenId}`);
|
|
4651
|
-
console.log(
|
|
4652
|
-
console.log(
|
|
4653
|
-
console.log(
|
|
4654
|
-
console.log(
|
|
4746
|
+
console.log(chalk7.dim(` Agent0 ID: ${agentId}`));
|
|
4747
|
+
console.log(chalk7.dim(` Name: ${opts.name}`));
|
|
4748
|
+
console.log(chalk7.dim(` Owner: ${account.address}`));
|
|
4749
|
+
console.log(chalk7.dim(` Saved to ~/.sherwood/config.json`));
|
|
4655
4750
|
console.log();
|
|
4656
|
-
console.log(
|
|
4657
|
-
console.log(
|
|
4751
|
+
console.log(chalk7.green("You can now create funds:"));
|
|
4752
|
+
console.log(chalk7.dim(` sherwood fund create --agent-id ${tokenId} --subdomain <name> --name <name>`));
|
|
4658
4753
|
} catch (err) {
|
|
4659
4754
|
spinner.fail("Failed to register identity");
|
|
4660
|
-
console.error(
|
|
4755
|
+
console.error(chalk7.red(formatContractError(err)));
|
|
4661
4756
|
process.exit(1);
|
|
4662
4757
|
}
|
|
4663
4758
|
});
|
|
@@ -4666,7 +4761,7 @@ function registerIdentityCommands(program2) {
|
|
|
4666
4761
|
const client = getPublicClient();
|
|
4667
4762
|
const registry = AGENT_REGISTRY().IDENTITY_REGISTRY;
|
|
4668
4763
|
const tokenId = Number(opts.id);
|
|
4669
|
-
const spinner =
|
|
4764
|
+
const spinner = ora6(`Verifying ownership of agent #${tokenId}...`).start();
|
|
4670
4765
|
try {
|
|
4671
4766
|
const owner = await client.readContract({
|
|
4672
4767
|
address: registry,
|
|
@@ -4680,11 +4775,11 @@ function registerIdentityCommands(program2) {
|
|
|
4680
4775
|
}
|
|
4681
4776
|
setAgentId(tokenId);
|
|
4682
4777
|
spinner.succeed(`Agent #${tokenId} loaded and saved to config`);
|
|
4683
|
-
console.log(
|
|
4684
|
-
console.log(
|
|
4778
|
+
console.log(chalk7.dim(` Owner: ${account.address}`));
|
|
4779
|
+
console.log(chalk7.dim(` Saved to ~/.sherwood/config.json`));
|
|
4685
4780
|
} catch (err) {
|
|
4686
4781
|
spinner.fail("Failed to load identity");
|
|
4687
|
-
console.error(
|
|
4782
|
+
console.error(chalk7.red(formatContractError(err)));
|
|
4688
4783
|
process.exit(1);
|
|
4689
4784
|
}
|
|
4690
4785
|
});
|
|
@@ -4692,10 +4787,10 @@ function registerIdentityCommands(program2) {
|
|
|
4692
4787
|
const account = getAccount();
|
|
4693
4788
|
const issuingChainId = opts.walletOnly ? WALLET_ONLY_CHAIN_ID : parseInt(opts.chainId, 10);
|
|
4694
4789
|
if (!opts.walletOnly && !ISSUING_CHAINS[issuingChainId]) {
|
|
4695
|
-
console.error(
|
|
4790
|
+
console.error(chalk7.red(`Unsupported issuing chain ${opts.chainId}. Supported: ${Object.keys(ISSUING_CHAINS).join(", ")}`));
|
|
4696
4791
|
process.exit(1);
|
|
4697
4792
|
}
|
|
4698
|
-
const spinner =
|
|
4793
|
+
const spinner = ora6("Resolving economyOS agent...").start();
|
|
4699
4794
|
try {
|
|
4700
4795
|
let virtualsWallet = opts.wallet;
|
|
4701
4796
|
let virtualsAgentId;
|
|
@@ -4708,8 +4803,8 @@ function registerIdentityCommands(program2) {
|
|
|
4708
4803
|
}
|
|
4709
4804
|
if (!virtualsWallet) {
|
|
4710
4805
|
spinner.fail("Could not resolve the economyOS agent wallet");
|
|
4711
|
-
console.error(
|
|
4712
|
-
console.error(
|
|
4806
|
+
console.error(chalk7.dim(" Install + authenticate the acp CLI (npm i -g @virtuals-protocol/acp-cli; acp configure)"));
|
|
4807
|
+
console.error(chalk7.dim(" or pass --wallet <address> explicitly."));
|
|
4713
4808
|
process.exit(1);
|
|
4714
4809
|
}
|
|
4715
4810
|
let tokenId;
|
|
@@ -4730,10 +4825,10 @@ function registerIdentityCommands(program2) {
|
|
|
4730
4825
|
});
|
|
4731
4826
|
if (results.length === 0) {
|
|
4732
4827
|
spinner.fail(`No ERC-8004 registration found for ${virtualsWallet} on chain ${issuingChainId}`);
|
|
4733
|
-
console.error(
|
|
4734
|
-
console.error(
|
|
4735
|
-
console.error(
|
|
4736
|
-
console.error(
|
|
4828
|
+
console.error(chalk7.dim(" Register first via the Virtuals CLI:"));
|
|
4829
|
+
console.error(chalk7.dim(` acp agent register-erc8004 --agent-id ${virtualsAgentId ?? "<id>"} --chain-id ${issuingChainId}`));
|
|
4830
|
+
console.error(chalk7.dim(" Then re-run this command (or pass --agent-id <tokenId> directly)."));
|
|
4831
|
+
console.error(chalk7.dim(" No registration possible yet? Link without one: --wallet-only"));
|
|
4737
4832
|
process.exit(1);
|
|
4738
4833
|
}
|
|
4739
4834
|
if (results.length > 1) {
|
|
@@ -4741,9 +4836,9 @@ function registerIdentityCommands(program2) {
|
|
|
4741
4836
|
for (const a of results) {
|
|
4742
4837
|
const idStr2 = String(a.agentId);
|
|
4743
4838
|
const tid = idStr2.includes(":") ? idStr2.split(":")[1] : idStr2;
|
|
4744
|
-
console.error(
|
|
4839
|
+
console.error(chalk7.dim(` #${tid} ${a.name || "(no name)"}`));
|
|
4745
4840
|
}
|
|
4746
|
-
console.error(
|
|
4841
|
+
console.error(chalk7.green(" Re-run with: sherwood identity link-virtuals --agent-id <tokenId>"));
|
|
4747
4842
|
process.exit(1);
|
|
4748
4843
|
}
|
|
4749
4844
|
const idStr = String(results[0].agentId);
|
|
@@ -4770,17 +4865,17 @@ function registerIdentityCommands(program2) {
|
|
|
4770
4865
|
spinner.text = "Requesting binding signature from the economyOS wallet (acp wallet sign-message)...";
|
|
4771
4866
|
bindingSig = await acpSignMessage(bindingMessage) ?? void 0;
|
|
4772
4867
|
if (bindingSig) {
|
|
4773
|
-
console.log(
|
|
4868
|
+
console.log(chalk7.dim(" Binding signed automatically via acp."));
|
|
4774
4869
|
}
|
|
4775
4870
|
}
|
|
4776
4871
|
if (!bindingSig) {
|
|
4777
4872
|
spinner.fail("Binding signature required (economyOS wallet differs from your Sherwood wallet)");
|
|
4778
4873
|
console.error();
|
|
4779
|
-
console.error(
|
|
4780
|
-
console.error(
|
|
4874
|
+
console.error(chalk7.bold(" Sign this exact message with the economyOS agent wallet:"));
|
|
4875
|
+
console.error(chalk7.green(` ${bindingMessage}`));
|
|
4781
4876
|
console.error();
|
|
4782
|
-
console.error(
|
|
4783
|
-
console.error(
|
|
4877
|
+
console.error(chalk7.dim(` acp wallet sign-message --message "${bindingMessage}" --json`));
|
|
4878
|
+
console.error(chalk7.dim(" Then re-run with --binding-sig <signature>."));
|
|
4784
4879
|
process.exit(1);
|
|
4785
4880
|
}
|
|
4786
4881
|
spinner.text = "Verifying binding signature...";
|
|
@@ -4793,8 +4888,8 @@ function registerIdentityCommands(program2) {
|
|
|
4793
4888
|
});
|
|
4794
4889
|
if (!ok) {
|
|
4795
4890
|
spinner.fail("Binding signature does not recover to the economyOS wallet");
|
|
4796
|
-
console.error(
|
|
4797
|
-
console.error(
|
|
4891
|
+
console.error(chalk7.dim(` Expected signer: ${virtualsWallet}`));
|
|
4892
|
+
console.error(chalk7.dim(` Signed message must be exactly: ${bindingMessage}`));
|
|
4798
4893
|
process.exit(1);
|
|
4799
4894
|
}
|
|
4800
4895
|
binding = { sig: bindingSig, message: bindingMessage };
|
|
@@ -4807,25 +4902,25 @@ function registerIdentityCommands(program2) {
|
|
|
4807
4902
|
spinner.succeed(
|
|
4808
4903
|
opts.walletOnly ? "Virtuals identity linked: wallet-only (no ERC-8004 \u2014 upgrade later by re-linking after registration)" : `Virtuals identity linked: agent #${tokenId} (chain ${issuingChainId})`
|
|
4809
4904
|
);
|
|
4810
|
-
console.log(
|
|
4811
|
-
if (virtualsAgentId) console.log(
|
|
4812
|
-
console.log(
|
|
4813
|
-
console.log(
|
|
4905
|
+
console.log(chalk7.dim(` economyOS wallet: ${virtualsWallet}`));
|
|
4906
|
+
if (virtualsAgentId) console.log(chalk7.dim(` Virtuals agent: ${virtualsAgentId}`));
|
|
4907
|
+
console.log(chalk7.dim(` Sherwood wallet: ${account.address}${self ? " (same as economyOS wallet \u2014 no binding needed)" : " (bound via signature)"}`));
|
|
4908
|
+
console.log(chalk7.dim(` Saved to ~/.sherwood/config.json`));
|
|
4814
4909
|
console.log();
|
|
4815
|
-
console.log(
|
|
4816
|
-
console.log(
|
|
4910
|
+
console.log(chalk7.green("You can now create or join funds with this identity:"));
|
|
4911
|
+
console.log(chalk7.dim(` sherwood fund create --agent-id ${tokenId} --subdomain <name> --name <name>`));
|
|
4817
4912
|
} catch (err) {
|
|
4818
4913
|
spinner.fail("Failed to link Virtuals identity");
|
|
4819
|
-
console.error(
|
|
4914
|
+
console.error(chalk7.red(formatContractError(err)));
|
|
4820
4915
|
process.exit(1);
|
|
4821
4916
|
}
|
|
4822
4917
|
});
|
|
4823
4918
|
identity.command("find").description("Search ERC-8004 agents by wallet address or name (recover a lost agent ID)").option("--wallet <address>", "Filter by owner / wallet address").option("--name <text>", "Filter by agent name (exact or substring match, depending on registry)").option("--all-chains", "Search across all chains, not just the active one").action(async (opts) => {
|
|
4824
4919
|
if (!opts.wallet && !opts.name) {
|
|
4825
|
-
console.error(
|
|
4920
|
+
console.error(chalk7.red("Pass --wallet <address> or --name <text> (or both)."));
|
|
4826
4921
|
process.exit(1);
|
|
4827
4922
|
}
|
|
4828
|
-
const spinner =
|
|
4923
|
+
const spinner = ora6("Searching ERC-8004 registry...").start();
|
|
4829
4924
|
try {
|
|
4830
4925
|
const sdk = getAgent0SDK();
|
|
4831
4926
|
const filters = {};
|
|
@@ -4835,32 +4930,32 @@ function registerIdentityCommands(program2) {
|
|
|
4835
4930
|
const results = await sdk.searchAgents(filters);
|
|
4836
4931
|
spinner.stop();
|
|
4837
4932
|
if (results.length === 0) {
|
|
4838
|
-
console.log(
|
|
4839
|
-
console.log(
|
|
4933
|
+
console.log(chalk7.dim("No agents found matching those filters."));
|
|
4934
|
+
console.log(chalk7.dim("If you minted on a different chain, retry with --all-chains."));
|
|
4840
4935
|
return;
|
|
4841
4936
|
}
|
|
4842
4937
|
console.log();
|
|
4843
|
-
console.log(
|
|
4844
|
-
console.log(
|
|
4938
|
+
console.log(chalk7.bold(`Found ${results.length} agent${results.length === 1 ? "" : "s"}`));
|
|
4939
|
+
console.log(chalk7.dim("\u2500".repeat(50)));
|
|
4845
4940
|
for (const a of results) {
|
|
4846
4941
|
const idStr = String(a.agentId);
|
|
4847
4942
|
const tokenId = idStr.includes(":") ? idStr.split(":")[1] : idStr;
|
|
4848
|
-
console.log(` ${
|
|
4849
|
-
console.log(
|
|
4850
|
-
console.log(
|
|
4943
|
+
console.log(` ${chalk7.bold(`#${tokenId}`)} ${a.name || chalk7.dim("(no name)")}`);
|
|
4944
|
+
console.log(chalk7.dim(` Chain: ${a.chainId}`));
|
|
4945
|
+
console.log(chalk7.dim(` Full: ${idStr}`));
|
|
4851
4946
|
if (a.owners?.length) {
|
|
4852
|
-
console.log(
|
|
4947
|
+
console.log(chalk7.dim(` Owner: ${a.owners[0]}`));
|
|
4853
4948
|
}
|
|
4854
4949
|
if (a.walletAddress) {
|
|
4855
|
-
console.log(
|
|
4950
|
+
console.log(chalk7.dim(` Wallet: ${a.walletAddress}`));
|
|
4856
4951
|
}
|
|
4857
4952
|
}
|
|
4858
4953
|
console.log();
|
|
4859
|
-
console.log(
|
|
4860
|
-
console.log(
|
|
4954
|
+
console.log(chalk7.green("To bind one of these to this machine:"));
|
|
4955
|
+
console.log(chalk7.dim(` sherwood identity load --id <tokenId>`));
|
|
4861
4956
|
} catch (err) {
|
|
4862
4957
|
spinner.fail("Search failed");
|
|
4863
|
-
console.error(
|
|
4958
|
+
console.error(chalk7.red(formatContractError(err)));
|
|
4864
4959
|
process.exit(1);
|
|
4865
4960
|
}
|
|
4866
4961
|
});
|
|
@@ -4871,16 +4966,16 @@ function registerIdentityCommands(program2) {
|
|
|
4871
4966
|
const savedId = getAgentId();
|
|
4872
4967
|
const issuingChainId = getIdentityChainId();
|
|
4873
4968
|
console.log();
|
|
4874
|
-
console.log(
|
|
4875
|
-
console.log(
|
|
4969
|
+
console.log(chalk7.bold("Agent Identity (ERC-8004 via Virtuals economyOS)"));
|
|
4970
|
+
console.log(chalk7.dim("\u2500".repeat(40)));
|
|
4876
4971
|
console.log(` Sherwood wallet: ${account.address}`);
|
|
4877
|
-
console.log(` economyOS wallet: ${virtuals?.wallet ??
|
|
4972
|
+
console.log(` economyOS wallet: ${virtuals?.wallet ?? chalk7.red("(missing)")}`);
|
|
4878
4973
|
if (virtuals?.agentId) console.log(` Virtuals agent: ${virtuals.agentId}`);
|
|
4879
4974
|
const walletOnly = issuingChainId === WALLET_ONLY_CHAIN_ID;
|
|
4880
|
-
console.log(` Issuing chain: ${walletOnly ?
|
|
4881
|
-
console.log(` Saved ID: ${savedId != null ? walletOnly ?
|
|
4975
|
+
console.log(` Issuing chain: ${walletOnly ? chalk7.yellow("wallet-only (no ERC-8004)") : issuingChainId ?? chalk7.red("(missing)")}`);
|
|
4976
|
+
console.log(` Saved ID: ${savedId != null ? walletOnly ? chalk7.dim("0 (wallet-only)") : `#${savedId}` : chalk7.red("(missing)")}`);
|
|
4882
4977
|
if (virtuals?.wallet && savedId != null && issuingChainId != null) {
|
|
4883
|
-
const spinner2 =
|
|
4978
|
+
const spinner2 = ora6("Re-verifying link...").start();
|
|
4884
4979
|
try {
|
|
4885
4980
|
let ownerOk = true;
|
|
4886
4981
|
let owner;
|
|
@@ -4900,21 +4995,21 @@ function registerIdentityCommands(program2) {
|
|
|
4900
4995
|
}
|
|
4901
4996
|
spinner2.stop();
|
|
4902
4997
|
if (walletOnly) {
|
|
4903
|
-
console.log(` NFT ownership: ${
|
|
4998
|
+
console.log(` NFT ownership: ${chalk7.dim("n/a (wallet-only link)")}`);
|
|
4904
4999
|
} else {
|
|
4905
|
-
console.log(` NFT ownership: ${ownerOk ?
|
|
5000
|
+
console.log(` NFT ownership: ${ownerOk ? chalk7.green("verified") : chalk7.red(`FAILED (owner is ${owner})`)}`);
|
|
4906
5001
|
}
|
|
4907
|
-
console.log(` Binding: ${bindingOk ?
|
|
5002
|
+
console.log(` Binding: ${bindingOk ? chalk7.green("verified") : chalk7.red("FAILED")}`);
|
|
4908
5003
|
if (!ownerOk) {
|
|
4909
|
-
console.log(
|
|
5004
|
+
console.log(chalk7.yellow(" The NFT moved since linking \u2014 re-run 'sherwood identity link-virtuals'."));
|
|
4910
5005
|
}
|
|
4911
5006
|
if (walletOnly) {
|
|
4912
|
-
console.log(
|
|
4913
|
-
console.log(
|
|
5007
|
+
console.log(chalk7.dim(" Upgrade to a registered link once ERC-8004 registration is available:"));
|
|
5008
|
+
console.log(chalk7.dim(" acp agent register-erc8004 ... && sherwood identity link-virtuals"));
|
|
4914
5009
|
}
|
|
4915
5010
|
} catch (err) {
|
|
4916
5011
|
spinner2.warn("Could not re-verify against the issuing chain");
|
|
4917
|
-
console.log(
|
|
5012
|
+
console.log(chalk7.dim(` ${err.message}`));
|
|
4918
5013
|
}
|
|
4919
5014
|
}
|
|
4920
5015
|
console.log();
|
|
@@ -4922,7 +5017,7 @@ function registerIdentityCommands(program2) {
|
|
|
4922
5017
|
}
|
|
4923
5018
|
const registry = AGENT_REGISTRY().IDENTITY_REGISTRY;
|
|
4924
5019
|
const client = getPublicClient();
|
|
4925
|
-
const spinner =
|
|
5020
|
+
const spinner = ora6("Checking identity...").start();
|
|
4926
5021
|
try {
|
|
4927
5022
|
const balance = await client.readContract({
|
|
4928
5023
|
address: registry,
|
|
@@ -4933,8 +5028,8 @@ function registerIdentityCommands(program2) {
|
|
|
4933
5028
|
spinner.stop();
|
|
4934
5029
|
const savedId = getAgentId();
|
|
4935
5030
|
console.log();
|
|
4936
|
-
console.log(
|
|
4937
|
-
console.log(
|
|
5031
|
+
console.log(chalk7.bold("Agent Identity (ERC-8004)"));
|
|
5032
|
+
console.log(chalk7.dim("\u2500".repeat(40)));
|
|
4938
5033
|
console.log(` Wallet: ${account.address}`);
|
|
4939
5034
|
console.log(` Registry: ${registry}`);
|
|
4940
5035
|
console.log(` NFTs owned: ${balance.toString()}`);
|
|
@@ -4947,25 +5042,25 @@ function registerIdentityCommands(program2) {
|
|
|
4947
5042
|
args: [BigInt(savedId)]
|
|
4948
5043
|
});
|
|
4949
5044
|
const isOwner = owner.toLowerCase() === account.address.toLowerCase();
|
|
4950
|
-
console.log(` Saved ID: #${savedId} ${isOwner ?
|
|
5045
|
+
console.log(` Saved ID: #${savedId} ${isOwner ? chalk7.green("(verified)") : chalk7.red("(owned by " + owner + ")")}`);
|
|
4951
5046
|
if (isOwner) {
|
|
4952
5047
|
try {
|
|
4953
5048
|
const sdk = getAgent0SDK();
|
|
4954
5049
|
const agent = await sdk.loadAgent(`${getChain().id}:${savedId}`);
|
|
4955
5050
|
if (agent.name) console.log(` Name: ${agent.name}`);
|
|
4956
|
-
if (agent.description) console.log(` Desc: ${
|
|
5051
|
+
if (agent.description) console.log(` Desc: ${chalk7.dim(agent.description)}`);
|
|
4957
5052
|
if (agent.walletAddress) console.log(` Wallet: ${agent.walletAddress}`);
|
|
4958
5053
|
} catch {
|
|
4959
5054
|
}
|
|
4960
5055
|
}
|
|
4961
5056
|
} catch {
|
|
4962
|
-
console.log(` Saved ID: #${savedId} ${
|
|
5057
|
+
console.log(` Saved ID: #${savedId} ${chalk7.red("(token not found)")}`);
|
|
4963
5058
|
}
|
|
4964
5059
|
console.log();
|
|
4965
5060
|
} else if (balance > 0n) {
|
|
4966
|
-
console.log(` Saved ID: ${
|
|
5061
|
+
console.log(` Saved ID: ${chalk7.yellow("none (but this wallet owns NFTs \u2014 listing below)")}`);
|
|
4967
5062
|
console.log();
|
|
4968
|
-
const lookupSpinner =
|
|
5063
|
+
const lookupSpinner = ora6("Resolving owned token IDs via Agent0 SDK...").start();
|
|
4969
5064
|
try {
|
|
4970
5065
|
const sdk = getAgent0SDK();
|
|
4971
5066
|
const results = await sdk.searchAgents({
|
|
@@ -4974,56 +5069,56 @@ function registerIdentityCommands(program2) {
|
|
|
4974
5069
|
});
|
|
4975
5070
|
lookupSpinner.stop();
|
|
4976
5071
|
if (results.length === 0) {
|
|
4977
|
-
console.log(
|
|
4978
|
-
console.log(
|
|
4979
|
-
console.log(
|
|
5072
|
+
console.log(chalk7.dim(" No agents resolved for this wallet on the active chain."));
|
|
5073
|
+
console.log(chalk7.dim(" If you minted on a different chain, try:"));
|
|
5074
|
+
console.log(chalk7.dim(" sherwood identity find --wallet " + account.address + " --all-chains"));
|
|
4980
5075
|
} else {
|
|
4981
|
-
console.log(
|
|
5076
|
+
console.log(chalk7.bold(` Owned identities on this chain (${results.length}):`));
|
|
4982
5077
|
for (const a of results) {
|
|
4983
5078
|
const idStr = String(a.agentId);
|
|
4984
5079
|
const tokenId = idStr.includes(":") ? idStr.split(":")[1] : idStr;
|
|
4985
|
-
console.log(` ${
|
|
5080
|
+
console.log(` ${chalk7.bold(`#${tokenId}`)} ${a.name || chalk7.dim("(no name)")}`);
|
|
4986
5081
|
}
|
|
4987
5082
|
console.log();
|
|
4988
|
-
console.log(
|
|
5083
|
+
console.log(chalk7.green(" Bind one to this machine:"));
|
|
4989
5084
|
if (results.length === 1) {
|
|
4990
5085
|
const idStr = String(results[0].agentId);
|
|
4991
5086
|
const tokenId = idStr.includes(":") ? idStr.split(":")[1] : idStr;
|
|
4992
|
-
console.log(
|
|
5087
|
+
console.log(chalk7.dim(` sherwood identity load --id ${tokenId}`));
|
|
4993
5088
|
} else {
|
|
4994
|
-
console.log(
|
|
5089
|
+
console.log(chalk7.dim(" sherwood identity load --id <tokenId>"));
|
|
4995
5090
|
}
|
|
4996
5091
|
}
|
|
4997
5092
|
} catch (err) {
|
|
4998
5093
|
lookupSpinner.warn("Could not resolve owned token IDs via Agent0 SDK");
|
|
4999
|
-
console.log(
|
|
5000
|
-
console.log(
|
|
5094
|
+
console.log(chalk7.dim(` ${err.message}`));
|
|
5095
|
+
console.log(chalk7.dim(" Try: sherwood identity find --wallet " + account.address));
|
|
5001
5096
|
}
|
|
5002
5097
|
console.log();
|
|
5003
5098
|
} else {
|
|
5004
|
-
console.log(` Saved ID: ${
|
|
5099
|
+
console.log(` Saved ID: ${chalk7.dim("none \u2014 run 'sherwood identity mint --name <name>'")}`);
|
|
5005
5100
|
console.log();
|
|
5006
5101
|
}
|
|
5007
5102
|
} catch (err) {
|
|
5008
5103
|
spinner.fail("Failed to check identity");
|
|
5009
|
-
console.error(
|
|
5104
|
+
console.error(chalk7.red(formatContractError(err)));
|
|
5010
5105
|
process.exit(1);
|
|
5011
5106
|
}
|
|
5012
5107
|
});
|
|
5013
5108
|
}
|
|
5014
5109
|
|
|
5015
5110
|
// src/commands/proposal.ts
|
|
5016
|
-
import { isAddress as
|
|
5017
|
-
import
|
|
5018
|
-
import
|
|
5111
|
+
import { isAddress as isAddress9, encodeFunctionData as encodeFunctionData10, parseUnits as parseUnits8 } from "viem";
|
|
5112
|
+
import chalk9 from "chalk";
|
|
5113
|
+
import ora7 from "ora";
|
|
5019
5114
|
import { readFileSync as readFileSync2 } from "fs";
|
|
5020
5115
|
|
|
5021
5116
|
// src/lib/simulate.ts
|
|
5022
5117
|
import { decodeFunctionData as decodeFunctionData2, encodeFunctionData as encodeFunctionData9 } from "viem";
|
|
5023
|
-
import
|
|
5118
|
+
import chalk8 from "chalk";
|
|
5024
5119
|
|
|
5025
5120
|
// src/lib/risk.ts
|
|
5026
|
-
import { decodeFunctionData, erc20Abi as
|
|
5121
|
+
import { decodeFunctionData, erc20Abi as erc20Abi4 } from "viem";
|
|
5027
5122
|
var MOONWELL_CTOKEN_ABI = [
|
|
5028
5123
|
{ name: "mint", type: "function", stateMutability: "nonpayable", inputs: [{ name: "mintAmount", type: "uint256" }], outputs: [{ name: "", type: "uint256" }] },
|
|
5029
5124
|
{ name: "redeem", type: "function", stateMutability: "nonpayable", inputs: [{ name: "redeemTokens", type: "uint256" }], outputs: [{ name: "", type: "uint256" }] },
|
|
@@ -5099,7 +5194,7 @@ function tryDecode(abi, data) {
|
|
|
5099
5194
|
}
|
|
5100
5195
|
}
|
|
5101
5196
|
function decodeCallArgs(data) {
|
|
5102
|
-
return tryDecode(
|
|
5197
|
+
return tryDecode(erc20Abi4, data) ?? tryDecode(MOONWELL_CTOKEN_ABI, data) ?? tryDecode(MOONWELL_COMPTROLLER_ABI, data) ?? tryDecode(SWAP_ROUTER_SINGLE_ABI, data) ?? // exactInputSingle (single-hop)
|
|
5103
5198
|
tryDecode(SWAP_ROUTER_MULTI_ABI, data);
|
|
5104
5199
|
}
|
|
5105
5200
|
function analyzeRisk(calls, callResults, context) {
|
|
@@ -5229,11 +5324,11 @@ function analyzeRisk(calls, callResults, context) {
|
|
|
5229
5324
|
}
|
|
5230
5325
|
|
|
5231
5326
|
// src/lib/simulate.ts
|
|
5232
|
-
var G2 =
|
|
5233
|
-
var W2 =
|
|
5234
|
-
var DIM2 =
|
|
5235
|
-
var BOLD2 =
|
|
5236
|
-
var LABEL2 =
|
|
5327
|
+
var G2 = chalk8.green;
|
|
5328
|
+
var W2 = chalk8.white;
|
|
5329
|
+
var DIM2 = chalk8.gray;
|
|
5330
|
+
var BOLD2 = chalk8.white.bold;
|
|
5331
|
+
var LABEL2 = chalk8.green.bold;
|
|
5237
5332
|
var SEP2 = () => console.log(DIM2("\u2500".repeat(60)));
|
|
5238
5333
|
var MOONWELL_CTOKEN_ABI2 = [
|
|
5239
5334
|
{
|
|
@@ -5651,17 +5746,17 @@ function printSimulationResult(result, callType) {
|
|
|
5651
5746
|
SEP2();
|
|
5652
5747
|
const sourceLabel = result.source === "api" ? "Tenderly (via API)" : `eth_call ${DIM2("(configure Tenderly for detailed results)")}`;
|
|
5653
5748
|
console.log(W2(` Source: ${sourceLabel}`));
|
|
5654
|
-
const statusIcon = result.success ? G2("\u2713 PASS") :
|
|
5749
|
+
const statusIcon = result.success ? G2("\u2713 PASS") : chalk8.red("\u2716 FAIL");
|
|
5655
5750
|
console.log(W2(` Status: ${statusIcon}`));
|
|
5656
5751
|
if (result.totalGasUsed > 0) {
|
|
5657
5752
|
console.log(W2(` Total Gas: ${result.totalGasUsed.toLocaleString()}`));
|
|
5658
5753
|
}
|
|
5659
5754
|
if (result.errorMessage && !result.success) {
|
|
5660
|
-
console.log(
|
|
5755
|
+
console.log(chalk8.red(` Error: ${result.errorMessage.slice(0, 120)}`));
|
|
5661
5756
|
}
|
|
5662
5757
|
console.log();
|
|
5663
5758
|
for (const cr of result.callResults) {
|
|
5664
|
-
const icon = cr.success ? G2("\u2713") :
|
|
5759
|
+
const icon = cr.success ? G2("\u2713") : chalk8.red("\u2716");
|
|
5665
5760
|
const funcName = cr.decodedFunction || `${cr.selector}... ${DIM2("(unknown)")}`;
|
|
5666
5761
|
console.log(W2(` [${cr.index + 1}] ${icon} ${funcName}`));
|
|
5667
5762
|
const label = getAddressLabel(cr.target);
|
|
@@ -5677,18 +5772,18 @@ function printSimulationResult(result, callType) {
|
|
|
5677
5772
|
const warnings = result.risks.filter((r) => r.level === "warning");
|
|
5678
5773
|
const infos = result.risks.filter((r) => r.level === "info");
|
|
5679
5774
|
if (criticals.length > 0) {
|
|
5680
|
-
console.log(
|
|
5775
|
+
console.log(chalk8.red.bold(` \u2716 CRITICAL RISKS (${criticals.length})`));
|
|
5681
5776
|
for (const r of criticals) {
|
|
5682
5777
|
const ref = r.callIndex !== void 0 ? ` [call #${r.callIndex + 1}]` : "";
|
|
5683
|
-
console.log(
|
|
5778
|
+
console.log(chalk8.red(` \u2716${ref} ${r.code} \u2014 ${r.message}`));
|
|
5684
5779
|
}
|
|
5685
5780
|
console.log();
|
|
5686
5781
|
}
|
|
5687
5782
|
if (warnings.length > 0) {
|
|
5688
|
-
console.log(
|
|
5783
|
+
console.log(chalk8.yellow.bold(` \u26A0 WARNINGS (${warnings.length})`));
|
|
5689
5784
|
for (const r of warnings) {
|
|
5690
5785
|
const ref = r.callIndex !== void 0 ? ` [call #${r.callIndex + 1}]` : "";
|
|
5691
|
-
console.log(
|
|
5786
|
+
console.log(chalk8.yellow(` \u26A0${ref} ${r.code} \u2014 ${r.message}`));
|
|
5692
5787
|
}
|
|
5693
5788
|
console.log();
|
|
5694
5789
|
}
|
|
@@ -5701,9 +5796,9 @@ function printSimulationResult(result, callType) {
|
|
|
5701
5796
|
}
|
|
5702
5797
|
}
|
|
5703
5798
|
if (result.warnings.length > 0) {
|
|
5704
|
-
console.log(
|
|
5799
|
+
console.log(chalk8.yellow(` \u26A0 Simulation Warnings`));
|
|
5705
5800
|
for (const w of result.warnings) {
|
|
5706
|
-
console.log(
|
|
5801
|
+
console.log(chalk8.yellow(` - ${w}`));
|
|
5707
5802
|
}
|
|
5708
5803
|
console.log();
|
|
5709
5804
|
}
|
|
@@ -5711,8 +5806,8 @@ function printSimulationResult(result, callType) {
|
|
|
5711
5806
|
}
|
|
5712
5807
|
async function sendSimulationAlert(subdomain, proposalId, vault, execResult, settleResult) {
|
|
5713
5808
|
try {
|
|
5714
|
-
const xmtp = await import("./xmtp-
|
|
5715
|
-
const { getAccount: getAccount2 } = await import("./client-
|
|
5809
|
+
const xmtp = await import("./xmtp-2U7CSBXM.js");
|
|
5810
|
+
const { getAccount: getAccount2 } = await import("./client-OPZ2ORWW.js");
|
|
5716
5811
|
const group = await xmtp.getGroup("", subdomain);
|
|
5717
5812
|
const allRisks = [...execResult.risks, ...settleResult?.risks ?? []];
|
|
5718
5813
|
const criticals = allRisks.filter((r) => r.level === "critical");
|
|
@@ -5788,16 +5883,16 @@ async function sendSimulationAlert(subdomain, proposalId, vault, execResult, set
|
|
|
5788
5883
|
});
|
|
5789
5884
|
} catch (err) {
|
|
5790
5885
|
const message = err instanceof Error ? err.message : String(err);
|
|
5791
|
-
console.log(
|
|
5886
|
+
console.log(chalk8.yellow(` \u26A0 Failed to send XMTP alert: ${message.slice(0, 100)}`));
|
|
5792
5887
|
}
|
|
5793
5888
|
}
|
|
5794
5889
|
|
|
5795
5890
|
// src/commands/proposal.ts
|
|
5796
|
-
var G3 =
|
|
5797
|
-
var W3 =
|
|
5798
|
-
var DIM3 =
|
|
5799
|
-
var BOLD3 =
|
|
5800
|
-
var LABEL3 =
|
|
5891
|
+
var G3 = chalk9.green;
|
|
5892
|
+
var W3 = chalk9.white;
|
|
5893
|
+
var DIM3 = chalk9.gray;
|
|
5894
|
+
var BOLD3 = chalk9.white.bold;
|
|
5895
|
+
var LABEL3 = chalk9.green.bold;
|
|
5801
5896
|
var SEP3 = () => console.log(DIM3("\u2500".repeat(60)));
|
|
5802
5897
|
function formatTimestamp(ts) {
|
|
5803
5898
|
if (ts === 0n) return "\u2014";
|
|
@@ -5805,9 +5900,9 @@ function formatTimestamp(ts) {
|
|
|
5805
5900
|
}
|
|
5806
5901
|
function parseVaultOption(raw, spinner) {
|
|
5807
5902
|
const vault = String(raw);
|
|
5808
|
-
if (!
|
|
5903
|
+
if (!isAddress9(vault)) {
|
|
5809
5904
|
if (spinner) spinner.fail(`Invalid vault address: ${vault}`);
|
|
5810
|
-
else console.error(
|
|
5905
|
+
else console.error(chalk9.red(`Invalid vault address: ${vault}`));
|
|
5811
5906
|
process.exit(1);
|
|
5812
5907
|
}
|
|
5813
5908
|
return vault;
|
|
@@ -5836,8 +5931,7 @@ var GUARDIAN_REGISTRY_ABI2 = [
|
|
|
5836
5931
|
outputs: [
|
|
5837
5932
|
{ name: "opened", type: "bool" },
|
|
5838
5933
|
{ name: "resolved", type: "bool" },
|
|
5839
|
-
{ name: "blocked", type: "bool" }
|
|
5840
|
-
{ name: "cohortTooSmall", type: "bool" }
|
|
5934
|
+
{ name: "blocked", type: "bool" }
|
|
5841
5935
|
],
|
|
5842
5936
|
stateMutability: "view"
|
|
5843
5937
|
}
|
|
@@ -5846,7 +5940,7 @@ function getRegistryAddress() {
|
|
|
5846
5940
|
const addr = SHERWOOD().GUARDIAN_REGISTRY;
|
|
5847
5941
|
if (addr === "0x0000000000000000000000000000000000000000") {
|
|
5848
5942
|
console.error(
|
|
5849
|
-
|
|
5943
|
+
chalk9.red(
|
|
5850
5944
|
`GUARDIAN_REGISTRY is not deployed on ${getNetwork()}. Update cli/src/lib/addresses.ts after deploy.`
|
|
5851
5945
|
)
|
|
5852
5946
|
);
|
|
@@ -5856,7 +5950,7 @@ function getRegistryAddress() {
|
|
|
5856
5950
|
}
|
|
5857
5951
|
async function resolveRiskEnvelope(opts, vault) {
|
|
5858
5952
|
if (opts.maxCapital === void 0) {
|
|
5859
|
-
console.error(
|
|
5953
|
+
console.error(chalk9.red(" --max-capital is required (the ceiling this proposal may deploy, in asset units)."));
|
|
5860
5954
|
process.exit(1);
|
|
5861
5955
|
}
|
|
5862
5956
|
const asset = await getAssetAddress();
|
|
@@ -5865,14 +5959,14 @@ async function resolveRiskEnvelope(opts, vault) {
|
|
|
5865
5959
|
abi: ERC20_ABI,
|
|
5866
5960
|
functionName: "decimals"
|
|
5867
5961
|
});
|
|
5868
|
-
const maxCapital =
|
|
5962
|
+
const maxCapital = parseUnits8(String(opts.maxCapital), decimals);
|
|
5869
5963
|
if (maxCapital === 0n) {
|
|
5870
|
-
console.error(
|
|
5964
|
+
console.error(chalk9.red(" --max-capital must be greater than zero."));
|
|
5871
5965
|
process.exit(1);
|
|
5872
5966
|
}
|
|
5873
5967
|
const maxDrawdownBps = opts.maxDrawdownBps === void 0 ? 2e3 : Number(opts.maxDrawdownBps);
|
|
5874
5968
|
if (!Number.isInteger(maxDrawdownBps) || maxDrawdownBps < 0 || maxDrawdownBps > 1e4) {
|
|
5875
|
-
console.error(
|
|
5969
|
+
console.error(chalk9.red(` --max-drawdown-bps must be an integer 0-10000 (got ${opts.maxDrawdownBps})`));
|
|
5876
5970
|
process.exit(1);
|
|
5877
5971
|
}
|
|
5878
5972
|
return { maxCapital, maxDrawdownBps };
|
|
@@ -5885,7 +5979,7 @@ function resolveCaps(list, callCount, fallbackTotal, flag) {
|
|
|
5885
5979
|
const parts = list.split(",").map((x) => x.trim()).filter((x) => x.length > 0);
|
|
5886
5980
|
if (parts.length !== callCount) {
|
|
5887
5981
|
console.error(
|
|
5888
|
-
|
|
5982
|
+
chalk9.red(` ${flag} has ${parts.length} entries for ${callCount} calls \u2014 every call must declare exactly one cap.`)
|
|
5889
5983
|
);
|
|
5890
5984
|
process.exit(1);
|
|
5891
5985
|
}
|
|
@@ -5908,8 +6002,8 @@ function registerProposalCommands(program2) {
|
|
|
5908
6002
|
).option("--metadata-uri <uri>", "Override \u2014 skip IPFS upload and use this URI directly").option("--simulate", "Simulate calls via Tenderly before submitting").option("--force", "Submit even if simulation fails (requires --simulate)").action(async (opts) => {
|
|
5909
6003
|
try {
|
|
5910
6004
|
const vault = opts.vault;
|
|
5911
|
-
if (!
|
|
5912
|
-
console.error(
|
|
6005
|
+
if (!isAddress9(vault)) {
|
|
6006
|
+
console.error(chalk9.red(`Invalid vault address: ${opts.vault}`));
|
|
5913
6007
|
process.exit(1);
|
|
5914
6008
|
}
|
|
5915
6009
|
const strategyDuration = parseDuration(opts.duration);
|
|
@@ -5918,8 +6012,8 @@ function registerProposalCommands(program2) {
|
|
|
5918
6012
|
const ZERO_STRATEGY = "0x0000000000000000000000000000000000000000";
|
|
5919
6013
|
let strategy2 = ZERO_STRATEGY;
|
|
5920
6014
|
if (opts.strategy) {
|
|
5921
|
-
if (!
|
|
5922
|
-
console.error(
|
|
6015
|
+
if (!isAddress9(opts.strategy)) {
|
|
6016
|
+
console.error(chalk9.red(`Invalid strategy address: ${opts.strategy}`));
|
|
5923
6017
|
process.exit(1);
|
|
5924
6018
|
}
|
|
5925
6019
|
strategy2 = opts.strategy;
|
|
@@ -5927,7 +6021,7 @@ function registerProposalCommands(program2) {
|
|
|
5927
6021
|
if (isCalldataOnly()) {
|
|
5928
6022
|
if (!opts.metadataUri) {
|
|
5929
6023
|
console.error(
|
|
5930
|
-
|
|
6024
|
+
chalk9.red(
|
|
5931
6025
|
"--calldata-only requires --metadata-uri (IPFS metadata upload needs a signing wallet)"
|
|
5932
6026
|
)
|
|
5933
6027
|
);
|
|
@@ -5963,14 +6057,14 @@ function registerProposalCommands(program2) {
|
|
|
5963
6057
|
)
|
|
5964
6058
|
);
|
|
5965
6059
|
} catch (err) {
|
|
5966
|
-
console.error(
|
|
6060
|
+
console.error(chalk9.red(err instanceof Error ? err.message : String(err)));
|
|
5967
6061
|
process.exit(1);
|
|
5968
6062
|
}
|
|
5969
6063
|
return;
|
|
5970
6064
|
}
|
|
5971
6065
|
let metadataURI = opts.metadataUri || "";
|
|
5972
6066
|
if (!metadataURI) {
|
|
5973
|
-
const spinner2 =
|
|
6067
|
+
const spinner2 = ora7({ text: W3("Uploading metadata to IPFS..."), color: "green" }).start();
|
|
5974
6068
|
try {
|
|
5975
6069
|
const account = getAccount();
|
|
5976
6070
|
const metadata = {
|
|
@@ -5992,7 +6086,7 @@ function registerProposalCommands(program2) {
|
|
|
5992
6086
|
metadataURI = await uploadMetadata(proposalMeta);
|
|
5993
6087
|
spinner2.succeed(G3(`Metadata pinned: ${DIM3(metadataURI)}`));
|
|
5994
6088
|
} catch (err) {
|
|
5995
|
-
spinner2.warn(
|
|
6089
|
+
spinner2.warn(chalk9.yellow("IPFS upload failed \u2014 using inline metadata"));
|
|
5996
6090
|
const json = JSON.stringify({ name: opts.name, description: opts.description, vault });
|
|
5997
6091
|
metadataURI = `data:application/json;base64,${Buffer.from(json).toString("base64")}`;
|
|
5998
6092
|
}
|
|
@@ -6020,31 +6114,31 @@ function registerProposalCommands(program2) {
|
|
|
6020
6114
|
if (preFetchedGovParams) {
|
|
6021
6115
|
createRiskContext = { ...createRiskContext, governorParams: preFetchedGovParams };
|
|
6022
6116
|
}
|
|
6023
|
-
const simSpinner =
|
|
6117
|
+
const simSpinner = ora7({ text: W3("Simulating execute calls..."), color: "green" }).start();
|
|
6024
6118
|
const execResult = await simulateBatchCalls(vault, executeCalls, "execute", createRiskContext);
|
|
6025
6119
|
simSpinner.stop();
|
|
6026
6120
|
printSimulationResult(execResult, "execute");
|
|
6027
6121
|
if (settleCalls.length > 0) {
|
|
6028
|
-
const settleSpinner =
|
|
6122
|
+
const settleSpinner = ora7({ text: W3("Simulating settlement calls..."), color: "green" }).start();
|
|
6029
6123
|
const settleResult = await simulateBatchCalls(vault, settleCalls, "settlement", createRiskContext);
|
|
6030
6124
|
settleSpinner.stop();
|
|
6031
6125
|
printSimulationResult(settleResult, "settlement");
|
|
6032
6126
|
if (!settleResult.success && !opts.force) {
|
|
6033
|
-
console.error(
|
|
6127
|
+
console.error(chalk9.red("\n \u2716 Settlement simulation failed. Use --force to submit anyway."));
|
|
6034
6128
|
process.exit(1);
|
|
6035
6129
|
}
|
|
6036
6130
|
}
|
|
6037
6131
|
if (!execResult.success && !opts.force) {
|
|
6038
|
-
console.error(
|
|
6132
|
+
console.error(chalk9.red("\n \u2716 Execution simulation failed. Use --force to submit anyway."));
|
|
6039
6133
|
process.exit(1);
|
|
6040
6134
|
}
|
|
6041
6135
|
const hasCriticalRisks = execResult.risks.some((r) => r.level === "critical");
|
|
6042
6136
|
if (hasCriticalRisks && !opts.force) {
|
|
6043
|
-
console.error(
|
|
6137
|
+
console.error(chalk9.red("\n \u2716 Critical risks detected. Use --force to submit anyway."));
|
|
6044
6138
|
process.exit(1);
|
|
6045
6139
|
}
|
|
6046
6140
|
}
|
|
6047
|
-
const spinner =
|
|
6141
|
+
const spinner = ora7({ text: W3("Submitting proposal..."), color: "green" }).start();
|
|
6048
6142
|
const envelope = await resolveRiskEnvelope(opts, vault);
|
|
6049
6143
|
const executeCallCaps = resolveCaps(
|
|
6050
6144
|
opts.executeCaps,
|
|
@@ -6078,13 +6172,13 @@ function registerProposalCommands(program2) {
|
|
|
6078
6172
|
SEP3();
|
|
6079
6173
|
console.log();
|
|
6080
6174
|
} catch (err) {
|
|
6081
|
-
console.error(
|
|
6175
|
+
console.error(chalk9.red(`
|
|
6082
6176
|
\u2716 ${formatContractError(err)}`));
|
|
6083
6177
|
process.exit(1);
|
|
6084
6178
|
}
|
|
6085
6179
|
});
|
|
6086
6180
|
proposal.command("list").description("List a vault's proposals (per-vault governor \u2014 IDs are scoped to the vault)").requiredOption("--vault <address>", "Vault whose proposals to list").option("--state <filter>", "Filter by state: draft, pending, approved, executed, settled, all", "all").action(async (opts) => {
|
|
6087
|
-
const spinner =
|
|
6181
|
+
const spinner = ora7("Loading proposals...").start();
|
|
6088
6182
|
try {
|
|
6089
6183
|
const vault = parseVaultOption(opts.vault, spinner);
|
|
6090
6184
|
const count = await proposalCount(vault);
|
|
@@ -6132,12 +6226,12 @@ function registerProposalCommands(program2) {
|
|
|
6132
6226
|
console.log();
|
|
6133
6227
|
} catch (err) {
|
|
6134
6228
|
spinner.fail("Failed to load proposals");
|
|
6135
|
-
console.error(
|
|
6229
|
+
console.error(chalk9.red(formatContractError(err)));
|
|
6136
6230
|
process.exit(1);
|
|
6137
6231
|
}
|
|
6138
6232
|
});
|
|
6139
6233
|
proposal.command("show").description("Show full proposal details").argument("<id>", "Proposal ID").requiredOption("--vault <address>", "Vault the proposal belongs to (per-vault governor)").action(async (idStr, opts) => {
|
|
6140
|
-
const spinner =
|
|
6234
|
+
const spinner = ora7("Loading proposal...").start();
|
|
6141
6235
|
try {
|
|
6142
6236
|
const vault = parseVaultOption(opts.vault, spinner);
|
|
6143
6237
|
const id = parseBigIntArg(idStr, "proposal ID");
|
|
@@ -6211,7 +6305,7 @@ function registerProposalCommands(program2) {
|
|
|
6211
6305
|
console.log();
|
|
6212
6306
|
} catch (err) {
|
|
6213
6307
|
spinner.fail("Failed to load proposal");
|
|
6214
|
-
console.error(
|
|
6308
|
+
console.error(chalk9.red(formatContractError(err)));
|
|
6215
6309
|
process.exit(1);
|
|
6216
6310
|
}
|
|
6217
6311
|
});
|
|
@@ -6222,7 +6316,7 @@ function registerProposalCommands(program2) {
|
|
|
6222
6316
|
const supportRaw = String(opts.support).toLowerCase();
|
|
6223
6317
|
const support = supportRaw === "yes" || supportRaw === "for" ? VOTE_TYPE.For : supportRaw === "no" || supportRaw === "against" ? VOTE_TYPE.Against : supportRaw === "abstain" ? VOTE_TYPE.Abstain : null;
|
|
6224
6318
|
if (support === null) {
|
|
6225
|
-
console.error(
|
|
6319
|
+
console.error(chalk9.red(`Invalid support value "${opts.support}". Use for|against|abstain.`));
|
|
6226
6320
|
process.exit(1);
|
|
6227
6321
|
}
|
|
6228
6322
|
if (isCalldataOnly()) {
|
|
@@ -6236,7 +6330,7 @@ function registerProposalCommands(program2) {
|
|
|
6236
6330
|
return;
|
|
6237
6331
|
}
|
|
6238
6332
|
const account = getAccount();
|
|
6239
|
-
const spinner =
|
|
6333
|
+
const spinner = ora7("Loading proposal...").start();
|
|
6240
6334
|
const p = await getProposal(vault, proposalId);
|
|
6241
6335
|
const state = await getProposalState(vault, proposalId);
|
|
6242
6336
|
if (state !== PROPOSAL_STATE.Pending) {
|
|
@@ -6256,17 +6350,17 @@ function registerProposalCommands(program2) {
|
|
|
6256
6350
|
console.log(W3(` Proposal: #${proposalId}`));
|
|
6257
6351
|
console.log(W3(` Vault: ${G3(p.vault)}`));
|
|
6258
6352
|
console.log(W3(
|
|
6259
|
-
` Support: ${support === VOTE_TYPE.For ? G3("FOR") : support === VOTE_TYPE.Against ?
|
|
6353
|
+
` Support: ${support === VOTE_TYPE.For ? G3("FOR") : support === VOTE_TYPE.Against ? chalk9.red("AGAINST") : DIM3("ABSTAIN")}`
|
|
6260
6354
|
));
|
|
6261
6355
|
console.log(W3(` Weight: ${formatShares(weight)} shares`));
|
|
6262
6356
|
SEP3();
|
|
6263
|
-
const voteSpinner =
|
|
6357
|
+
const voteSpinner = ora7({ text: W3("Submitting vote..."), color: "green" }).start();
|
|
6264
6358
|
const hash = await vote(vault, proposalId, support);
|
|
6265
6359
|
voteSpinner.succeed(G3("Vote cast"));
|
|
6266
6360
|
console.log(DIM3(` ${getExplorerUrl(hash)}`));
|
|
6267
6361
|
console.log();
|
|
6268
6362
|
} catch (err) {
|
|
6269
|
-
console.error(
|
|
6363
|
+
console.error(chalk9.red(`
|
|
6270
6364
|
\u2716 ${formatContractError(err)}`));
|
|
6271
6365
|
process.exit(1);
|
|
6272
6366
|
}
|
|
@@ -6284,7 +6378,7 @@ function registerProposalCommands(program2) {
|
|
|
6284
6378
|
);
|
|
6285
6379
|
return;
|
|
6286
6380
|
}
|
|
6287
|
-
const spinner =
|
|
6381
|
+
const spinner = ora7("Loading proposal...").start();
|
|
6288
6382
|
const [state, p, latestBlock] = await Promise.all([
|
|
6289
6383
|
getProposalState(vault, proposalId),
|
|
6290
6384
|
getProposal(vault, proposalId),
|
|
@@ -6347,7 +6441,7 @@ function registerProposalCommands(program2) {
|
|
|
6347
6441
|
}
|
|
6348
6442
|
console.log();
|
|
6349
6443
|
} catch (err) {
|
|
6350
|
-
console.error(
|
|
6444
|
+
console.error(chalk9.red(`
|
|
6351
6445
|
\u2716 ${formatContractError(err)}`));
|
|
6352
6446
|
process.exit(1);
|
|
6353
6447
|
}
|
|
@@ -6369,7 +6463,7 @@ function registerProposalCommands(program2) {
|
|
|
6369
6463
|
return;
|
|
6370
6464
|
}
|
|
6371
6465
|
const account = getAccount();
|
|
6372
|
-
const spinner =
|
|
6466
|
+
const spinner = ora7("Loading proposal...").start();
|
|
6373
6467
|
const [p, state, latestBlock] = await Promise.all([
|
|
6374
6468
|
getProposal(vault, proposalId),
|
|
6375
6469
|
getProposalState(vault, proposalId),
|
|
@@ -6403,7 +6497,7 @@ function registerProposalCommands(program2) {
|
|
|
6403
6497
|
console.log(DIM3(` ${getExplorerUrl(hash)}`));
|
|
6404
6498
|
console.log();
|
|
6405
6499
|
} catch (err) {
|
|
6406
|
-
console.error(
|
|
6500
|
+
console.error(chalk9.red(`
|
|
6407
6501
|
\u2716 ${formatContractError(err)}`));
|
|
6408
6502
|
process.exit(1);
|
|
6409
6503
|
}
|
|
@@ -6427,7 +6521,7 @@ function registerProposalCommands(program2) {
|
|
|
6427
6521
|
return;
|
|
6428
6522
|
}
|
|
6429
6523
|
const account = getAccount();
|
|
6430
|
-
const spinner =
|
|
6524
|
+
const spinner = ora7("Loading proposal...").start();
|
|
6431
6525
|
const p = await getProposal(vault, proposalId);
|
|
6432
6526
|
const state = await getProposalState(vault, proposalId);
|
|
6433
6527
|
if (state !== PROPOSAL_STATE.Executed) {
|
|
@@ -6508,7 +6602,7 @@ function registerProposalCommands(program2) {
|
|
|
6508
6602
|
return;
|
|
6509
6603
|
}
|
|
6510
6604
|
if (!opts.yes) {
|
|
6511
|
-
process.stdout.write(
|
|
6605
|
+
process.stdout.write(chalk9.yellow(" Proceed? [y/N] "));
|
|
6512
6606
|
const answer = await new Promise((resolve3) => {
|
|
6513
6607
|
process.stdin.resume();
|
|
6514
6608
|
process.stdin.setEncoding("utf-8");
|
|
@@ -6520,7 +6614,7 @@ function registerProposalCommands(program2) {
|
|
|
6520
6614
|
return;
|
|
6521
6615
|
}
|
|
6522
6616
|
}
|
|
6523
|
-
const send =
|
|
6617
|
+
const send = ora7("Broadcasting emergencySettle...").start();
|
|
6524
6618
|
const hash = await emergencySettle(vault, proposalId, fallbackCalls);
|
|
6525
6619
|
send.succeed(G3("Vault unstuck \u2014 proposal Settled"));
|
|
6526
6620
|
console.log(DIM3(` ${getExplorerUrl(hash)}`));
|
|
@@ -6529,20 +6623,20 @@ function registerProposalCommands(program2) {
|
|
|
6529
6623
|
console.log(G3(" redemptionsLocked cleared \u2713"));
|
|
6530
6624
|
} else {
|
|
6531
6625
|
console.log(
|
|
6532
|
-
|
|
6626
|
+
chalk9.yellow(
|
|
6533
6627
|
` \u26A0 getActiveProposal(vault) still returns ${stillActive} \u2014 check manually.`
|
|
6534
6628
|
)
|
|
6535
6629
|
);
|
|
6536
6630
|
}
|
|
6537
6631
|
console.log();
|
|
6538
6632
|
} catch (err) {
|
|
6539
|
-
console.error(
|
|
6633
|
+
console.error(chalk9.red(`
|
|
6540
6634
|
\u2716 ${formatContractError(err)}`));
|
|
6541
6635
|
process.exit(1);
|
|
6542
6636
|
}
|
|
6543
6637
|
});
|
|
6544
6638
|
proposal.command("resolve-reviews").description("Keeper: resolve guardian reviews whose review window has elapsed").requiredOption("--vault <address>", "Vault whose governor's reviews to scan (per-vault governor)").option("--dry-run", "List resolvable reviews without sending transactions").action(async (opts) => {
|
|
6545
|
-
const spinner =
|
|
6639
|
+
const spinner = ora7("Scanning proposals for resolvable reviews...").start();
|
|
6546
6640
|
try {
|
|
6547
6641
|
const vault = parseVaultOption(opts.vault, spinner);
|
|
6548
6642
|
const registryAddr = getRegistryAddress();
|
|
@@ -6620,7 +6714,7 @@ function registerProposalCommands(program2) {
|
|
|
6620
6714
|
}
|
|
6621
6715
|
const resolved = [];
|
|
6622
6716
|
for (const c of resolvable) {
|
|
6623
|
-
const txSpinner =
|
|
6717
|
+
const txSpinner = ora7(`Resolving review for proposal #${c.id}...`).start();
|
|
6624
6718
|
try {
|
|
6625
6719
|
const { result: blocked } = await pc.simulateContract({
|
|
6626
6720
|
address: registryAddr,
|
|
@@ -6640,7 +6734,7 @@ function registerProposalCommands(program2) {
|
|
|
6640
6734
|
await waitForReceipt(hash);
|
|
6641
6735
|
resolved.push({ id: c.id, blocked, hash });
|
|
6642
6736
|
txSpinner.succeed(
|
|
6643
|
-
G3(`Proposal #${c.id} resolved \u2014 blocked=${blocked ?
|
|
6737
|
+
G3(`Proposal #${c.id} resolved \u2014 blocked=${blocked ? chalk9.red("true") : "false"}`)
|
|
6644
6738
|
);
|
|
6645
6739
|
} catch (err) {
|
|
6646
6740
|
txSpinner.fail(`Proposal #${c.id} resolve failed: ${formatContractError(err)}`);
|
|
@@ -6651,7 +6745,7 @@ function registerProposalCommands(program2) {
|
|
|
6651
6745
|
SEP3();
|
|
6652
6746
|
for (const r of resolved) {
|
|
6653
6747
|
console.log(
|
|
6654
|
-
W3(` Proposal #${r.id} blocked=${r.blocked ?
|
|
6748
|
+
W3(` Proposal #${r.id} blocked=${r.blocked ? chalk9.red("true") : "false"} `) + DIM3(getExplorerUrl(r.hash))
|
|
6655
6749
|
);
|
|
6656
6750
|
}
|
|
6657
6751
|
SEP3();
|
|
@@ -6659,7 +6753,7 @@ function registerProposalCommands(program2) {
|
|
|
6659
6753
|
if (resolved.length < resolvable.length) process.exit(1);
|
|
6660
6754
|
} catch (err) {
|
|
6661
6755
|
spinner.fail("Failed to scan reviews");
|
|
6662
|
-
console.error(
|
|
6756
|
+
console.error(chalk9.red(formatContractError(err)));
|
|
6663
6757
|
process.exit(1);
|
|
6664
6758
|
}
|
|
6665
6759
|
});
|
|
@@ -6676,7 +6770,7 @@ function registerProposalCommands(program2) {
|
|
|
6676
6770
|
);
|
|
6677
6771
|
return;
|
|
6678
6772
|
}
|
|
6679
|
-
const spinner =
|
|
6773
|
+
const spinner = ora7("Loading proposal...").start();
|
|
6680
6774
|
const state = await getProposalState(vault, proposalId);
|
|
6681
6775
|
if (state === PROPOSAL_STATE.Settled || state === PROPOSAL_STATE.Cancelled) {
|
|
6682
6776
|
spinner.fail(`Proposal is already ${PROPOSAL_STATES[state]}`);
|
|
@@ -6717,7 +6811,7 @@ function registerProposalCommands(program2) {
|
|
|
6717
6811
|
console.log(DIM3(` ${getExplorerUrl(hash)}`));
|
|
6718
6812
|
console.log();
|
|
6719
6813
|
} catch (err) {
|
|
6720
|
-
console.error(
|
|
6814
|
+
console.error(chalk9.red(`
|
|
6721
6815
|
\u2716 ${formatContractError(err)}`));
|
|
6722
6816
|
process.exit(1);
|
|
6723
6817
|
}
|
|
@@ -6730,13 +6824,13 @@ function registerProposalCommands(program2) {
|
|
|
6730
6824
|
let riskContext = {};
|
|
6731
6825
|
let proposalId;
|
|
6732
6826
|
if (opts.id) {
|
|
6733
|
-
if (!opts.vault || !
|
|
6734
|
-
console.error(
|
|
6827
|
+
if (!opts.vault || !isAddress9(opts.vault)) {
|
|
6828
|
+
console.error(chalk9.red("\n \u2716 --vault is required when using --id (proposal IDs are per-vault)"));
|
|
6735
6829
|
process.exit(1);
|
|
6736
6830
|
}
|
|
6737
6831
|
vault = opts.vault;
|
|
6738
6832
|
proposalId = parseBigIntArg(opts.id, "proposal ID");
|
|
6739
|
-
const spinner2 =
|
|
6833
|
+
const spinner2 = ora7("Loading proposal...").start();
|
|
6740
6834
|
const p = await getProposal(vault, proposalId);
|
|
6741
6835
|
executeCalls = await getExecuteCalls(vault, proposalId);
|
|
6742
6836
|
settlementCalls = await getSettlementCalls(vault, proposalId);
|
|
@@ -6754,8 +6848,8 @@ function registerProposalCommands(program2) {
|
|
|
6754
6848
|
console.log(DIM3(`
|
|
6755
6849
|
Simulating proposal #${proposalId} on vault ${vault.slice(0, 10)}...`));
|
|
6756
6850
|
} else if (opts.executeCalls) {
|
|
6757
|
-
if (!opts.vault || !
|
|
6758
|
-
console.error(
|
|
6851
|
+
if (!opts.vault || !isAddress9(opts.vault)) {
|
|
6852
|
+
console.error(chalk9.red("\n \u2716 --vault is required when using --execute-calls"));
|
|
6759
6853
|
process.exit(1);
|
|
6760
6854
|
}
|
|
6761
6855
|
vault = opts.vault;
|
|
@@ -6765,22 +6859,22 @@ function registerProposalCommands(program2) {
|
|
|
6765
6859
|
}
|
|
6766
6860
|
riskContext = { vault };
|
|
6767
6861
|
} else {
|
|
6768
|
-
console.error(
|
|
6862
|
+
console.error(chalk9.red("\n \u2716 Provide --id <proposalId> or --execute-calls <path>"));
|
|
6769
6863
|
process.exit(1);
|
|
6770
6864
|
}
|
|
6771
|
-
const spinner =
|
|
6865
|
+
const spinner = ora7({ text: W3("Simulating execute calls..."), color: "green" }).start();
|
|
6772
6866
|
const execResult = await simulateBatchCalls(vault, executeCalls, "execute", riskContext);
|
|
6773
6867
|
spinner.stop();
|
|
6774
6868
|
printSimulationResult(execResult, "execute");
|
|
6775
6869
|
let settleResult;
|
|
6776
6870
|
if (settlementCalls.length > 0) {
|
|
6777
|
-
const settleSpinner =
|
|
6871
|
+
const settleSpinner = ora7({ text: W3("Simulating settlement calls..."), color: "green" }).start();
|
|
6778
6872
|
settleResult = await simulateBatchCalls(vault, settlementCalls, "settlement", riskContext);
|
|
6779
6873
|
settleSpinner.stop();
|
|
6780
6874
|
printSimulationResult(settleResult, "settlement");
|
|
6781
6875
|
}
|
|
6782
6876
|
if (opts.notify && proposalId !== void 0) {
|
|
6783
|
-
const notifySpinner =
|
|
6877
|
+
const notifySpinner = ora7({ text: W3("Sending XMTP alert..."), color: "green" }).start();
|
|
6784
6878
|
await sendSimulationAlert(opts.notify, proposalId, vault, execResult, settleResult);
|
|
6785
6879
|
notifySpinner.succeed(G3("Risk report sent to chat"));
|
|
6786
6880
|
}
|
|
@@ -6789,7 +6883,7 @@ function registerProposalCommands(program2) {
|
|
|
6789
6883
|
process.exit(1);
|
|
6790
6884
|
}
|
|
6791
6885
|
} catch (err) {
|
|
6792
|
-
console.error(
|
|
6886
|
+
console.error(chalk9.red(`
|
|
6793
6887
|
\u2716 ${err instanceof Error ? err.message : String(err)}`));
|
|
6794
6888
|
process.exit(1);
|
|
6795
6889
|
}
|
|
@@ -6797,9 +6891,9 @@ function registerProposalCommands(program2) {
|
|
|
6797
6891
|
}
|
|
6798
6892
|
|
|
6799
6893
|
// src/commands/queue.ts
|
|
6800
|
-
import { parseUnits as
|
|
6801
|
-
import
|
|
6802
|
-
import
|
|
6894
|
+
import { parseUnits as parseUnits9, formatUnits as formatUnits9, isAddress as isAddress10, parseEventLogs } from "viem";
|
|
6895
|
+
import chalk10 from "chalk";
|
|
6896
|
+
import ora8 from "ora";
|
|
6803
6897
|
var WETH_ABI2 = [
|
|
6804
6898
|
{ name: "deposit", type: "function", stateMutability: "payable", inputs: [], outputs: [] }
|
|
6805
6899
|
];
|
|
@@ -6933,8 +7027,8 @@ var QUEUE_ABI = [
|
|
|
6933
7027
|
];
|
|
6934
7028
|
var KIND_LABELS = ["Redeem", "Deposit"];
|
|
6935
7029
|
function resolveVaultAddress(opt) {
|
|
6936
|
-
if (!opt || !
|
|
6937
|
-
console.error(
|
|
7030
|
+
if (!opt || !isAddress10(opt)) {
|
|
7031
|
+
console.error(chalk10.red(`Invalid or missing --vault address: ${opt ?? "(none)"}`));
|
|
6938
7032
|
process.exit(1);
|
|
6939
7033
|
}
|
|
6940
7034
|
return opt;
|
|
@@ -6964,10 +7058,10 @@ function registerQueueCommands(program2) {
|
|
|
6964
7058
|
const vault = resolveVaultAddress(opts.vault);
|
|
6965
7059
|
const account = getAccount();
|
|
6966
7060
|
const client = getPublicClient();
|
|
6967
|
-
const spinner =
|
|
7061
|
+
const spinner = ora8(`Queueing deposit of ${opts.amount}...`).start();
|
|
6968
7062
|
try {
|
|
6969
7063
|
const { asset, decimals } = await getVaultAsset(vault);
|
|
6970
|
-
const assets =
|
|
7064
|
+
const assets = parseUnits9(opts.amount, decimals);
|
|
6971
7065
|
if (opts.useEth) {
|
|
6972
7066
|
if (asset.toLowerCase() !== TOKENS().WETH.toLowerCase()) {
|
|
6973
7067
|
throw new Error("--use-eth is only supported for WETH vaults.");
|
|
@@ -6986,7 +7080,7 @@ function registerQueueCommands(program2) {
|
|
|
6986
7080
|
);
|
|
6987
7081
|
if (ethBalance < shortfall + minGas) {
|
|
6988
7082
|
throw new Error(
|
|
6989
|
-
`Insufficient ETH on ${getChain().name}. Have ${
|
|
7083
|
+
`Insufficient ETH on ${getChain().name}. Have ${formatUnits9(ethBalance, 18)} ETH, need ${formatUnits9(shortfall, 18)} to wrap + ~${formatUnits9(minGas, 18)} for gas.`
|
|
6990
7084
|
);
|
|
6991
7085
|
}
|
|
6992
7086
|
spinner.text = "Wrapping ETH \u2192 WETH...";
|
|
@@ -7038,16 +7132,16 @@ function registerQueueCommands(program2) {
|
|
|
7038
7132
|
const id = logs[0]?.args?.requestId;
|
|
7039
7133
|
if (id === void 0) {
|
|
7040
7134
|
spinner.fail("Deposit queued but could not read requestId from logs");
|
|
7041
|
-
console.log(
|
|
7135
|
+
console.log(chalk10.dim(` ${getExplorerUrl(hash)}`));
|
|
7042
7136
|
process.exit(1);
|
|
7043
7137
|
}
|
|
7044
7138
|
const req = await readRequest(await getQueueAddress(vault), id);
|
|
7045
7139
|
spinner.succeed(`Queued deposit request #${id} (pid ${req.pid})`);
|
|
7046
7140
|
console.log(`Queued deposit request #${id} (pid ${req.pid})`);
|
|
7047
|
-
console.log(
|
|
7141
|
+
console.log(chalk10.dim(` ${getExplorerUrl(hash)}`));
|
|
7048
7142
|
} catch (err) {
|
|
7049
7143
|
spinner.fail("Queue deposit failed");
|
|
7050
|
-
console.error(
|
|
7144
|
+
console.error(chalk10.red(formatContractError(err)));
|
|
7051
7145
|
process.exit(1);
|
|
7052
7146
|
}
|
|
7053
7147
|
});
|
|
@@ -7055,7 +7149,7 @@ function registerQueueCommands(program2) {
|
|
|
7055
7149
|
const vault = resolveVaultAddress(opts.vault);
|
|
7056
7150
|
const account = getAccount();
|
|
7057
7151
|
const shares = BigInt(opts.shares);
|
|
7058
|
-
const spinner =
|
|
7152
|
+
const spinner = ora8(`Queueing redeem of ${opts.shares} shares...`).start();
|
|
7059
7153
|
try {
|
|
7060
7154
|
const hash = await writeContractWithRetry({
|
|
7061
7155
|
account,
|
|
@@ -7074,16 +7168,16 @@ function registerQueueCommands(program2) {
|
|
|
7074
7168
|
const id = logs[0]?.args?.requestId;
|
|
7075
7169
|
if (id === void 0) {
|
|
7076
7170
|
spinner.fail("Redeem queued but could not read requestId from logs");
|
|
7077
|
-
console.log(
|
|
7171
|
+
console.log(chalk10.dim(` ${getExplorerUrl(hash)}`));
|
|
7078
7172
|
process.exit(1);
|
|
7079
7173
|
}
|
|
7080
7174
|
const req = await readRequest(await getQueueAddress(vault), id);
|
|
7081
7175
|
spinner.succeed(`Queued redeem request #${id} (pid ${req.pid})`);
|
|
7082
7176
|
console.log(`Queued redeem request #${id} (pid ${req.pid})`);
|
|
7083
|
-
console.log(
|
|
7177
|
+
console.log(chalk10.dim(` ${getExplorerUrl(hash)}`));
|
|
7084
7178
|
} catch (err) {
|
|
7085
7179
|
spinner.fail("Queue redeem failed");
|
|
7086
|
-
console.error(
|
|
7180
|
+
console.error(chalk10.red(formatContractError(err)));
|
|
7087
7181
|
process.exit(1);
|
|
7088
7182
|
}
|
|
7089
7183
|
});
|
|
@@ -7091,7 +7185,7 @@ function registerQueueCommands(program2) {
|
|
|
7091
7185
|
const vault = resolveVaultAddress(opts.vault);
|
|
7092
7186
|
const account = getAccount();
|
|
7093
7187
|
const id = BigInt(opts.id);
|
|
7094
|
-
const spinner =
|
|
7188
|
+
const spinner = ora8(`Claiming request #${id}...`).start();
|
|
7095
7189
|
try {
|
|
7096
7190
|
const queueAddr = await getQueueAddress(vault);
|
|
7097
7191
|
const hash = await writeContractWithRetry({
|
|
@@ -7108,10 +7202,10 @@ function registerQueueCommands(program2) {
|
|
|
7108
7202
|
const unit = req.kind === 1 ? "shares" : "assets";
|
|
7109
7203
|
void receipt;
|
|
7110
7204
|
spinner.succeed(`Claimed ${label} request #${id} \u2192 received ${unit}`);
|
|
7111
|
-
console.log(
|
|
7205
|
+
console.log(chalk10.dim(` ${getExplorerUrl(hash)}`));
|
|
7112
7206
|
} catch (err) {
|
|
7113
7207
|
spinner.fail("Claim failed");
|
|
7114
|
-
console.error(
|
|
7208
|
+
console.error(chalk10.red(formatContractError(err)));
|
|
7115
7209
|
process.exit(1);
|
|
7116
7210
|
}
|
|
7117
7211
|
});
|
|
@@ -7119,7 +7213,7 @@ function registerQueueCommands(program2) {
|
|
|
7119
7213
|
const vault = resolveVaultAddress(opts.vault);
|
|
7120
7214
|
const account = getAccount();
|
|
7121
7215
|
const id = BigInt(opts.id);
|
|
7122
|
-
const spinner =
|
|
7216
|
+
const spinner = ora8(`Cancelling request #${id}...`).start();
|
|
7123
7217
|
try {
|
|
7124
7218
|
const queueAddr = await getQueueAddress(vault);
|
|
7125
7219
|
const hash = await writeContractWithRetry({
|
|
@@ -7132,10 +7226,10 @@ function registerQueueCommands(program2) {
|
|
|
7132
7226
|
});
|
|
7133
7227
|
await waitForReceipt(hash);
|
|
7134
7228
|
spinner.succeed(`Cancelled request #${id}`);
|
|
7135
|
-
console.log(
|
|
7229
|
+
console.log(chalk10.dim(` ${getExplorerUrl(hash)}`));
|
|
7136
7230
|
} catch (err) {
|
|
7137
7231
|
spinner.fail("Cancel failed (request may already be settled \u2014 cancel is only allowed before the price is stamped)");
|
|
7138
|
-
console.error(
|
|
7232
|
+
console.error(chalk10.red(formatContractError(err)));
|
|
7139
7233
|
process.exit(1);
|
|
7140
7234
|
}
|
|
7141
7235
|
});
|
|
@@ -7143,7 +7237,7 @@ function registerQueueCommands(program2) {
|
|
|
7143
7237
|
const vault = resolveVaultAddress(opts.vault);
|
|
7144
7238
|
const account = getAccount();
|
|
7145
7239
|
const client = getPublicClient();
|
|
7146
|
-
const spinner =
|
|
7240
|
+
const spinner = ora8("Loading queue requests...").start();
|
|
7147
7241
|
try {
|
|
7148
7242
|
const queueAddr = await getQueueAddress(vault);
|
|
7149
7243
|
const ids = await client.readContract({
|
|
@@ -7154,16 +7248,16 @@ function registerQueueCommands(program2) {
|
|
|
7154
7248
|
});
|
|
7155
7249
|
if (ids.length === 0) {
|
|
7156
7250
|
spinner.stop();
|
|
7157
|
-
console.log(
|
|
7251
|
+
console.log(chalk10.dim(" No queue requests for this address."));
|
|
7158
7252
|
return;
|
|
7159
7253
|
}
|
|
7160
7254
|
const requests = await Promise.all(ids.map((id) => readRequest(queueAddr, id)));
|
|
7161
7255
|
spinner.stop();
|
|
7162
7256
|
console.log();
|
|
7163
|
-
console.log(
|
|
7164
|
-
console.log(
|
|
7257
|
+
console.log(chalk10.bold(`Queue requests (${ids.length}) \u2014 ${account.address}`));
|
|
7258
|
+
console.log(chalk10.dim("\u2500".repeat(72)));
|
|
7165
7259
|
console.log(
|
|
7166
|
-
|
|
7260
|
+
chalk10.dim(
|
|
7167
7261
|
` ${"id".padEnd(6)}${"kind".padEnd(9)}${"amount".padEnd(26)}${"pid".padEnd(6)}${"claimed".padEnd(9)}cancelled`
|
|
7168
7262
|
)
|
|
7169
7263
|
);
|
|
@@ -7177,26 +7271,26 @@ function registerQueueCommands(program2) {
|
|
|
7177
7271
|
console.log();
|
|
7178
7272
|
} catch (err) {
|
|
7179
7273
|
spinner.fail("Failed to load queue requests");
|
|
7180
|
-
console.error(
|
|
7274
|
+
console.error(chalk10.red(formatContractError(err)));
|
|
7181
7275
|
process.exit(1);
|
|
7182
7276
|
}
|
|
7183
7277
|
});
|
|
7184
7278
|
}
|
|
7185
7279
|
|
|
7186
7280
|
// src/commands/governor.ts
|
|
7187
|
-
import
|
|
7188
|
-
import
|
|
7189
|
-
import { isAddress as
|
|
7190
|
-
var G4 =
|
|
7191
|
-
var W4 =
|
|
7192
|
-
var DIM4 =
|
|
7193
|
-
var BOLD4 =
|
|
7194
|
-
var LABEL4 =
|
|
7281
|
+
import chalk11 from "chalk";
|
|
7282
|
+
import ora9 from "ora";
|
|
7283
|
+
import { isAddress as isAddress11 } from "viem";
|
|
7284
|
+
var G4 = chalk11.green;
|
|
7285
|
+
var W4 = chalk11.white;
|
|
7286
|
+
var DIM4 = chalk11.gray;
|
|
7287
|
+
var BOLD4 = chalk11.white.bold;
|
|
7288
|
+
var LABEL4 = chalk11.green.bold;
|
|
7195
7289
|
var SEP4 = () => console.log(DIM4("\u2500".repeat(60)));
|
|
7196
7290
|
function parseVaultArg(raw) {
|
|
7197
7291
|
const vault = String(raw);
|
|
7198
|
-
if (!
|
|
7199
|
-
console.error(
|
|
7292
|
+
if (!isAddress11(vault)) {
|
|
7293
|
+
console.error(chalk11.red(`Invalid vault address: ${vault}`));
|
|
7200
7294
|
process.exit(1);
|
|
7201
7295
|
}
|
|
7202
7296
|
return vault;
|
|
@@ -7215,19 +7309,19 @@ function registerParamSetter(governor, spec) {
|
|
|
7215
7309
|
)
|
|
7216
7310
|
);
|
|
7217
7311
|
} catch (err) {
|
|
7218
|
-
console.error(
|
|
7312
|
+
console.error(chalk11.red(formatContractError(err)));
|
|
7219
7313
|
process.exit(1);
|
|
7220
7314
|
}
|
|
7221
7315
|
return;
|
|
7222
7316
|
}
|
|
7223
|
-
const spinner =
|
|
7317
|
+
const spinner = ora9(spec.spinnerText).start();
|
|
7224
7318
|
try {
|
|
7225
7319
|
const hash = await spec.setter(vault, parseBigIntArg(rawValue, spec.valueLabel));
|
|
7226
7320
|
spinner.succeed(G4(spec.succeed(rawValue)));
|
|
7227
7321
|
console.log(DIM4(` ${getExplorerUrl(hash)}`));
|
|
7228
7322
|
} catch (err) {
|
|
7229
7323
|
spinner.fail(spec.failText);
|
|
7230
|
-
console.error(
|
|
7324
|
+
console.error(chalk11.red(formatContractError(err)));
|
|
7231
7325
|
process.exit(1);
|
|
7232
7326
|
}
|
|
7233
7327
|
});
|
|
@@ -7236,7 +7330,7 @@ function registerGovernorCommands(program2) {
|
|
|
7236
7330
|
const governor = program2.command("governor").description("Per-vault governor parameters");
|
|
7237
7331
|
governor.command("info").description("Display a vault's governor address and parameters").requiredOption("--vault <address>", "Vault whose governor to inspect").action(async (opts) => {
|
|
7238
7332
|
const vault = parseVaultArg(opts.vault);
|
|
7239
|
-
const spinner =
|
|
7333
|
+
const spinner = ora9("Loading governor info...").start();
|
|
7240
7334
|
try {
|
|
7241
7335
|
const [governorAddress, params] = await Promise.all([
|
|
7242
7336
|
getGovernorOf(vault),
|
|
@@ -7258,7 +7352,7 @@ function registerGovernorCommands(program2) {
|
|
|
7258
7352
|
console.log();
|
|
7259
7353
|
} catch (err) {
|
|
7260
7354
|
spinner.fail("Failed to load governor info");
|
|
7261
|
-
console.error(
|
|
7355
|
+
console.error(chalk11.red(formatContractError(err)));
|
|
7262
7356
|
process.exit(1);
|
|
7263
7357
|
}
|
|
7264
7358
|
});
|
|
@@ -7346,12 +7440,12 @@ function registerGovernorCommands(program2) {
|
|
|
7346
7440
|
)
|
|
7347
7441
|
);
|
|
7348
7442
|
} catch (err) {
|
|
7349
|
-
console.error(
|
|
7443
|
+
console.error(chalk11.red(formatContractError(err)));
|
|
7350
7444
|
process.exit(1);
|
|
7351
7445
|
}
|
|
7352
7446
|
return;
|
|
7353
7447
|
}
|
|
7354
|
-
const spinner =
|
|
7448
|
+
const spinner = ora9("Setting protocol fee...").start();
|
|
7355
7449
|
try {
|
|
7356
7450
|
const hash = await setProtocolFeeBps(parseBigIntArg(rawValue, "bps"));
|
|
7357
7451
|
const configAddress = await getProtocolConfigAddress();
|
|
@@ -7361,23 +7455,23 @@ function registerGovernorCommands(program2) {
|
|
|
7361
7455
|
console.log(DIM4(` ${getExplorerUrl(hash)}`));
|
|
7362
7456
|
} catch (err) {
|
|
7363
7457
|
spinner.fail("Failed to set protocol fee");
|
|
7364
|
-
console.error(
|
|
7458
|
+
console.error(chalk11.red(formatContractError(err)));
|
|
7365
7459
|
process.exit(1);
|
|
7366
7460
|
}
|
|
7367
7461
|
});
|
|
7368
7462
|
}
|
|
7369
7463
|
|
|
7370
7464
|
// src/commands/grid.ts
|
|
7371
|
-
import
|
|
7465
|
+
import chalk17 from "chalk";
|
|
7372
7466
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
7373
7467
|
|
|
7374
7468
|
// src/grid/loop.ts
|
|
7375
|
-
import
|
|
7469
|
+
import chalk16 from "chalk";
|
|
7376
7470
|
import { appendFile, mkdir as mkdir4 } from "fs/promises";
|
|
7377
7471
|
import { dirname as dirname4 } from "path";
|
|
7378
7472
|
|
|
7379
7473
|
// src/grid/manager.ts
|
|
7380
|
-
import
|
|
7474
|
+
import chalk12 from "chalk";
|
|
7381
7475
|
|
|
7382
7476
|
// src/grid/portfolio.ts
|
|
7383
7477
|
import { readFile, writeFile, rename, mkdir } from "fs/promises";
|
|
@@ -7596,7 +7690,7 @@ var GridManager = class {
|
|
|
7596
7690
|
return { fills: 0, roundTrips: 0, pnlUsd: 0, paused: state.paused };
|
|
7597
7691
|
}
|
|
7598
7692
|
if (regime === "high-volatility") {
|
|
7599
|
-
console.error(
|
|
7693
|
+
console.error(chalk12.dim(` [grid] Paused: high-volatility regime \u2014 too risky for grid`));
|
|
7600
7694
|
return { fills: 0, roundTrips: 0, pnlUsd: 0, paused: false };
|
|
7601
7695
|
}
|
|
7602
7696
|
this.portfolio.resetDailyStats(state);
|
|
@@ -7642,13 +7736,13 @@ var GridManager = class {
|
|
|
7642
7736
|
async buildGrid(grid, currentPrice) {
|
|
7643
7737
|
const candles = await this.candleFetcher(grid.token, "4h", 14 * 24 * 60 * 60 * 1e3);
|
|
7644
7738
|
if (!candles || candles.length < this.config.atrPeriod) {
|
|
7645
|
-
console.error(
|
|
7739
|
+
console.error(chalk12.dim(` [grid] Cannot build grid for ${grid.token}: insufficient candle data`));
|
|
7646
7740
|
return;
|
|
7647
7741
|
}
|
|
7648
7742
|
const signals = getLatestSignals(candles);
|
|
7649
7743
|
const atr = signals.atr;
|
|
7650
7744
|
if (!Number.isFinite(atr) || atr <= 0) {
|
|
7651
|
-
console.error(
|
|
7745
|
+
console.error(chalk12.dim(` [grid] Cannot build grid for ${grid.token}: invalid ATR (${atr})`));
|
|
7652
7746
|
return;
|
|
7653
7747
|
}
|
|
7654
7748
|
const TREND_LOOKBACK_BARS = 14;
|
|
@@ -7691,7 +7785,7 @@ var GridManager = class {
|
|
|
7691
7785
|
grid.atr = atr;
|
|
7692
7786
|
grid.trend = trend;
|
|
7693
7787
|
grid.stats.lastRebalanceAt = Date.now();
|
|
7694
|
-
console.error(
|
|
7788
|
+
console.error(chalk12.dim(
|
|
7695
7789
|
` [grid] Built ${grid.token} grid: center=$${currentPrice.toFixed(2)} ATR=$${atr.toFixed(2)} range=$${(currentPrice - range).toFixed(2)}-$${(currentPrice + range).toFixed(2)} spacing=$${spacing.toFixed(2)} qty=${quantity.toFixed(6)} trend=${(trend * 100).toFixed(1)}%${inDowntrend ? " [DOWNTREND: 1 buy / " + this.config.levelsPerSide + " sells]" : ""}`
|
|
7696
7790
|
));
|
|
7697
7791
|
}
|
|
@@ -7731,7 +7825,7 @@ var GridManager = class {
|
|
|
7731
7825
|
pnlUsd: 0,
|
|
7732
7826
|
closedAt: 0
|
|
7733
7827
|
});
|
|
7734
|
-
console.error(
|
|
7828
|
+
console.error(chalk12.dim(
|
|
7735
7829
|
` [grid] BUY fill ${grid.token} @ $${level.price.toFixed(2)} qty=${level.quantity.toFixed(6)}`
|
|
7736
7830
|
));
|
|
7737
7831
|
}
|
|
@@ -7741,7 +7835,7 @@ var GridManager = class {
|
|
|
7741
7835
|
fills++;
|
|
7742
7836
|
grid.stats.totalFills++;
|
|
7743
7837
|
grid.stats.todayFills++;
|
|
7744
|
-
console.error(
|
|
7838
|
+
console.error(chalk12.dim(
|
|
7745
7839
|
` [grid] SELL level ${grid.token} @ $${level.price.toFixed(2)} qty=${level.quantity.toFixed(6)}`
|
|
7746
7840
|
));
|
|
7747
7841
|
}
|
|
@@ -7760,7 +7854,7 @@ var GridManager = class {
|
|
|
7760
7854
|
grid.stats.totalPnlUsd += profit;
|
|
7761
7855
|
grid.stats.todayPnlUsd += profit;
|
|
7762
7856
|
grid.allocation += profit;
|
|
7763
|
-
console.error(
|
|
7857
|
+
console.error(chalk12.green(
|
|
7764
7858
|
` [grid] ROUND-TRIP ${grid.token}: buy $${openFill.buyPrice.toFixed(2)} \u2192 sell $${currentPrice.toFixed(2)} = +$${profit.toFixed(2)}`
|
|
7765
7859
|
));
|
|
7766
7860
|
}
|
|
@@ -7779,7 +7873,7 @@ var GridManager = class {
|
|
|
7779
7873
|
grid.stats.totalPnlUsd += realizedLoss;
|
|
7780
7874
|
grid.stats.todayPnlUsd += realizedLoss;
|
|
7781
7875
|
grid.allocation += realizedLoss;
|
|
7782
|
-
console.error(
|
|
7876
|
+
console.error(chalk12.red(
|
|
7783
7877
|
` [grid] STOP-LOSS ${grid.token}: buy $${openFill.buyPrice.toFixed(2)} \u2192 close $${currentPrice.toFixed(2)} = $${realizedLoss.toFixed(2)}`
|
|
7784
7878
|
));
|
|
7785
7879
|
}
|
|
@@ -7804,7 +7898,7 @@ var GridManager = class {
|
|
|
7804
7898
|
}
|
|
7805
7899
|
/** Shift the grid to re-center on the current price. */
|
|
7806
7900
|
async shiftGrid(grid, currentPrice) {
|
|
7807
|
-
console.error(
|
|
7901
|
+
console.error(chalk12.dim(
|
|
7808
7902
|
` [grid] Shifting ${grid.token} grid: center $${grid.centerPrice.toFixed(2)} \u2192 $${currentPrice.toFixed(2)}`
|
|
7809
7903
|
));
|
|
7810
7904
|
await this.buildGrid(grid, currentPrice);
|
|
@@ -7928,7 +8022,7 @@ var DEFAULT_GRID_CONFIG = {
|
|
|
7928
8022
|
// src/grid/hedge.ts
|
|
7929
8023
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
7930
8024
|
import { dirname as dirname2 } from "path";
|
|
7931
|
-
import
|
|
8025
|
+
import chalk13 from "chalk";
|
|
7932
8026
|
var HEDGE_RATIO_DEFAULT = Number.isFinite(Number(process.env.HEDGE_RATIO)) ? Number(process.env.HEDGE_RATIO) : 0.3;
|
|
7933
8027
|
function hedgeRatioFor(marketTrend) {
|
|
7934
8028
|
if (process.env.HEDGE_RATIO !== void 0 && Number.isFinite(Number(process.env.HEDGE_RATIO))) {
|
|
@@ -8011,7 +8105,7 @@ var GridHedgeManager = class {
|
|
|
8011
8105
|
state.totalRealizedPnl += closePnl;
|
|
8012
8106
|
state.todayRealizedPnl += closePnl;
|
|
8013
8107
|
existing.realizedPnl += closePnl;
|
|
8014
|
-
console.error(
|
|
8108
|
+
console.error(chalk13.yellow(
|
|
8015
8109
|
` [hedge] CLOSE ${fill.token} short ${existing.quantity.toFixed(6)} @ $${price.toFixed(2)} PnL: $${closePnl.toFixed(2)}`
|
|
8016
8110
|
));
|
|
8017
8111
|
existing.quantity = 0;
|
|
@@ -8028,7 +8122,7 @@ var GridHedgeManager = class {
|
|
|
8028
8122
|
lastAdjustedAt: Date.now(),
|
|
8029
8123
|
realizedPnl: 0
|
|
8030
8124
|
});
|
|
8031
|
-
console.error(
|
|
8125
|
+
console.error(chalk13.magenta(
|
|
8032
8126
|
` [hedge] OPEN ${fill.token} short ${desiredShortQty.toFixed(6)} @ $${price.toFixed(2)} (${(activeRatio * 100).toFixed(0)}% of ${fill.fillCount} open fills)`
|
|
8033
8127
|
));
|
|
8034
8128
|
adjustments++;
|
|
@@ -8041,7 +8135,7 @@ var GridHedgeManager = class {
|
|
|
8041
8135
|
existing.entryPrice = newAvgEntry;
|
|
8042
8136
|
existing.quantity = desiredShortQty;
|
|
8043
8137
|
existing.lastAdjustedAt = Date.now();
|
|
8044
|
-
console.error(
|
|
8138
|
+
console.error(chalk13.magenta(
|
|
8045
8139
|
` [hedge] INCREASE ${fill.token} short to ${desiredShortQty.toFixed(6)} @ avg $${newAvgEntry.toFixed(2)}`
|
|
8046
8140
|
));
|
|
8047
8141
|
adjustments++;
|
|
@@ -8053,7 +8147,7 @@ var GridHedgeManager = class {
|
|
|
8053
8147
|
existing.realizedPnl += closePnl;
|
|
8054
8148
|
existing.quantity = desiredShortQty;
|
|
8055
8149
|
existing.lastAdjustedAt = Date.now();
|
|
8056
|
-
console.error(
|
|
8150
|
+
console.error(chalk13.magenta(
|
|
8057
8151
|
` [hedge] DECREASE ${fill.token} short to ${desiredShortQty.toFixed(6)}, closed ${closeQty.toFixed(6)} PnL: $${closePnl.toFixed(2)}`
|
|
8058
8152
|
));
|
|
8059
8153
|
adjustments++;
|
|
@@ -8065,7 +8159,7 @@ var GridHedgeManager = class {
|
|
|
8065
8159
|
existing.entryPrice = price;
|
|
8066
8160
|
existing.quantity = desiredShortQty;
|
|
8067
8161
|
existing.lastAdjustedAt = Date.now();
|
|
8068
|
-
console.error(
|
|
8162
|
+
console.error(chalk13.magenta(
|
|
8069
8163
|
` [hedge] REOPEN ${fill.token} short ${desiredShortQty.toFixed(6)} @ $${price.toFixed(2)}`
|
|
8070
8164
|
));
|
|
8071
8165
|
adjustments++;
|
|
@@ -8080,7 +8174,7 @@ var GridHedgeManager = class {
|
|
|
8080
8174
|
state.totalRealizedPnl += closePnl;
|
|
8081
8175
|
state.todayRealizedPnl += closePnl;
|
|
8082
8176
|
pos.realizedPnl += closePnl;
|
|
8083
|
-
console.error(
|
|
8177
|
+
console.error(chalk13.yellow(
|
|
8084
8178
|
` [hedge] CLOSE orphaned ${pos.token} short, PnL: $${closePnl.toFixed(2)}`
|
|
8085
8179
|
));
|
|
8086
8180
|
pos.quantity = 0;
|
|
@@ -8140,7 +8234,7 @@ var GridHedgeManager = class {
|
|
|
8140
8234
|
};
|
|
8141
8235
|
|
|
8142
8236
|
// src/grid/executor.ts
|
|
8143
|
-
import
|
|
8237
|
+
import chalk14 from "chalk";
|
|
8144
8238
|
var GridExecutor = class {
|
|
8145
8239
|
cfg;
|
|
8146
8240
|
constructor(cfg) {
|
|
@@ -8163,7 +8257,7 @@ var GridExecutor = class {
|
|
|
8163
8257
|
try {
|
|
8164
8258
|
await hlCancelAllOrders(coin);
|
|
8165
8259
|
cancelled++;
|
|
8166
|
-
console.error(
|
|
8260
|
+
console.error(chalk14.dim(` [grid-exec] Cancelled all orders for ${coin}`));
|
|
8167
8261
|
} catch (e) {
|
|
8168
8262
|
errors.push(`Cancel ${coin} failed: ${e.message}`);
|
|
8169
8263
|
}
|
|
@@ -8190,16 +8284,16 @@ var GridExecutor = class {
|
|
|
8190
8284
|
};
|
|
8191
8285
|
|
|
8192
8286
|
// src/grid/onchain-executor.ts
|
|
8193
|
-
import
|
|
8287
|
+
import chalk15 from "chalk";
|
|
8194
8288
|
import { mkdir as mkdir3, readFile as readFile3, rename as rename2, writeFile as writeFile3 } from "fs/promises";
|
|
8195
8289
|
import { dirname as dirname3 } from "path";
|
|
8196
|
-
import { encodeAbiParameters as
|
|
8290
|
+
import { encodeAbiParameters as encodeAbiParameters7, parseUnits as parseUnits10 } from "viem";
|
|
8197
8291
|
|
|
8198
8292
|
// src/grid/cloid.ts
|
|
8199
|
-
import { keccak256 as keccak2562, encodeAbiParameters as
|
|
8293
|
+
import { keccak256 as keccak2562, encodeAbiParameters as encodeAbiParameters6 } from "viem";
|
|
8200
8294
|
function gridCloid(strategy2, assetIndex, isBuy, levelIndex, nonce) {
|
|
8201
8295
|
const hash = keccak2562(
|
|
8202
|
-
|
|
8296
|
+
encodeAbiParameters6(
|
|
8203
8297
|
[
|
|
8204
8298
|
{ type: "address" },
|
|
8205
8299
|
{ type: "uint32" },
|
|
@@ -8275,13 +8369,13 @@ var OnchainGridExecutor = class {
|
|
|
8275
8369
|
for (const [tok, cloids] of Object.entries(state.placedCloids)) {
|
|
8276
8370
|
this.placedCloids.set(tok, cloids.map((s) => BigInt(s)));
|
|
8277
8371
|
}
|
|
8278
|
-
console.error(
|
|
8372
|
+
console.error(chalk15.dim(` [grid-onchain] Loaded state: ${this.nonces.size} tokens`));
|
|
8279
8373
|
} catch (e) {
|
|
8280
8374
|
const code = e.code;
|
|
8281
8375
|
if (code === "ENOENT") {
|
|
8282
8376
|
} else {
|
|
8283
8377
|
console.error(
|
|
8284
|
-
|
|
8378
|
+
chalk15.yellow(
|
|
8285
8379
|
` [grid-onchain] Failed to load state (${e.message}) \u2014 starting fresh. Existing on-chain orders may be orphaned.`
|
|
8286
8380
|
)
|
|
8287
8381
|
);
|
|
@@ -8401,8 +8495,8 @@ var OnchainGridExecutor = class {
|
|
|
8401
8495
|
const encoded = orders.map((o, i) => {
|
|
8402
8496
|
const cloid = gridCloid(this.cfg.strategyAddress, assetIndex, o.isBuy, i, nonce);
|
|
8403
8497
|
cloids.push(cloid);
|
|
8404
|
-
const limitPx =
|
|
8405
|
-
const sz =
|
|
8498
|
+
const limitPx = parseUnits10(o.price.toFixed(meta.pxDecimals), meta.pxDecimals);
|
|
8499
|
+
const sz = parseUnits10(o.quantity.toFixed(meta.szDecimals), meta.szDecimals);
|
|
8406
8500
|
if (limitPx > UINT64_MAX || sz > UINT64_MAX) {
|
|
8407
8501
|
throw new Error(
|
|
8408
8502
|
`Order exceeds uint64: token=${o.token} px=${limitPx} sz=${sz}`
|
|
@@ -8426,7 +8520,7 @@ var OnchainGridExecutor = class {
|
|
|
8426
8520
|
for (const chunk of chunks) {
|
|
8427
8521
|
const nonce = this.bumpNonce(token);
|
|
8428
8522
|
const { encoded, cloids } = this.encodeOrders(assetIndex, chunk, meta, nonce);
|
|
8429
|
-
const data =
|
|
8523
|
+
const data = encodeAbiParameters7(
|
|
8430
8524
|
[{ type: "uint8" }, { type: "tuple[]", components: [...GRID_ORDER_COMPONENTS] }],
|
|
8431
8525
|
[ACTION_PLACE_GRID, encoded]
|
|
8432
8526
|
);
|
|
@@ -8444,7 +8538,7 @@ var OnchainGridExecutor = class {
|
|
|
8444
8538
|
this.bumpNonce(token);
|
|
8445
8539
|
return "0x";
|
|
8446
8540
|
}
|
|
8447
|
-
const data =
|
|
8541
|
+
const data = encodeAbiParameters7(
|
|
8448
8542
|
[{ type: "uint8" }, { type: "uint32" }, { type: "uint128[]" }],
|
|
8449
8543
|
[ACTION_CANCEL_ALL, assetIndex, cloids]
|
|
8450
8544
|
);
|
|
@@ -8473,7 +8567,7 @@ var OnchainGridExecutor = class {
|
|
|
8473
8567
|
const { encoded, cloids } = this.encodeOrders(assetIndex, chunk, meta, newNonce);
|
|
8474
8568
|
let data;
|
|
8475
8569
|
if (i === 0) {
|
|
8476
|
-
data =
|
|
8570
|
+
data = encodeAbiParameters7(
|
|
8477
8571
|
[
|
|
8478
8572
|
{ type: "uint8" },
|
|
8479
8573
|
{ type: "uint32" },
|
|
@@ -8483,7 +8577,7 @@ var OnchainGridExecutor = class {
|
|
|
8483
8577
|
[ACTION_CANCEL_AND_PLACE, assetIndex, oldCloids, encoded]
|
|
8484
8578
|
);
|
|
8485
8579
|
} else {
|
|
8486
|
-
data =
|
|
8580
|
+
data = encodeAbiParameters7(
|
|
8487
8581
|
[{ type: "uint8" }, { type: "tuple[]", components: [...GRID_ORDER_COMPONENTS] }],
|
|
8488
8582
|
[ACTION_PLACE_GRID, encoded]
|
|
8489
8583
|
);
|
|
@@ -8554,7 +8648,7 @@ var OnchainGridExecutor = class {
|
|
|
8554
8648
|
if (receipt.status !== "success") {
|
|
8555
8649
|
throw new Error(`Tx ${tx} reverted on-chain`);
|
|
8556
8650
|
}
|
|
8557
|
-
console.error(
|
|
8651
|
+
console.error(chalk15.dim(` [grid-onchain] tx ${tx.slice(0, 10)}... confirmed`));
|
|
8558
8652
|
return tx;
|
|
8559
8653
|
}
|
|
8560
8654
|
};
|
|
@@ -8605,7 +8699,7 @@ var GridLoop = class {
|
|
|
8605
8699
|
async start() {
|
|
8606
8700
|
this.running = true;
|
|
8607
8701
|
const shutdown = () => {
|
|
8608
|
-
console.error(
|
|
8702
|
+
console.error(chalk16.yellow("\n [grid-loop] Shutting down\u2026"));
|
|
8609
8703
|
this.running = false;
|
|
8610
8704
|
if (this.timer) {
|
|
8611
8705
|
clearTimeout(this.timer);
|
|
@@ -8618,7 +8712,7 @@ var GridLoop = class {
|
|
|
8618
8712
|
if (this.executor instanceof OnchainGridExecutor) {
|
|
8619
8713
|
await this.executor.load();
|
|
8620
8714
|
}
|
|
8621
|
-
console.error(
|
|
8715
|
+
console.error(chalk16.cyan(
|
|
8622
8716
|
`
|
|
8623
8717
|
[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 state-dir=${gridStateDir(this.cfg.stateDir)}
|
|
8624
8718
|
`
|
|
@@ -8627,7 +8721,7 @@ var GridLoop = class {
|
|
|
8627
8721
|
try {
|
|
8628
8722
|
await this.tick();
|
|
8629
8723
|
} catch (err) {
|
|
8630
|
-
console.error(
|
|
8724
|
+
console.error(chalk16.red(` [grid-loop] Tick error: ${err.message}`));
|
|
8631
8725
|
}
|
|
8632
8726
|
if (this.running) {
|
|
8633
8727
|
await new Promise((resolve3) => {
|
|
@@ -8637,7 +8731,7 @@ var GridLoop = class {
|
|
|
8637
8731
|
}
|
|
8638
8732
|
process.off("SIGINT", shutdown);
|
|
8639
8733
|
process.off("SIGTERM", shutdown);
|
|
8640
|
-
console.error(
|
|
8734
|
+
console.error(chalk16.yellow(" [grid-loop] Stopped."));
|
|
8641
8735
|
}
|
|
8642
8736
|
/** Execute one grid cycle. */
|
|
8643
8737
|
async tick() {
|
|
@@ -8651,7 +8745,7 @@ var GridLoop = class {
|
|
|
8651
8745
|
}
|
|
8652
8746
|
}
|
|
8653
8747
|
if (Object.keys(prices).length === 0) {
|
|
8654
|
-
console.error(
|
|
8748
|
+
console.error(chalk16.dim(` [grid-loop] #${this.cycleCount} \u2014 no prices, skipping`));
|
|
8655
8749
|
return;
|
|
8656
8750
|
}
|
|
8657
8751
|
const result = await this.manager.tick(prices);
|
|
@@ -8661,20 +8755,20 @@ var GridLoop = class {
|
|
|
8661
8755
|
if (plan.ordersToPlace.length > 0 || plan.assetsToCancel.length > 0) {
|
|
8662
8756
|
const res = await this.executor.execute(plan);
|
|
8663
8757
|
if (res.errors.length > 0) {
|
|
8664
|
-
console.error(
|
|
8758
|
+
console.error(chalk16.yellow(` [grid-loop] Executor errors: ${res.errors.join("; ")}`));
|
|
8665
8759
|
}
|
|
8666
8760
|
if (res.placed > 0 || res.cancelled > 0) {
|
|
8667
|
-
console.error(
|
|
8761
|
+
console.error(chalk16.cyan(` [grid-loop] Live: placed=${res.placed} cancelled=${res.cancelled}`));
|
|
8668
8762
|
}
|
|
8669
8763
|
}
|
|
8670
8764
|
}
|
|
8671
8765
|
if (result.roundTrips > 0) {
|
|
8672
|
-
console.error(
|
|
8766
|
+
console.error(chalk16.green(
|
|
8673
8767
|
` [grid-loop] #${this.cycleCount} \u2014 ${result.roundTrips} RT(s), +$${result.pnlUsd.toFixed(2)} PnL, ${result.fills} fill(s) [${elapsed}ms]`
|
|
8674
8768
|
));
|
|
8675
8769
|
}
|
|
8676
8770
|
if (result.fills > 0 && result.roundTrips === 0) {
|
|
8677
|
-
console.error(
|
|
8771
|
+
console.error(chalk16.dim(
|
|
8678
8772
|
` [grid-loop] #${this.cycleCount} \u2014 ${result.fills} fill(s), 0 RTs [${elapsed}ms]`
|
|
8679
8773
|
));
|
|
8680
8774
|
}
|
|
@@ -8682,7 +8776,7 @@ var GridLoop = class {
|
|
|
8682
8776
|
const marketTrend = this.manager.getMarketTrend();
|
|
8683
8777
|
const hedgeResult = await this.hedge.tick(openExposure, prices, Date.now(), marketTrend, result.paused);
|
|
8684
8778
|
if (hedgeResult.adjustments > 0) {
|
|
8685
|
-
console.error(
|
|
8779
|
+
console.error(chalk16.magenta(
|
|
8686
8780
|
` [hedge] ${hedgeResult.adjustments} adjustment(s), unrealized: $${hedgeResult.unrealizedPnl.toFixed(2)}, total realized: $${hedgeResult.totalRealizedPnl.toFixed(2)}`
|
|
8687
8781
|
));
|
|
8688
8782
|
}
|
|
@@ -8711,7 +8805,7 @@ var GridLoop = class {
|
|
|
8711
8805
|
if (stats2) {
|
|
8712
8806
|
const hedgeStatus = this.hedge.getStatus();
|
|
8713
8807
|
const hedgeInfo = hedgeStatus && hedgeStatus.positions.length > 0 ? ` hedge=$${hedgeResult.unrealizedPnl.toFixed(2)}unr/$${hedgeStatus.totalRealizedPnl.toFixed(2)}real` : "";
|
|
8714
|
-
console.error(
|
|
8808
|
+
console.error(chalk16.cyan(
|
|
8715
8809
|
` [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)" : ""}`
|
|
8716
8810
|
));
|
|
8717
8811
|
}
|
|
@@ -9462,10 +9556,10 @@ async function runSweep(opts) {
|
|
|
9462
9556
|
var DEFAULT_PAUSE_REASON = "Manually paused by operator";
|
|
9463
9557
|
var DEFAULT_FLATTEN_REASON = "Manually flattened by operator";
|
|
9464
9558
|
var CONFIRM_TIMEOUT_SECONDS = 60;
|
|
9465
|
-
var DIM5 =
|
|
9466
|
-
var G5 =
|
|
9467
|
-
var BOLD5 =
|
|
9468
|
-
var W5 =
|
|
9559
|
+
var DIM5 = chalk17.gray;
|
|
9560
|
+
var G5 = chalk17.green;
|
|
9561
|
+
var BOLD5 = chalk17.white.bold;
|
|
9562
|
+
var W5 = chalk17.white;
|
|
9469
9563
|
var SEP5 = () => console.log(DIM5("\u2500".repeat(60)));
|
|
9470
9564
|
function printSummary(r) {
|
|
9471
9565
|
const dd = r.drawdown;
|
|
@@ -9499,7 +9593,7 @@ function printSummary(r) {
|
|
|
9499
9593
|
console.log();
|
|
9500
9594
|
console.log(BOLD5(" Per token:"));
|
|
9501
9595
|
for (const t of r.perToken) {
|
|
9502
|
-
const tokPnl = t.pnlUsd >= 0 ? G5(`+$${t.pnlUsd.toFixed(2)}`) :
|
|
9596
|
+
const tokPnl = t.pnlUsd >= 0 ? G5(`+$${t.pnlUsd.toFixed(2)}`) : chalk17.red(`-$${Math.abs(t.pnlUsd).toFixed(2)}`);
|
|
9503
9597
|
console.log(W5(` ${t.token.padEnd(10)} $${t.allocation.initial.toFixed(0).padStart(6)} \u2192 $${t.allocation.final.toFixed(0).padStart(6)} ${tokPnl} RTs=${t.roundTrips} fills=${t.fills}`));
|
|
9504
9598
|
}
|
|
9505
9599
|
if (r.liquidations.events.length > 0) {
|
|
@@ -9511,7 +9605,7 @@ function printSummary(r) {
|
|
|
9511
9605
|
}
|
|
9512
9606
|
if (r.liquidations.haltedAt !== null) {
|
|
9513
9607
|
const haltDate = new Date(r.liquidations.haltedAt).toISOString().slice(0, 10);
|
|
9514
|
-
console.log(
|
|
9608
|
+
console.log(chalk17.red.bold(` RUN HALTED on ${haltDate} \u2014 all tokens liquidated.`));
|
|
9515
9609
|
}
|
|
9516
9610
|
}
|
|
9517
9611
|
console.log();
|
|
@@ -9536,7 +9630,7 @@ function printSweepSummary(s) {
|
|
|
9536
9630
|
for (let i = 0; i < s.runs.length; i++) {
|
|
9537
9631
|
const r = s.runs[i];
|
|
9538
9632
|
const rank = String(i + 1).padStart(4);
|
|
9539
|
-
const surv = r.survived ? G5(" \u2713 ") :
|
|
9633
|
+
const surv = r.survived ? G5(" \u2713 ") : chalk17.red(" \u2717 ");
|
|
9540
9634
|
const lev = String(r.config.leverage ?? "").padStart(3);
|
|
9541
9635
|
const lvls = String(r.config.levelsPerSide ?? "").padStart(6);
|
|
9542
9636
|
const atr = String(r.config.atrMultiplier ?? "").padStart(4);
|
|
@@ -9546,7 +9640,7 @@ function printSweepSummary(s) {
|
|
|
9546
9640
|
const netPct = `${(r.capital.pnlPct * 100).toFixed(1)}%`.padStart(8);
|
|
9547
9641
|
const ddPct = `${(r.drawdown.maxPct * 100).toFixed(1)}%`.padStart(8);
|
|
9548
9642
|
const ra = r.riskAdjusted.toFixed(2).padStart(8);
|
|
9549
|
-
const pnlColor = r.capital.pnlUsd >= 0 ? G5 :
|
|
9643
|
+
const pnlColor = r.capital.pnlUsd >= 0 ? G5 : chalk17.red;
|
|
9550
9644
|
console.log(` ${rank} ${surv} ${lev} ${lvls} ${atr} ${drift} ${rts} ${pnlColor(netPnl)} ${pnlColor(netPct)} ${ddPct} ${ra}`);
|
|
9551
9645
|
}
|
|
9552
9646
|
console.log(DIM5(" " + "\u2500".repeat(102)));
|
|
@@ -9661,7 +9755,7 @@ function registerGridCommand(program2) {
|
|
|
9661
9755
|
console.log(G5.bold(" Grid Portfolio"));
|
|
9662
9756
|
SEP5();
|
|
9663
9757
|
console.log(W5(` Allocation: $${state.totalAllocation.toLocaleString()}`));
|
|
9664
|
-
console.log(W5(` Status: ${state.paused ?
|
|
9758
|
+
console.log(W5(` Status: ${state.paused ? chalk17.red("PAUSED \u2014 " + state.pauseReason) : G5("Active")}`));
|
|
9665
9759
|
console.log(W5(` Initialized: ${new Date(state.initializedAt).toLocaleString()}`));
|
|
9666
9760
|
SEP5();
|
|
9667
9761
|
console.log();
|
|
@@ -9674,13 +9768,13 @@ function registerGridCommand(program2) {
|
|
|
9674
9768
|
const pnl = `$${g.stats.totalPnlUsd.toFixed(2)}`.padStart(10);
|
|
9675
9769
|
const todayPnl = `$${g.stats.todayPnlUsd.toFixed(2)}`.padStart(10);
|
|
9676
9770
|
const fills = String(g.stats.totalFills).padStart(6);
|
|
9677
|
-
const pnlColor = g.stats.totalPnlUsd >= 0 ? G5 :
|
|
9678
|
-
const todayColor = g.stats.todayPnlUsd >= 0 ? G5 :
|
|
9771
|
+
const pnlColor = g.stats.totalPnlUsd >= 0 ? G5 : chalk17.red;
|
|
9772
|
+
const todayColor = g.stats.todayPnlUsd >= 0 ? G5 : chalk17.red;
|
|
9679
9773
|
console.log(` ${W5(name)} ${alloc} ${rts} ${pnlColor(pnl)} ${todayColor(todayPnl)} ${fills}`);
|
|
9680
9774
|
}
|
|
9681
9775
|
console.log(DIM5(" " + "\u2500".repeat(72)));
|
|
9682
|
-
const totalPnlColor = agg.totalPnlUsd >= 0 ? G5 :
|
|
9683
|
-
const todayTotalColor = agg.todayPnlUsd >= 0 ? G5 :
|
|
9776
|
+
const totalPnlColor = agg.totalPnlUsd >= 0 ? G5 : chalk17.red;
|
|
9777
|
+
const todayTotalColor = agg.todayPnlUsd >= 0 ? G5 : chalk17.red;
|
|
9684
9778
|
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)}`);
|
|
9685
9779
|
console.log();
|
|
9686
9780
|
});
|
|
@@ -9786,7 +9880,7 @@ function registerGridCommand(program2) {
|
|
|
9786
9880
|
state.pauseReason = opts.reason;
|
|
9787
9881
|
await portfolio.save(state);
|
|
9788
9882
|
console.log();
|
|
9789
|
-
console.log(
|
|
9883
|
+
console.log(chalk17.yellow(` Grid paused. Reason: ${state.pauseReason}`));
|
|
9790
9884
|
console.log(DIM5(" Next manager.tick will be a no-op until you run `sherwood grid resume`."));
|
|
9791
9885
|
console.log();
|
|
9792
9886
|
});
|
|
@@ -9810,7 +9904,7 @@ function registerGridCommand(program2) {
|
|
|
9810
9904
|
await portfolio.save(state);
|
|
9811
9905
|
console.log();
|
|
9812
9906
|
if (wasCircuitBroken) {
|
|
9813
|
-
console.log(
|
|
9907
|
+
console.log(chalk17.yellow(` Hard circuit breaker cleared.`));
|
|
9814
9908
|
}
|
|
9815
9909
|
console.log(G5(` Grid resumed. (was paused: ${wasReason || "<unknown>"})`));
|
|
9816
9910
|
console.log();
|
|
@@ -9846,7 +9940,7 @@ function registerGridCommand(program2) {
|
|
|
9846
9940
|
console.log(W5(` Will pause: yes \u2014 reason: "${opts.reason}"`));
|
|
9847
9941
|
SEP5();
|
|
9848
9942
|
if (opts.dryRun) {
|
|
9849
|
-
console.log(
|
|
9943
|
+
console.log(chalk17.yellow(" --dry-run set \u2014 no state or chain mutation."));
|
|
9850
9944
|
console.log();
|
|
9851
9945
|
return;
|
|
9852
9946
|
}
|
|
@@ -9859,7 +9953,7 @@ function registerGridCommand(program2) {
|
|
|
9859
9953
|
const timer = setTimeout(() => ac.abort(), CONFIRM_TIMEOUT_SECONDS * 1e3);
|
|
9860
9954
|
let answer;
|
|
9861
9955
|
try {
|
|
9862
|
-
answer = (await rl.question(
|
|
9956
|
+
answer = (await rl.question(chalk17.yellow(" Proceed with flatten? [y/N] "), { signal: ac.signal })).trim().toLowerCase();
|
|
9863
9957
|
} catch (e) {
|
|
9864
9958
|
if (e.name === "AbortError") {
|
|
9865
9959
|
console.log(DIM5(`
|
|
@@ -9889,7 +9983,7 @@ function registerGridCommand(program2) {
|
|
|
9889
9983
|
console.log(DIM5(` Skipped (no tracked CLOIDs): ${res.skipped.join(", ")}`));
|
|
9890
9984
|
}
|
|
9891
9985
|
if (res.errors.length > 0) {
|
|
9892
|
-
for (const err of res.errors) console.log(
|
|
9986
|
+
for (const err of res.errors) console.log(chalk17.red(` Cancel error: ${err}`));
|
|
9893
9987
|
}
|
|
9894
9988
|
}
|
|
9895
9989
|
if (openHedges.length > 0) {
|
|
@@ -9907,7 +10001,7 @@ function registerGridCommand(program2) {
|
|
|
9907
10001
|
}
|
|
9908
10002
|
}
|
|
9909
10003
|
if (hres.skipped.length > 0) {
|
|
9910
|
-
console.log(
|
|
10004
|
+
console.log(chalk17.yellow(` Hedge skipped (no mark price): ${hres.skipped.join(", ")}`));
|
|
9911
10005
|
}
|
|
9912
10006
|
console.log(W5(` Total hedge realized PnL: $${hres.totalRealizedPnl.toFixed(2)}`));
|
|
9913
10007
|
} else {
|
|
@@ -9917,7 +10011,7 @@ function registerGridCommand(program2) {
|
|
|
9917
10011
|
state.paused = true;
|
|
9918
10012
|
state.pauseReason = opts.reason;
|
|
9919
10013
|
await portfolio.save(state);
|
|
9920
|
-
console.log(
|
|
10014
|
+
console.log(chalk17.yellow(` Grid paused. Reason: ${state.pauseReason}`));
|
|
9921
10015
|
} else {
|
|
9922
10016
|
console.log(DIM5(` Grid was already paused: ${state.pauseReason}`));
|
|
9923
10017
|
}
|
|
@@ -9933,22 +10027,22 @@ try {
|
|
|
9933
10027
|
} catch {
|
|
9934
10028
|
}
|
|
9935
10029
|
var require2 = createRequire(import.meta.url);
|
|
9936
|
-
var CLI_VERSION = "0.83.
|
|
10030
|
+
var CLI_VERSION = "0.83.1";
|
|
9937
10031
|
async function loadXmtp() {
|
|
9938
|
-
return import("./xmtp-
|
|
10032
|
+
return import("./xmtp-2U7CSBXM.js");
|
|
9939
10033
|
}
|
|
9940
10034
|
async function loadCron() {
|
|
9941
10035
|
return import("./cron-E3UAZF5B.js");
|
|
9942
10036
|
}
|
|
9943
|
-
var G6 =
|
|
9944
|
-
var W6 =
|
|
9945
|
-
var DIM6 =
|
|
9946
|
-
var BOLD6 =
|
|
9947
|
-
var LABEL5 =
|
|
10037
|
+
var G6 = chalk18.green;
|
|
10038
|
+
var W6 = chalk18.white;
|
|
10039
|
+
var DIM6 = chalk18.gray;
|
|
10040
|
+
var BOLD6 = chalk18.white.bold;
|
|
10041
|
+
var LABEL5 = chalk18.green.bold;
|
|
9948
10042
|
var SEP6 = () => console.log(DIM6("\u2500".repeat(60)));
|
|
9949
10043
|
function validateAddress(value, name) {
|
|
9950
|
-
if (!
|
|
9951
|
-
console.error(
|
|
10044
|
+
if (!isAddress12(value)) {
|
|
10045
|
+
console.error(chalk18.red(`Invalid ${name} address: ${value}`));
|
|
9952
10046
|
process.exit(1);
|
|
9953
10047
|
}
|
|
9954
10048
|
return value;
|
|
@@ -10027,13 +10121,13 @@ fund.command("create").description("Create a new fund via the factory (interacti
|
|
|
10027
10121
|
asset = opts.asset;
|
|
10028
10122
|
} else {
|
|
10029
10123
|
const supported = Object.keys(ASSET_SYMBOLS).join(", ");
|
|
10030
|
-
console.error(
|
|
10124
|
+
console.error(chalk18.red(` Unknown asset "${opts.asset}". Use a symbol (${supported}) or a 0x address.`));
|
|
10031
10125
|
process.exit(1);
|
|
10032
10126
|
}
|
|
10033
10127
|
} else if (nonInteractive) {
|
|
10034
10128
|
const preferred = ["USDC", "USDG", "WETH"].find((s) => ASSET_SYMBOLS[s]);
|
|
10035
10129
|
if (!preferred) {
|
|
10036
|
-
console.error(
|
|
10130
|
+
console.error(chalk18.red(" No known vault asset on this chain. Pass --asset <0xaddress>."));
|
|
10037
10131
|
process.exit(1);
|
|
10038
10132
|
}
|
|
10039
10133
|
asset = ASSET_SYMBOLS[preferred];
|
|
@@ -10049,7 +10143,7 @@ fund.command("create").description("Create a new fund via the factory (interacti
|
|
|
10049
10143
|
description: DESCRIPTIONS[s]
|
|
10050
10144
|
}));
|
|
10051
10145
|
if (choices.length === 0) {
|
|
10052
|
-
console.error(
|
|
10146
|
+
console.error(chalk18.red(" No known vault asset on this chain. Pass --asset <0xaddress>."));
|
|
10053
10147
|
process.exit(1);
|
|
10054
10148
|
}
|
|
10055
10149
|
const assetChoice = await select({
|
|
@@ -10073,7 +10167,7 @@ fund.command("create").description("Create a new fund via the factory (interacti
|
|
|
10073
10167
|
console.log(W6(` Agent ID: #${agentIdStr}`));
|
|
10074
10168
|
console.log(W6(` Asset: ${assetSymbol} (${asset.slice(0, 10)}...)`));
|
|
10075
10169
|
console.log(W6(` Share token: ${symbol}`));
|
|
10076
|
-
console.log(W6(` Open deposits: ${openDeposits ?
|
|
10170
|
+
console.log(W6(` Open deposits: ${openDeposits ? chalk18.yellow("yes (anyone can deposit)") : G6("no (whitelist)")}`));
|
|
10077
10171
|
SEP6();
|
|
10078
10172
|
if (!nonInteractive) {
|
|
10079
10173
|
const go = await confirm({ message: G6("Deploy fund?"), default: true });
|
|
@@ -10084,7 +10178,7 @@ fund.command("create").description("Create a new fund via the factory (interacti
|
|
|
10084
10178
|
}
|
|
10085
10179
|
let metadataURI = opts.metadataUri || "";
|
|
10086
10180
|
if (!metadataURI) {
|
|
10087
|
-
const spinner2 =
|
|
10181
|
+
const spinner2 = ora10({ text: W6("Uploading metadata to IPFS..."), color: "green" }).start();
|
|
10088
10182
|
try {
|
|
10089
10183
|
const metadata = {
|
|
10090
10184
|
schema: "sherwood/fund/v1",
|
|
@@ -10100,7 +10194,7 @@ fund.command("create").description("Create a new fund via the factory (interacti
|
|
|
10100
10194
|
metadataURI = await uploadMetadata(metadata);
|
|
10101
10195
|
spinner2.succeed(G6(`Metadata pinned: ${DIM6(metadataURI)}`));
|
|
10102
10196
|
} catch (err) {
|
|
10103
|
-
spinner2.warn(
|
|
10197
|
+
spinner2.warn(chalk18.yellow(`IPFS upload failed \u2014 using inline metadata`));
|
|
10104
10198
|
const json = JSON.stringify({ name, description, subdomain, asset: assetSymbol, openDeposits, createdBy: "@sherwoodagent/cli" });
|
|
10105
10199
|
metadataURI = `data:application/json;base64,${Buffer.from(json).toString("base64")}`;
|
|
10106
10200
|
}
|
|
@@ -10127,15 +10221,15 @@ fund.command("create").description("Create a new fund via the factory (interacti
|
|
|
10127
10221
|
const existingInfo = await getFund(existingId);
|
|
10128
10222
|
const callerAddress = getAccount().address.toLowerCase();
|
|
10129
10223
|
if (existingInfo.creator.toLowerCase() !== callerAddress) {
|
|
10130
|
-
console.error(
|
|
10224
|
+
console.error(chalk18.red(`
|
|
10131
10225
|
\u2716 Subdomain '${subdomain}' is already taken by another fund.
|
|
10132
10226
|
`));
|
|
10133
10227
|
process.exit(1);
|
|
10134
10228
|
}
|
|
10135
|
-
console.log(
|
|
10229
|
+
console.log(chalk18.yellow(`
|
|
10136
10230
|
\u26A0 Fund '${subdomain}' already exists \u2014 resuming setup...
|
|
10137
10231
|
`));
|
|
10138
|
-
const spinner2 =
|
|
10232
|
+
const spinner2 = ora10({ text: W6("Resuming setup..."), color: "green" }).start();
|
|
10139
10233
|
const chainId2 = getChain().id;
|
|
10140
10234
|
setChainContract(chainId2, "vault", existingInfo.vault);
|
|
10141
10235
|
addFund(chainId2, { subdomain, vault: existingInfo.vault, role: "creator" });
|
|
@@ -10163,11 +10257,11 @@ fund.command("create").description("Create a new fund via the factory (interacti
|
|
|
10163
10257
|
try {
|
|
10164
10258
|
await setTextRecord(subdomain, "xmtpGroupId", groupId, existingInfo.vault);
|
|
10165
10259
|
} catch {
|
|
10166
|
-
console.log(
|
|
10260
|
+
console.log(chalk18.dim(" ENS text record skipped (metadata fallback active)"));
|
|
10167
10261
|
}
|
|
10168
10262
|
}
|
|
10169
10263
|
} catch {
|
|
10170
|
-
console.warn(
|
|
10264
|
+
console.warn(chalk18.yellow("\n \u26A0 Could not set up XMTP chat group"));
|
|
10171
10265
|
}
|
|
10172
10266
|
try {
|
|
10173
10267
|
const cron = await loadCron();
|
|
@@ -10189,7 +10283,7 @@ fund.command("create").description("Create a new fund via the factory (interacti
|
|
|
10189
10283
|
console.log();
|
|
10190
10284
|
return;
|
|
10191
10285
|
}
|
|
10192
|
-
const spinner =
|
|
10286
|
+
const spinner = ora10({ text: W6("Deploying vault via factory..."), color: "green" }).start();
|
|
10193
10287
|
const result = await createFund({
|
|
10194
10288
|
creatorAgentId: BigInt(agentIdStr),
|
|
10195
10289
|
metadataURI,
|
|
@@ -10214,7 +10308,7 @@ fund.command("create").description("Create a new fund via the factory (interacti
|
|
|
10214
10308
|
// agentAddress = creator EOA (direct execution)
|
|
10215
10309
|
);
|
|
10216
10310
|
} catch (regErr) {
|
|
10217
|
-
console.warn(
|
|
10311
|
+
console.warn(chalk18.yellow("\n \u26A0 Could not auto-register creator as agent \u2014 register manually with `fund add`"));
|
|
10218
10312
|
}
|
|
10219
10313
|
spinner.text = W6("Setting up chat...");
|
|
10220
10314
|
try {
|
|
@@ -10225,11 +10319,11 @@ fund.command("create").description("Create a new fund via the factory (interacti
|
|
|
10225
10319
|
try {
|
|
10226
10320
|
await setTextRecord(subdomain, "xmtpGroupId", groupId, result.vault);
|
|
10227
10321
|
} catch {
|
|
10228
|
-
console.log(
|
|
10322
|
+
console.log(chalk18.dim(" ENS text record skipped (metadata fallback active)"));
|
|
10229
10323
|
}
|
|
10230
10324
|
} catch {
|
|
10231
|
-
console.warn(
|
|
10232
|
-
console.warn(
|
|
10325
|
+
console.warn(chalk18.yellow("\n \u26A0 Could not create XMTP chat group"));
|
|
10326
|
+
console.warn(chalk18.dim(` Recover later with: sherwood chat ${subdomain} init`));
|
|
10233
10327
|
}
|
|
10234
10328
|
try {
|
|
10235
10329
|
const cron = await loadCron();
|
|
@@ -10257,13 +10351,13 @@ fund.command("create").description("Create a new fund via the factory (interacti
|
|
|
10257
10351
|
console.log(G6(" \u2713 Fund saved to ~/.sherwood/config.json (set as primary)"));
|
|
10258
10352
|
console.log();
|
|
10259
10353
|
} catch (err) {
|
|
10260
|
-
console.error(
|
|
10354
|
+
console.error(chalk18.red(`
|
|
10261
10355
|
\u2716 ${formatContractError(err)}`));
|
|
10262
10356
|
process.exit(1);
|
|
10263
10357
|
}
|
|
10264
10358
|
});
|
|
10265
10359
|
fund.command("list").description("List active funds (queries subgraph, falls back to on-chain)").option("--creator <address>", "Filter by creator address").option("--all-chains", "Aggregate active funds across all supported chains (on-chain only)").action(async (opts) => {
|
|
10266
|
-
const spinner =
|
|
10360
|
+
const spinner = ora10("Loading funds...").start();
|
|
10267
10361
|
try {
|
|
10268
10362
|
let funds = [];
|
|
10269
10363
|
if (opts.allChains) {
|
|
@@ -10306,43 +10400,43 @@ fund.command("list").description("List active funds (queries subgraph, falls bac
|
|
|
10306
10400
|
}
|
|
10307
10401
|
spinner.stop();
|
|
10308
10402
|
if (funds.length === 0) {
|
|
10309
|
-
console.log(
|
|
10403
|
+
console.log(chalk18.dim("No active funds found."));
|
|
10310
10404
|
return;
|
|
10311
10405
|
}
|
|
10312
10406
|
console.log();
|
|
10313
|
-
console.log(
|
|
10407
|
+
console.log(chalk18.bold(`Active Funds (${funds.length})`));
|
|
10314
10408
|
if (!process.env.SUBGRAPH_URL && !opts.allChains) {
|
|
10315
|
-
console.log(
|
|
10409
|
+
console.log(chalk18.dim(" (Set SUBGRAPH_URL for faster indexed queries)"));
|
|
10316
10410
|
}
|
|
10317
10411
|
if (opts.allChains) {
|
|
10318
|
-
console.log(
|
|
10412
|
+
console.log(chalk18.dim(" (Aggregated across chains via on-chain factory calls; SUBGRAPH_URL is single-chain only)"));
|
|
10319
10413
|
}
|
|
10320
|
-
console.log(
|
|
10414
|
+
console.log(chalk18.dim("\u2500".repeat(70)));
|
|
10321
10415
|
for (const s of funds) {
|
|
10322
10416
|
const ts = typeof s.createdAt === "string" ? Number(s.createdAt) : Number(s.createdAt);
|
|
10323
10417
|
const date = new Date(ts * 1e3).toLocaleDateString();
|
|
10324
10418
|
const ensName = s.subdomain ? `${s.subdomain}.sherwoodagent.eth` : "";
|
|
10325
|
-
const chainTag = s.chain ?
|
|
10326
|
-
console.log(` ${chainTag}#${s.id} ${
|
|
10327
|
-
if (ensName) console.log(` Vault: ${
|
|
10419
|
+
const chainTag = s.chain ? chalk18.dim(`[${s.chain}] `) : "";
|
|
10420
|
+
console.log(` ${chainTag}#${s.id} ${chalk18.bold(ensName || String(s.vault))}`);
|
|
10421
|
+
if (ensName) console.log(` Vault: ${chalk18.cyan(String(s.vault))}`);
|
|
10328
10422
|
console.log(` Creator: ${s.creator}`);
|
|
10329
10423
|
console.log(` Created: ${date}`);
|
|
10330
10424
|
if (s.totalDeposits) {
|
|
10331
10425
|
console.log(` Deposits: ${s.totalDeposits} USDC`);
|
|
10332
10426
|
}
|
|
10333
10427
|
if (s.metadataURI) {
|
|
10334
|
-
console.log(` Metadata: ${
|
|
10428
|
+
console.log(` Metadata: ${chalk18.dim(s.metadataURI)}`);
|
|
10335
10429
|
}
|
|
10336
10430
|
console.log();
|
|
10337
10431
|
}
|
|
10338
10432
|
} catch (err) {
|
|
10339
10433
|
spinner.fail("Failed to load funds");
|
|
10340
|
-
console.error(
|
|
10434
|
+
console.error(chalk18.red(formatContractError(err)));
|
|
10341
10435
|
process.exit(1);
|
|
10342
10436
|
}
|
|
10343
10437
|
});
|
|
10344
10438
|
fund.command("info").description("Display fund details by ID or subdomain").argument("<id>", "Fund ID or subdomain name").action(async (idStr) => {
|
|
10345
|
-
const spinner =
|
|
10439
|
+
const spinner = ora10("Loading fund info...").start();
|
|
10346
10440
|
try {
|
|
10347
10441
|
let id;
|
|
10348
10442
|
let crossChainHint = null;
|
|
@@ -10377,38 +10471,38 @@ fund.command("info").description("Display fund details by ID or subdomain").argu
|
|
|
10377
10471
|
spinner.stop();
|
|
10378
10472
|
if (crossChainHint) {
|
|
10379
10473
|
const others = crossChainHint.others.length > 0 ? ` (also on: ${crossChainHint.others.join(", ")})` : "";
|
|
10380
|
-
console.log(
|
|
10381
|
-
console.log(
|
|
10474
|
+
console.log(chalk18.yellow(` Found on chain: ${chalk18.bold(crossChainHint.network)}${others}`));
|
|
10475
|
+
console.log(chalk18.dim(` Re-run with --chain ${crossChainHint.network} to interact with this fund.`));
|
|
10382
10476
|
console.log();
|
|
10383
10477
|
}
|
|
10384
10478
|
if (!info.vault || info.vault === "0x0000000000000000000000000000000000000000") {
|
|
10385
|
-
console.log(
|
|
10479
|
+
console.log(chalk18.red(`Fund #${id} not found.`));
|
|
10386
10480
|
process.exit(1);
|
|
10387
10481
|
}
|
|
10388
10482
|
const date = new Date(Number(info.createdAt) * 1e3).toLocaleDateString();
|
|
10389
10483
|
console.log();
|
|
10390
|
-
console.log(
|
|
10391
|
-
console.log(
|
|
10484
|
+
console.log(chalk18.bold(`Fund #${info.id}`));
|
|
10485
|
+
console.log(chalk18.dim("\u2500".repeat(40)));
|
|
10392
10486
|
if (info.subdomain) {
|
|
10393
|
-
console.log(` ENS: ${
|
|
10487
|
+
console.log(` ENS: ${chalk18.bold(`${info.subdomain}.sherwoodagent.eth`)}`);
|
|
10394
10488
|
}
|
|
10395
|
-
console.log(` Vault: ${
|
|
10489
|
+
console.log(` Vault: ${chalk18.cyan(info.vault)}`);
|
|
10396
10490
|
console.log(` Creator: ${info.creator}`);
|
|
10397
10491
|
console.log(` Created: ${date}`);
|
|
10398
|
-
console.log(` Active: ${info.active ?
|
|
10492
|
+
console.log(` Active: ${info.active ? chalk18.green("yes") : chalk18.red("no")}`);
|
|
10399
10493
|
if (info.metadataURI) {
|
|
10400
|
-
console.log(` Metadata: ${
|
|
10494
|
+
console.log(` Metadata: ${chalk18.dim(info.metadataURI)}`);
|
|
10401
10495
|
}
|
|
10402
10496
|
if (info.subdomain) {
|
|
10403
10497
|
const xmtpGroupId = getCachedGroupId(info.subdomain);
|
|
10404
10498
|
if (xmtpGroupId) {
|
|
10405
|
-
console.log(` XMTP Group: ${
|
|
10499
|
+
console.log(` XMTP Group: ${chalk18.cyan(xmtpGroupId)}`);
|
|
10406
10500
|
}
|
|
10407
10501
|
}
|
|
10408
10502
|
setVaultAddress(info.vault);
|
|
10409
10503
|
const vaultInfo = crossChainHint ? await withNetwork(crossChainHint.network, () => getVaultInfo()) : await getVaultInfo();
|
|
10410
10504
|
console.log();
|
|
10411
|
-
console.log(
|
|
10505
|
+
console.log(chalk18.bold(" Vault Stats"));
|
|
10412
10506
|
console.log(` Total Assets: ${vaultInfo.totalAssets}`);
|
|
10413
10507
|
console.log(` Agent Count: ${vaultInfo.agentCount}`);
|
|
10414
10508
|
console.log(` Redemptions Locked: ${vaultInfo.redemptionsLocked}`);
|
|
@@ -10416,12 +10510,12 @@ fund.command("info").description("Display fund details by ID or subdomain").argu
|
|
|
10416
10510
|
console.log();
|
|
10417
10511
|
} catch (err) {
|
|
10418
10512
|
spinner.fail("Failed to load fund info");
|
|
10419
|
-
console.error(
|
|
10513
|
+
console.error(chalk18.red(formatContractError(err)));
|
|
10420
10514
|
process.exit(1);
|
|
10421
10515
|
}
|
|
10422
10516
|
});
|
|
10423
10517
|
fund.command("update-metadata").description("Update fund metadata (creator only)").requiredOption("--id <id>", "Fund ID").option("--name <name>", "Fund name").option("--description <text>", "Short description").option("--uri <uri>", "Direct metadata URI (skips IPFS upload)").action(async (opts) => {
|
|
10424
|
-
const spinner =
|
|
10518
|
+
const spinner = ora10({ text: W6("Loading fund..."), color: "green" }).start();
|
|
10425
10519
|
try {
|
|
10426
10520
|
const fundId = BigInt(opts.id);
|
|
10427
10521
|
let metadataURI = opts.uri;
|
|
@@ -10437,7 +10531,7 @@ fund.command("update-metadata").description("Update fund metadata (creator only)
|
|
|
10437
10531
|
current = await fetchMetadata(info.metadataURI);
|
|
10438
10532
|
} catch (fetchErr) {
|
|
10439
10533
|
console.warn(
|
|
10440
|
-
|
|
10534
|
+
chalk18.yellow(
|
|
10441
10535
|
" \u26A0 Could not fetch current metadata \u2014 rebuilding from flags (unknown fields like xmtpGroupId may be dropped)"
|
|
10442
10536
|
)
|
|
10443
10537
|
);
|
|
@@ -10459,7 +10553,7 @@ fund.command("update-metadata").description("Update fund metadata (creator only)
|
|
|
10459
10553
|
console.log(DIM6(` ${getExplorerUrl(hash)}`));
|
|
10460
10554
|
} catch (err) {
|
|
10461
10555
|
spinner.fail("Metadata update failed");
|
|
10462
|
-
console.error(
|
|
10556
|
+
console.error(chalk18.red(formatContractError(err)));
|
|
10463
10557
|
process.exit(1);
|
|
10464
10558
|
}
|
|
10465
10559
|
});
|
|
@@ -10475,28 +10569,28 @@ fund.command("approve-depositor").description("Approve an address to deposit (ow
|
|
|
10475
10569
|
);
|
|
10476
10570
|
return;
|
|
10477
10571
|
}
|
|
10478
|
-
const spinner =
|
|
10572
|
+
const spinner = ora10("Approving depositor...").start();
|
|
10479
10573
|
try {
|
|
10480
10574
|
const hash = await approveDepositor(depositor);
|
|
10481
10575
|
spinner.succeed(`Depositor approved: ${hash}`);
|
|
10482
|
-
console.log(
|
|
10576
|
+
console.log(chalk18.dim(` ${getExplorerUrl(hash)}`));
|
|
10483
10577
|
} catch (err) {
|
|
10484
10578
|
spinner.fail("Approval failed");
|
|
10485
|
-
console.error(
|
|
10579
|
+
console.error(chalk18.red(formatContractError(err)));
|
|
10486
10580
|
process.exit(1);
|
|
10487
10581
|
}
|
|
10488
10582
|
});
|
|
10489
10583
|
fund.command("remove-depositor").description("Remove an address from the depositor whitelist (owner only)").option("--vault <address>", "Vault address (default: from config)").requiredOption("--depositor <address>", "Address to remove").action(async (opts) => {
|
|
10490
10584
|
resolveVault(opts);
|
|
10491
|
-
const spinner =
|
|
10585
|
+
const spinner = ora10("Removing depositor...").start();
|
|
10492
10586
|
try {
|
|
10493
10587
|
const depositor = validateAddress(opts.depositor, "depositor");
|
|
10494
10588
|
const hash = await removeDepositor(depositor);
|
|
10495
10589
|
spinner.succeed(`Depositor removed: ${hash}`);
|
|
10496
|
-
console.log(
|
|
10590
|
+
console.log(chalk18.dim(` ${getExplorerUrl(hash)}`));
|
|
10497
10591
|
} catch (err) {
|
|
10498
10592
|
spinner.fail("Removal failed");
|
|
10499
|
-
console.error(
|
|
10593
|
+
console.error(chalk18.red(formatContractError(err)));
|
|
10500
10594
|
process.exit(1);
|
|
10501
10595
|
}
|
|
10502
10596
|
});
|
|
@@ -10505,17 +10599,17 @@ fund.command("set-agent-fee").description("Set the vault's agent performance fee
|
|
|
10505
10599
|
`Agent fee in bps (e.g. 1000 = 10%; max ${MAX_AGENT_FEE_BPS} = ${MAX_AGENT_FEE_BPS / 100}%). The governor clamps the realized fee to its configured maxPerformanceFeeBps, which may be lower.`
|
|
10506
10600
|
).action(async (opts) => {
|
|
10507
10601
|
resolveVault(opts);
|
|
10508
|
-
const spinner =
|
|
10602
|
+
const spinner = ora10("Setting agent fee...").start();
|
|
10509
10603
|
try {
|
|
10510
10604
|
const bps = parseBigIntArg(opts.bps, "bps");
|
|
10511
10605
|
const hash = await setAgentFeeBps(bps);
|
|
10512
10606
|
spinner.succeed(
|
|
10513
10607
|
`Agent fee set to ${Number(bps) / 100}% (realized fee is clamped to the governor's maxPerformanceFeeBps): ${hash}`
|
|
10514
10608
|
);
|
|
10515
|
-
console.log(
|
|
10609
|
+
console.log(chalk18.dim(` ${getExplorerUrl(hash)}`));
|
|
10516
10610
|
} catch (err) {
|
|
10517
10611
|
spinner.fail("Failed to set agent fee");
|
|
10518
|
-
console.error(
|
|
10612
|
+
console.error(chalk18.red(formatContractError(err)));
|
|
10519
10613
|
process.exit(1);
|
|
10520
10614
|
}
|
|
10521
10615
|
});
|
|
@@ -10525,7 +10619,7 @@ fund.command("add").description("Register an agent on a fund vault (creator only
|
|
|
10525
10619
|
const agentWallet = validateAddress(opts.wallet, "wallet");
|
|
10526
10620
|
if (!opts.agentId) {
|
|
10527
10621
|
console.error(
|
|
10528
|
-
|
|
10622
|
+
chalk18.red(
|
|
10529
10623
|
"--calldata-only requires --agent-id (auto-lookup from wallet is skipped in calldata mode)"
|
|
10530
10624
|
)
|
|
10531
10625
|
);
|
|
@@ -10543,7 +10637,7 @@ fund.command("add").description("Register an agent on a fund vault (creator only
|
|
|
10543
10637
|
);
|
|
10544
10638
|
return;
|
|
10545
10639
|
}
|
|
10546
|
-
const spinner =
|
|
10640
|
+
const spinner = ora10("Verifying creator...").start();
|
|
10547
10641
|
try {
|
|
10548
10642
|
resolveVault(opts);
|
|
10549
10643
|
const vaultAddress = getVaultAddress();
|
|
@@ -10579,7 +10673,7 @@ fund.command("add").description("Register an agent on a fund vault (creator only
|
|
|
10579
10673
|
});
|
|
10580
10674
|
if (balance === 0n) {
|
|
10581
10675
|
spinner.fail("Agent wallet does not own an ERC-8004 identity NFT");
|
|
10582
|
-
console.error(
|
|
10676
|
+
console.error(chalk18.dim(" Mint one first: sherwood identity mint --name <name>"));
|
|
10583
10677
|
process.exit(1);
|
|
10584
10678
|
}
|
|
10585
10679
|
const tokenId = await client.readContract({
|
|
@@ -10595,13 +10689,13 @@ fund.command("add").description("Register an agent on a fund vault (creator only
|
|
|
10595
10689
|
args: [agentWallet, 0n]
|
|
10596
10690
|
});
|
|
10597
10691
|
agentId = tokenId;
|
|
10598
|
-
console.log(
|
|
10692
|
+
console.log(chalk18.dim(` Resolved agent ID: #${agentId}`));
|
|
10599
10693
|
}
|
|
10600
10694
|
}
|
|
10601
10695
|
spinner.text = "Registering agent...";
|
|
10602
10696
|
const hash = await registerAgent(agentId, agentWallet);
|
|
10603
10697
|
spinner.succeed(`Agent registered: ${hash}`);
|
|
10604
|
-
console.log(
|
|
10698
|
+
console.log(chalk18.dim(` ${getExplorerUrl(hash)}`));
|
|
10605
10699
|
try {
|
|
10606
10700
|
const xmtp = await loadXmtp();
|
|
10607
10701
|
const xmtpClient = await xmtp.getXmtpClient();
|
|
@@ -10613,14 +10707,14 @@ fund.command("add").description("Register an agent on a fund vault (creator only
|
|
|
10613
10707
|
fund: subdomain,
|
|
10614
10708
|
timestamp: Math.floor(Date.now() / 1e3)
|
|
10615
10709
|
});
|
|
10616
|
-
console.log(
|
|
10710
|
+
console.log(chalk18.dim(` Added to chat: ${subdomain}`));
|
|
10617
10711
|
} catch {
|
|
10618
|
-
console.warn(
|
|
10619
|
-
console.warn(
|
|
10712
|
+
console.warn(chalk18.yellow(" \u26A0 Could not add agent to chat group"));
|
|
10713
|
+
console.warn(chalk18.dim(` If no group exists, run: sherwood chat ${subdomain} init`));
|
|
10620
10714
|
}
|
|
10621
10715
|
} catch (err) {
|
|
10622
10716
|
spinner.fail("Registration failed");
|
|
10623
|
-
console.error(
|
|
10717
|
+
console.error(chalk18.red(formatContractError(err)));
|
|
10624
10718
|
process.exit(1);
|
|
10625
10719
|
}
|
|
10626
10720
|
});
|
|
@@ -10628,7 +10722,7 @@ fund.command("share").description("Print the shareable dashboard URL for your fu
|
|
|
10628
10722
|
try {
|
|
10629
10723
|
const subdomain = opts.subdomain || getPrimaryFund(getChain().id)?.subdomain;
|
|
10630
10724
|
if (!subdomain) {
|
|
10631
|
-
console.error(
|
|
10725
|
+
console.error(chalk18.red(" No fund configured. Pass --subdomain <name> or run 'fund create/join' first."));
|
|
10632
10726
|
process.exit(1);
|
|
10633
10727
|
}
|
|
10634
10728
|
const fundInfo = await resolveFund(subdomain);
|
|
@@ -10655,12 +10749,12 @@ fund.command("share").description("Print the shareable dashboard URL for your fu
|
|
|
10655
10749
|
}
|
|
10656
10750
|
console.log();
|
|
10657
10751
|
} catch (err) {
|
|
10658
|
-
console.error(
|
|
10752
|
+
console.error(chalk18.red(` \u2716 ${formatContractError(err)}`));
|
|
10659
10753
|
process.exit(1);
|
|
10660
10754
|
}
|
|
10661
10755
|
});
|
|
10662
10756
|
fund.command("join").description("Request to join a fund (creates an EAS attestation)").option("--subdomain <name>", "Fund subdomain to join").option("--ref <agentId>", "Referrer agent ID (from invite link)").option("--message <text>", "Message to the creator", "Requesting to join your fund").action(async (opts) => {
|
|
10663
|
-
const spinner =
|
|
10757
|
+
const spinner = ora10("Resolving fund...").start();
|
|
10664
10758
|
try {
|
|
10665
10759
|
const subdomain = opts.subdomain || getPrimaryFund(getChain().id)?.subdomain;
|
|
10666
10760
|
if (!subdomain) {
|
|
@@ -10680,14 +10774,14 @@ fund.command("join").description("Request to join a fund (creates an EAS attesta
|
|
|
10680
10774
|
const hits = await searchFundAcrossChains(subdomain);
|
|
10681
10775
|
spinner.fail(`Fund "${subdomain}" not found on ${getNetwork()}`);
|
|
10682
10776
|
if (hits.length === 0) {
|
|
10683
|
-
console.error(
|
|
10777
|
+
console.error(chalk18.red(` Not found on any supported chain.`));
|
|
10684
10778
|
} else if (hits.length === 1) {
|
|
10685
|
-
console.error(
|
|
10686
|
-
console.error(
|
|
10779
|
+
console.error(chalk18.yellow(` Found on chain: ${chalk18.bold(hits[0].network)}`));
|
|
10780
|
+
console.error(chalk18.dim(` Re-run: sherwood --chain ${hits[0].network} fund join --subdomain ${subdomain}`));
|
|
10687
10781
|
} else {
|
|
10688
10782
|
const chains = hits.map((h) => h.network).join(", ");
|
|
10689
|
-
console.error(
|
|
10690
|
-
console.error(
|
|
10783
|
+
console.error(chalk18.yellow(` Found on chains: ${chalk18.bold(chains)}`));
|
|
10784
|
+
console.error(chalk18.dim(` Specify which chain to join with --chain <network>.`));
|
|
10691
10785
|
}
|
|
10692
10786
|
process.exit(1);
|
|
10693
10787
|
}
|
|
@@ -10728,17 +10822,17 @@ fund.command("join").description("Request to join a fund (creates an EAS attesta
|
|
|
10728
10822
|
try {
|
|
10729
10823
|
const xmtp = await loadXmtp();
|
|
10730
10824
|
await xmtp.getXmtpClient();
|
|
10731
|
-
console.log(
|
|
10825
|
+
console.log(chalk18.dim(" XMTP identity ready"));
|
|
10732
10826
|
} catch {
|
|
10733
|
-
console.warn(
|
|
10827
|
+
console.warn(chalk18.yellow(" \u26A0 Could not initialize XMTP identity"));
|
|
10734
10828
|
}
|
|
10735
10829
|
try {
|
|
10736
10830
|
const cron = await loadCron();
|
|
10737
10831
|
const cronResult = cron.registerFundCrons(subdomain, isTestnet(), getNotifyTo());
|
|
10738
10832
|
if (cronResult.isOpenClaw && cronResult.registered) {
|
|
10739
|
-
console.log(
|
|
10833
|
+
console.log(chalk18.green(" \u2713 Participation crons registered"));
|
|
10740
10834
|
} else if (!cronResult.isOpenClaw) {
|
|
10741
|
-
console.log(
|
|
10835
|
+
console.log(chalk18.dim(" Tip: Set up a scheduled process to run `sherwood session check " + subdomain + "` periodically"));
|
|
10742
10836
|
}
|
|
10743
10837
|
} catch {
|
|
10744
10838
|
}
|
|
@@ -10751,14 +10845,14 @@ fund.command("join").description("Request to join a fund (creates an EAS attesta
|
|
|
10751
10845
|
);
|
|
10752
10846
|
if (existingRequest) {
|
|
10753
10847
|
spinner.succeed("You already have a pending join request for this fund");
|
|
10754
|
-
console.log(
|
|
10755
|
-
console.log(
|
|
10848
|
+
console.log(chalk18.dim(` Attestation: ${existingRequest.uid}`));
|
|
10849
|
+
console.log(chalk18.dim(` Submitted: ${new Date(existingRequest.time * 1e3).toLocaleString()}`));
|
|
10756
10850
|
try {
|
|
10757
10851
|
const xmtp = await loadXmtp();
|
|
10758
10852
|
await xmtp.getXmtpClient();
|
|
10759
|
-
console.log(
|
|
10853
|
+
console.log(chalk18.dim(" XMTP identity ready"));
|
|
10760
10854
|
} catch {
|
|
10761
|
-
console.warn(
|
|
10855
|
+
console.warn(chalk18.yellow(" \u26A0 Could not initialize XMTP identity"));
|
|
10762
10856
|
}
|
|
10763
10857
|
return;
|
|
10764
10858
|
}
|
|
@@ -10779,7 +10873,7 @@ fund.command("join").description("Request to join a fund (creates an EAS attesta
|
|
|
10779
10873
|
spinner.succeed("Join request created (XMTP identity ready)");
|
|
10780
10874
|
} catch {
|
|
10781
10875
|
spinner.succeed("Join request created");
|
|
10782
|
-
console.warn(
|
|
10876
|
+
console.warn(chalk18.yellow(" \u26A0 Could not initialize XMTP identity \u2014 creator may not be able to auto-add you to chat"));
|
|
10783
10877
|
}
|
|
10784
10878
|
addFund(getChain().id, { subdomain, vault: fund2.vault, role: "agent" });
|
|
10785
10879
|
setPrimaryFund(getChain().id, subdomain);
|
|
@@ -10809,12 +10903,12 @@ fund.command("join").description("Request to join a fund (creates an EAS attesta
|
|
|
10809
10903
|
console.log();
|
|
10810
10904
|
} catch (err) {
|
|
10811
10905
|
spinner.fail("Join request failed");
|
|
10812
|
-
console.error(
|
|
10906
|
+
console.error(chalk18.red(formatContractError(err)));
|
|
10813
10907
|
process.exit(1);
|
|
10814
10908
|
}
|
|
10815
10909
|
});
|
|
10816
10910
|
fund.command("requests").description("View pending join requests for a fund (creator only)").option("--subdomain <name>", "Fund subdomain").option("--vault <address>", "Vault address (default: from config)").action(async (opts) => {
|
|
10817
|
-
const spinner =
|
|
10911
|
+
const spinner = ora10("Loading join requests...").start();
|
|
10818
10912
|
try {
|
|
10819
10913
|
let creatorAddress;
|
|
10820
10914
|
let subdomain;
|
|
@@ -10871,11 +10965,11 @@ fund.command("requests").description("View pending join requests for a fund (cre
|
|
|
10871
10965
|
if (vid) {
|
|
10872
10966
|
const v = await verifyVidTag(vid, req.attester, ownerOfCached);
|
|
10873
10967
|
const verified = v.bindingOk && v.ownerOk;
|
|
10874
|
-
const badge = verified ? G6("\u2714 verified") :
|
|
10968
|
+
const badge = verified ? G6("\u2714 verified") : chalk18.red("\u2718 FAILED");
|
|
10875
10969
|
const detail = v.walletOnly ? `(wallet-only \u2014 no ERC-8004, wallet ${vid.wallet})` : `(agent #${vid.tokenId} on chain ${vid.issuingChainId}, wallet ${vid.wallet})`;
|
|
10876
10970
|
console.log(W6(` Virtuals: ${badge} ${DIM6(detail)}`));
|
|
10877
|
-
if (!v.bindingOk) console.log(
|
|
10878
|
-
else if (!v.ownerOk) console.log(
|
|
10971
|
+
if (!v.bindingOk) console.log(chalk18.red(` binding signature invalid \u2014 do not approve based on this identity`));
|
|
10972
|
+
else if (!v.ownerOk) console.log(chalk18.red(` NFT ownership check failed${v.owner ? ` (owner is ${v.owner})` : v.error ? ` (${v.error})` : ""}`));
|
|
10879
10973
|
}
|
|
10880
10974
|
console.log(DIM6(` Requested: ${date}`));
|
|
10881
10975
|
console.log(DIM6(` Attestation: ${req.uid}`));
|
|
@@ -10888,12 +10982,12 @@ fund.command("requests").description("View pending join requests for a fund (cre
|
|
|
10888
10982
|
console.log();
|
|
10889
10983
|
} catch (err) {
|
|
10890
10984
|
spinner.fail("Failed to load requests");
|
|
10891
|
-
console.error(
|
|
10985
|
+
console.error(chalk18.red(formatContractError(err)));
|
|
10892
10986
|
process.exit(1);
|
|
10893
10987
|
}
|
|
10894
10988
|
});
|
|
10895
10989
|
fund.command("approve").description("Approve an agent join request (registers agent + creates EAS approval)").option("--vault <address>", "Vault address (default: from config)").option("--subdomain <name>", "Fund subdomain (alternative to --vault)").requiredOption("--agent-id <id>", "Agent's ERC-8004 identity token ID").requiredOption("--wallet <address>", "Agent wallet address").action(async (opts) => {
|
|
10896
|
-
const spinner =
|
|
10990
|
+
const spinner = ora10("Verifying creator...").start();
|
|
10897
10991
|
try {
|
|
10898
10992
|
const approveSubdomain = opts.subdomain || getPrimaryFund(getChain().id)?.subdomain;
|
|
10899
10993
|
if (approveSubdomain && !opts.vault) {
|
|
@@ -10978,8 +11072,8 @@ fund.command("approve").description("Approve an agent join request (registers ag
|
|
|
10978
11072
|
});
|
|
10979
11073
|
console.log(DIM6(` Added to chat: ${subdomain}`));
|
|
10980
11074
|
} catch {
|
|
10981
|
-
console.warn(
|
|
10982
|
-
console.warn(
|
|
11075
|
+
console.warn(chalk18.yellow(" \u26A0 Could not add agent to chat group"));
|
|
11076
|
+
console.warn(chalk18.dim(` If no group exists, run: sherwood chat ${subdomain} init`));
|
|
10983
11077
|
}
|
|
10984
11078
|
spinner.succeed("Agent approved and registered");
|
|
10985
11079
|
console.log();
|
|
@@ -10992,29 +11086,29 @@ fund.command("approve").description("Approve an agent join request (registers ag
|
|
|
10992
11086
|
SEP6();
|
|
10993
11087
|
} catch (err) {
|
|
10994
11088
|
spinner.fail("Approval failed");
|
|
10995
|
-
console.error(
|
|
11089
|
+
console.error(chalk18.red(formatContractError(err)));
|
|
10996
11090
|
process.exit(1);
|
|
10997
11091
|
}
|
|
10998
11092
|
});
|
|
10999
11093
|
fund.command("reject").description("Reject a join request by revoking its attestation").requiredOption("--attestation <uid>", "Join request attestation UID to revoke").action(async (opts) => {
|
|
11000
|
-
const spinner =
|
|
11094
|
+
const spinner = ora10("Revoking attestation...").start();
|
|
11001
11095
|
try {
|
|
11002
11096
|
const hash = await revokeJoinRequest(opts.attestation);
|
|
11003
11097
|
spinner.succeed("Join request rejected");
|
|
11004
11098
|
console.log(DIM6(` ${getEasExplorerUrl(hash)}`));
|
|
11005
11099
|
} catch (err) {
|
|
11006
11100
|
spinner.fail("Rejection failed");
|
|
11007
|
-
console.error(
|
|
11101
|
+
console.error(chalk18.red(formatContractError(err)));
|
|
11008
11102
|
process.exit(1);
|
|
11009
11103
|
}
|
|
11010
11104
|
});
|
|
11011
11105
|
fund.command("leave").description("Leave a fund \u2014 removes participation crons and session state").option("--subdomain <name>", "Fund subdomain to leave").action(async (opts) => {
|
|
11012
11106
|
const subdomain = opts.subdomain || getPrimaryFund(getChain().id)?.subdomain;
|
|
11013
11107
|
if (!subdomain) {
|
|
11014
|
-
console.error(
|
|
11108
|
+
console.error(chalk18.red(" No fund configured. Pass --subdomain <name>."));
|
|
11015
11109
|
process.exit(1);
|
|
11016
11110
|
}
|
|
11017
|
-
const spinner =
|
|
11111
|
+
const spinner = ora10("Cleaning up...").start();
|
|
11018
11112
|
try {
|
|
11019
11113
|
let cronsRemoved = false;
|
|
11020
11114
|
try {
|
|
@@ -11031,13 +11125,13 @@ fund.command("leave").description("Leave a fund \u2014 removes participation cro
|
|
|
11031
11125
|
}
|
|
11032
11126
|
console.log(G6(" \u2713 Session state cleared"));
|
|
11033
11127
|
console.log();
|
|
11034
|
-
console.log(
|
|
11035
|
-
console.log(
|
|
11036
|
-
console.log(
|
|
11128
|
+
console.log(chalk18.dim(" Note: This does not remove you on-chain. To exit your position:"));
|
|
11129
|
+
console.log(chalk18.dim(" sherwood vault balance \u2014 check your LP share balance"));
|
|
11130
|
+
console.log(chalk18.dim(" Redeem shares via the vault contract or dashboard"));
|
|
11037
11131
|
console.log();
|
|
11038
11132
|
} catch (err) {
|
|
11039
11133
|
spinner.fail("Leave failed");
|
|
11040
|
-
console.error(
|
|
11134
|
+
console.error(chalk18.red(err instanceof Error ? err.message : String(err)));
|
|
11041
11135
|
process.exit(1);
|
|
11042
11136
|
}
|
|
11043
11137
|
});
|
|
@@ -11088,7 +11182,7 @@ fund.command("set-primary").description("Set the active fund for CLI commands (a
|
|
|
11088
11182
|
}
|
|
11089
11183
|
}
|
|
11090
11184
|
if (!target) {
|
|
11091
|
-
console.error(
|
|
11185
|
+
console.error(chalk18.red(" Could not resolve fund. Pass --subdomain or --vault."));
|
|
11092
11186
|
process.exit(1);
|
|
11093
11187
|
}
|
|
11094
11188
|
setPrimaryFund(chainId, target);
|
|
@@ -11096,7 +11190,7 @@ fund.command("set-primary").description("Set the active fund for CLI commands (a
|
|
|
11096
11190
|
\u2713 Primary fund set to ${target}.sherwoodagent.eth
|
|
11097
11191
|
`));
|
|
11098
11192
|
} catch (err) {
|
|
11099
|
-
console.error(
|
|
11193
|
+
console.error(chalk18.red(formatContractError(err)));
|
|
11100
11194
|
process.exit(1);
|
|
11101
11195
|
}
|
|
11102
11196
|
});
|
|
@@ -11104,11 +11198,11 @@ var vaultCmd = program.command("vault");
|
|
|
11104
11198
|
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)").option("--receiver <address>", "Depositor / share receiver (required with --calldata-only)").action(async (opts) => {
|
|
11105
11199
|
resolveVault(opts);
|
|
11106
11200
|
const decimals = await getAssetDecimals();
|
|
11107
|
-
const amount =
|
|
11201
|
+
const amount = parseUnits11(opts.amount, decimals);
|
|
11108
11202
|
if (isCalldataOnly()) {
|
|
11109
11203
|
if (!opts.receiver) {
|
|
11110
11204
|
console.error(
|
|
11111
|
-
|
|
11205
|
+
chalk18.red(
|
|
11112
11206
|
"--calldata-only requires --receiver <address> (the depositor / share receiver)"
|
|
11113
11207
|
)
|
|
11114
11208
|
);
|
|
@@ -11147,7 +11241,7 @@ vaultCmd.command("deposit").description("Deposit into a vault").option("--vault
|
|
|
11147
11241
|
)
|
|
11148
11242
|
);
|
|
11149
11243
|
} catch (err) {
|
|
11150
|
-
console.error(
|
|
11244
|
+
console.error(chalk18.red(err instanceof Error ? err.message : String(err)));
|
|
11151
11245
|
process.exit(1);
|
|
11152
11246
|
}
|
|
11153
11247
|
return;
|
|
@@ -11157,12 +11251,12 @@ vaultCmd.command("deposit").description("Deposit into a vault").option("--vault
|
|
|
11157
11251
|
await preflightDeposit(amount);
|
|
11158
11252
|
}
|
|
11159
11253
|
} catch (err) {
|
|
11160
|
-
console.error(
|
|
11254
|
+
console.error(chalk18.red(`
|
|
11161
11255
|
\u2716 ${err instanceof Error ? err.message : String(err)}
|
|
11162
11256
|
`));
|
|
11163
11257
|
process.exit(1);
|
|
11164
11258
|
}
|
|
11165
|
-
const spinner =
|
|
11259
|
+
const spinner = ora10(`Depositing ${opts.amount}...`).start();
|
|
11166
11260
|
try {
|
|
11167
11261
|
let hash;
|
|
11168
11262
|
if (opts.useEth) {
|
|
@@ -11172,22 +11266,22 @@ vaultCmd.command("deposit").description("Deposit into a vault").option("--vault
|
|
|
11172
11266
|
hash = await deposit(amount);
|
|
11173
11267
|
}
|
|
11174
11268
|
spinner.succeed(`Deposited: ${hash}`);
|
|
11175
|
-
console.log(
|
|
11269
|
+
console.log(chalk18.dim(` ${getExplorerUrl(hash)}`));
|
|
11176
11270
|
} catch (err) {
|
|
11177
11271
|
spinner.fail("Deposit failed");
|
|
11178
|
-
console.error(
|
|
11272
|
+
console.error(chalk18.red(formatContractError(err)));
|
|
11179
11273
|
process.exit(1);
|
|
11180
11274
|
}
|
|
11181
11275
|
});
|
|
11182
11276
|
vaultCmd.command("info").description("Display vault state").option("--vault <address>", "Vault address (default: from config)").action(async (opts) => {
|
|
11183
11277
|
resolveVault(opts);
|
|
11184
|
-
const spinner =
|
|
11278
|
+
const spinner = ora10("Loading vault info...").start();
|
|
11185
11279
|
try {
|
|
11186
11280
|
const info = await getVaultInfo();
|
|
11187
11281
|
spinner.stop();
|
|
11188
11282
|
console.log();
|
|
11189
|
-
console.log(
|
|
11190
|
-
console.log(
|
|
11283
|
+
console.log(chalk18.bold("Vault Info"));
|
|
11284
|
+
console.log(chalk18.dim("\u2500".repeat(40)));
|
|
11191
11285
|
console.log(` Address: ${info.address}`);
|
|
11192
11286
|
console.log(` Total Assets: ${info.totalAssets}`);
|
|
11193
11287
|
console.log(` Agent Count: ${info.agentCount}`);
|
|
@@ -11196,26 +11290,26 @@ vaultCmd.command("info").description("Display vault state").option("--vault <add
|
|
|
11196
11290
|
console.log();
|
|
11197
11291
|
} catch (err) {
|
|
11198
11292
|
spinner.fail("Failed to load vault info");
|
|
11199
|
-
console.error(
|
|
11293
|
+
console.error(chalk18.red(formatContractError(err)));
|
|
11200
11294
|
process.exit(1);
|
|
11201
11295
|
}
|
|
11202
11296
|
});
|
|
11203
11297
|
vaultCmd.command("balance").description("Show LP share balance and asset value").option("--vault <address>", "Vault address (default: from config)").option("--address <address>", "Address to check (default: your wallet)").action(async (opts) => {
|
|
11204
11298
|
resolveVault(opts);
|
|
11205
|
-
const spinner =
|
|
11299
|
+
const spinner = ora10("Loading balance...").start();
|
|
11206
11300
|
try {
|
|
11207
11301
|
const balance = await getBalance(opts.address);
|
|
11208
11302
|
spinner.stop();
|
|
11209
11303
|
console.log();
|
|
11210
|
-
console.log(
|
|
11211
|
-
console.log(
|
|
11304
|
+
console.log(chalk18.bold("LP Position"));
|
|
11305
|
+
console.log(chalk18.dim("\u2500".repeat(40)));
|
|
11212
11306
|
console.log(` Shares: ${balance.shares.toString()}`);
|
|
11213
11307
|
console.log(` Asset Value: ${balance.assetsValue}`);
|
|
11214
11308
|
console.log(` % of Vault: ${balance.percentOfVault}`);
|
|
11215
11309
|
console.log();
|
|
11216
11310
|
} catch (err) {
|
|
11217
11311
|
spinner.fail("Failed to load balance");
|
|
11218
|
-
console.error(
|
|
11312
|
+
console.error(chalk18.red(formatContractError(err)));
|
|
11219
11313
|
process.exit(1);
|
|
11220
11314
|
}
|
|
11221
11315
|
});
|
|
@@ -11226,7 +11320,7 @@ vaultCmd.command("redeem").description("Redeem vault shares for the underlying a
|
|
|
11226
11320
|
if (isCalldataOnly()) {
|
|
11227
11321
|
if (!opts.shares) {
|
|
11228
11322
|
console.error(
|
|
11229
|
-
|
|
11323
|
+
chalk18.red(
|
|
11230
11324
|
"--calldata-only requires --shares (auto-'all' needs your on-chain share balance)"
|
|
11231
11325
|
)
|
|
11232
11326
|
);
|
|
@@ -11234,7 +11328,7 @@ vaultCmd.command("redeem").description("Redeem vault shares for the underlying a
|
|
|
11234
11328
|
}
|
|
11235
11329
|
if (!opts.owner) {
|
|
11236
11330
|
console.error(
|
|
11237
|
-
|
|
11331
|
+
chalk18.red(
|
|
11238
11332
|
"--calldata-only requires --owner <address> (the share holder whose shares burn)"
|
|
11239
11333
|
)
|
|
11240
11334
|
);
|
|
@@ -11248,7 +11342,7 @@ vaultCmd.command("redeem").description("Redeem vault shares for the underlying a
|
|
|
11248
11342
|
vault: getVaultAddress(),
|
|
11249
11343
|
receiver,
|
|
11250
11344
|
owner,
|
|
11251
|
-
shares:
|
|
11345
|
+
shares: parseUnits11(opts.shares, shareDecimals)
|
|
11252
11346
|
},
|
|
11253
11347
|
getChain().id
|
|
11254
11348
|
)
|
|
@@ -11258,22 +11352,22 @@ vaultCmd.command("redeem").description("Redeem vault shares for the underlying a
|
|
|
11258
11352
|
let shares;
|
|
11259
11353
|
try {
|
|
11260
11354
|
if (opts.shares) {
|
|
11261
|
-
shares =
|
|
11355
|
+
shares = parseUnits11(opts.shares, shareDecimals);
|
|
11262
11356
|
} else {
|
|
11263
11357
|
shares = await getShareBalance();
|
|
11264
11358
|
if (shares === 0n) {
|
|
11265
|
-
console.error(
|
|
11359
|
+
console.error(chalk18.red("\n \u2716 No shares to redeem.\n"));
|
|
11266
11360
|
process.exit(1);
|
|
11267
11361
|
}
|
|
11268
11362
|
}
|
|
11269
11363
|
} catch (err) {
|
|
11270
|
-
console.error(
|
|
11364
|
+
console.error(chalk18.red(`
|
|
11271
11365
|
\u2716 ${err instanceof Error ? err.message : String(err)}
|
|
11272
11366
|
`));
|
|
11273
11367
|
process.exit(1);
|
|
11274
11368
|
}
|
|
11275
|
-
if (opts.receiver && !
|
|
11276
|
-
console.error(
|
|
11369
|
+
if (opts.receiver && !isAddress12(opts.receiver)) {
|
|
11370
|
+
console.error(chalk18.red(`
|
|
11277
11371
|
\u2716 Invalid receiver address: ${opts.receiver}
|
|
11278
11372
|
`));
|
|
11279
11373
|
process.exit(1);
|
|
@@ -11281,31 +11375,31 @@ vaultCmd.command("redeem").description("Redeem vault shares for the underlying a
|
|
|
11281
11375
|
try {
|
|
11282
11376
|
await preflightRedeem(shares);
|
|
11283
11377
|
} catch (err) {
|
|
11284
|
-
console.error(
|
|
11378
|
+
console.error(chalk18.red(`
|
|
11285
11379
|
\u2716 ${err instanceof Error ? err.message : String(err)}
|
|
11286
11380
|
`));
|
|
11287
11381
|
process.exit(1);
|
|
11288
11382
|
}
|
|
11289
|
-
const sharesDisplay =
|
|
11290
|
-
const spinner =
|
|
11383
|
+
const sharesDisplay = formatUnits10(shares, shareDecimals);
|
|
11384
|
+
const spinner = ora10(`Redeeming ${sharesDisplay} shares...`).start();
|
|
11291
11385
|
try {
|
|
11292
11386
|
const { hash, assetsOut } = await redeem(
|
|
11293
11387
|
shares,
|
|
11294
11388
|
opts.receiver
|
|
11295
11389
|
);
|
|
11296
11390
|
spinner.succeed(`Redeemed: ${hash}`);
|
|
11297
|
-
console.log(
|
|
11298
|
-
console.log(` Assets received: ${
|
|
11391
|
+
console.log(chalk18.dim(` ${getExplorerUrl(hash)}`));
|
|
11392
|
+
console.log(` Assets received: ${formatUnits10(assetsOut, assetDecimals)}`);
|
|
11299
11393
|
} catch (err) {
|
|
11300
11394
|
spinner.fail("Redeem failed");
|
|
11301
|
-
console.error(
|
|
11395
|
+
console.error(chalk18.red(formatContractError(err)));
|
|
11302
11396
|
process.exit(1);
|
|
11303
11397
|
}
|
|
11304
11398
|
});
|
|
11305
11399
|
var strategy = program.command("strategy").description("Strategy templates \u2014 list, clone, propose");
|
|
11306
11400
|
registerStrategyTemplateCommands(strategy);
|
|
11307
11401
|
program.command("providers").description("List available providers (research + swap routing)").action(async () => {
|
|
11308
|
-
const { listProviders } = await import("./providers-
|
|
11402
|
+
const { listProviders } = await import("./providers-4HQHB7V5.js");
|
|
11309
11403
|
for (const info of listProviders()) {
|
|
11310
11404
|
console.log(`
|
|
11311
11405
|
${info.name} (${info.type})`);
|
|
@@ -11315,17 +11409,17 @@ ${info.name} (${info.type})`);
|
|
|
11315
11409
|
}
|
|
11316
11410
|
});
|
|
11317
11411
|
try {
|
|
11318
|
-
const { registerChatCommands } = await import("./chat-
|
|
11412
|
+
const { registerChatCommands } = await import("./chat-FGTYCJ4B.js");
|
|
11319
11413
|
registerChatCommands(program);
|
|
11320
11414
|
} catch {
|
|
11321
11415
|
program.command("chat <name> [action] [actionArgs...]").description("Fund chat (XMTP) \u2014 requires @xmtp/cli").action(() => {
|
|
11322
|
-
console.error(
|
|
11323
|
-
console.error(
|
|
11324
|
-
console.error(
|
|
11416
|
+
console.error(chalk18.red("XMTP CLI not available."));
|
|
11417
|
+
console.error(chalk18.dim("Install with: npm install -g @xmtp/cli"));
|
|
11418
|
+
console.error(chalk18.dim("Or reinstall: npm i -g @sherwoodagent/cli"));
|
|
11325
11419
|
process.exit(1);
|
|
11326
11420
|
});
|
|
11327
11421
|
}
|
|
11328
|
-
var { registerSessionCommands } = await import("./session-
|
|
11422
|
+
var { registerSessionCommands } = await import("./session-SIOJZO6P.js");
|
|
11329
11423
|
registerSessionCommands(program);
|
|
11330
11424
|
registerVeniceCommands(program);
|
|
11331
11425
|
registerAllowanceCommands(program);
|
|
@@ -11334,11 +11428,11 @@ registerProposalCommands(program);
|
|
|
11334
11428
|
registerQueueCommands(program);
|
|
11335
11429
|
registerGovernorCommands(program);
|
|
11336
11430
|
registerGuardianCommands(program);
|
|
11337
|
-
var { registerResearchCommands } = await import("./research-
|
|
11431
|
+
var { registerResearchCommands } = await import("./research-2RWAMWWH.js");
|
|
11338
11432
|
registerResearchCommands(program);
|
|
11339
|
-
var { registerAgentCommands } = await import("./agent-
|
|
11433
|
+
var { registerAgentCommands } = await import("./agent-C5XZSK7Z.js");
|
|
11340
11434
|
registerAgentCommands(program);
|
|
11341
|
-
var { registerTradeCommands } = await import("./trade-
|
|
11435
|
+
var { registerTradeCommands } = await import("./trade-ZEIY3MCL.js");
|
|
11342
11436
|
registerTradeCommands(program);
|
|
11343
11437
|
registerGridCommand(program);
|
|
11344
11438
|
var configCmd = program.command("config");
|
|
@@ -11347,58 +11441,58 @@ configCmd.command("set").description("Save settings to ~/.sherwood/config.json (
|
|
|
11347
11441
|
if (opts.privateKey) {
|
|
11348
11442
|
setPrivateKey(opts.privateKey);
|
|
11349
11443
|
const account = getAccount();
|
|
11350
|
-
console.log(
|
|
11351
|
-
console.log(
|
|
11444
|
+
console.log(chalk18.green("Private key saved to ~/.sherwood/config.json"));
|
|
11445
|
+
console.log(chalk18.dim(` Wallet: ${account.address}`));
|
|
11352
11446
|
saved = true;
|
|
11353
11447
|
}
|
|
11354
11448
|
if (opts.vault) {
|
|
11355
11449
|
const chainId = getChain().id;
|
|
11356
11450
|
setChainContract(chainId, "vault", opts.vault);
|
|
11357
|
-
console.log(
|
|
11358
|
-
console.log(
|
|
11451
|
+
console.log(chalk18.green(`Vault saved to ~/.sherwood/config.json (chain ${chainId})`));
|
|
11452
|
+
console.log(chalk18.dim(` Vault: ${opts.vault}`));
|
|
11359
11453
|
saved = true;
|
|
11360
11454
|
}
|
|
11361
11455
|
if (opts.rpc) {
|
|
11362
11456
|
const network = getNetwork();
|
|
11363
11457
|
setConfigRpcUrl(network, opts.rpc);
|
|
11364
|
-
console.log(
|
|
11365
|
-
console.log(
|
|
11458
|
+
console.log(chalk18.green(`RPC URL saved for ${network}`));
|
|
11459
|
+
console.log(chalk18.dim(` RPC: ${opts.rpc}`));
|
|
11366
11460
|
saved = true;
|
|
11367
11461
|
}
|
|
11368
11462
|
if (opts.notifyTo) {
|
|
11369
11463
|
setNotifyTo(opts.notifyTo);
|
|
11370
|
-
console.log(
|
|
11371
|
-
console.log(
|
|
11464
|
+
console.log(chalk18.green("Notify destination saved to ~/.sherwood/config.json"));
|
|
11465
|
+
console.log(chalk18.dim(` Notify to: ${opts.notifyTo}`));
|
|
11372
11466
|
saved = true;
|
|
11373
11467
|
}
|
|
11374
11468
|
if (opts.uniswapApiKey) {
|
|
11375
11469
|
setUniswapApiKey(opts.uniswapApiKey);
|
|
11376
|
-
console.log(
|
|
11470
|
+
console.log(chalk18.green("Uniswap API key saved to ~/.sherwood/config.json"));
|
|
11377
11471
|
saved = true;
|
|
11378
11472
|
}
|
|
11379
11473
|
if (opts.veniceApiKey) {
|
|
11380
11474
|
setVeniceApiKey(opts.veniceApiKey);
|
|
11381
|
-
console.log(
|
|
11475
|
+
console.log(chalk18.green("Venice API key saved to ~/.sherwood/config.json"));
|
|
11382
11476
|
saved = true;
|
|
11383
11477
|
}
|
|
11384
11478
|
if (opts.anthropicApiKey) {
|
|
11385
11479
|
setAnthropicApiKey(opts.anthropicApiKey);
|
|
11386
|
-
console.log(
|
|
11480
|
+
console.log(chalk18.green("Anthropic API key saved to ~/.sherwood/config.json"));
|
|
11387
11481
|
saved = true;
|
|
11388
11482
|
}
|
|
11389
11483
|
if (opts.xmtpGroup) {
|
|
11390
11484
|
const parts = opts.xmtpGroup.split(":");
|
|
11391
11485
|
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
|
11392
|
-
console.log(
|
|
11486
|
+
console.log(chalk18.red("Format: --xmtp-group <subdomain>:<groupId>"));
|
|
11393
11487
|
process.exit(1);
|
|
11394
11488
|
}
|
|
11395
11489
|
cacheGroupId(parts[0], parts[1]);
|
|
11396
|
-
console.log(
|
|
11397
|
-
console.log(
|
|
11490
|
+
console.log(chalk18.green(`XMTP group ID cached for ${parts[0]}`));
|
|
11491
|
+
console.log(chalk18.dim(` Group ID: ${parts[1]}`));
|
|
11398
11492
|
saved = true;
|
|
11399
11493
|
}
|
|
11400
11494
|
if (!saved) {
|
|
11401
|
-
console.log(
|
|
11495
|
+
console.log(chalk18.red("Provide at least one of: --private-key, --vault, --rpc, --notify-to, --uniswap-api-key, --venice-api-key, --anthropic-api-key, --xmtp-group"));
|
|
11402
11496
|
process.exit(1);
|
|
11403
11497
|
}
|
|
11404
11498
|
});
|
|
@@ -11409,25 +11503,25 @@ configCmd.command("show").description("Display current config for the active net
|
|
|
11409
11503
|
const config = loadConfig();
|
|
11410
11504
|
const customRpc = config.rpc?.[network];
|
|
11411
11505
|
console.log();
|
|
11412
|
-
console.log(
|
|
11413
|
-
console.log(
|
|
11414
|
-
console.log(` Network: ${
|
|
11415
|
-
console.log(` RPC: ${customRpc ?
|
|
11416
|
-
console.log(` Wallet: ${config.privateKey ?
|
|
11417
|
-
console.log(` Agent ID: ${config.agentId ??
|
|
11418
|
-
console.log(` Vault: ${contracts.vault ??
|
|
11419
|
-
console.log(` Uniswap: ${getUniswapApiKey() ?
|
|
11420
|
-
console.log(` Venice: ${getVeniceApiKey() ?
|
|
11506
|
+
console.log(chalk18.bold(`Sherwood Config`));
|
|
11507
|
+
console.log(chalk18.dim("\u2500".repeat(50)));
|
|
11508
|
+
console.log(` Network: ${chalk18.cyan(network)} (chain ${chainId})`);
|
|
11509
|
+
console.log(` RPC: ${customRpc ? chalk18.green(customRpc) : chalk18.dim("default")}`);
|
|
11510
|
+
console.log(` Wallet: ${config.privateKey ? chalk18.green("configured") : chalk18.dim("not set")}`);
|
|
11511
|
+
console.log(` Agent ID: ${config.agentId ?? chalk18.dim("not set")}`);
|
|
11512
|
+
console.log(` Vault: ${contracts.vault ?? chalk18.dim("not set")}`);
|
|
11513
|
+
console.log(` Uniswap: ${getUniswapApiKey() ? chalk18.green("API key configured") : chalk18.dim("not set")}`);
|
|
11514
|
+
console.log(` Venice: ${getVeniceApiKey() ? chalk18.green("API key configured") : chalk18.dim("not set")}`);
|
|
11421
11515
|
const groupEntries = Object.entries(config.groupCache || {}).filter(([, v]) => v);
|
|
11422
11516
|
if (groupEntries.length > 0) {
|
|
11423
11517
|
console.log();
|
|
11424
|
-
console.log(
|
|
11518
|
+
console.log(chalk18.bold(" XMTP Groups"));
|
|
11425
11519
|
for (const [sub, gid] of groupEntries) {
|
|
11426
|
-
console.log(` ${sub}: ${
|
|
11520
|
+
console.log(` ${sub}: ${chalk18.cyan(gid)}`);
|
|
11427
11521
|
}
|
|
11428
11522
|
}
|
|
11429
11523
|
console.log();
|
|
11430
|
-
console.log(
|
|
11524
|
+
console.log(chalk18.dim(" Config file: ~/.sherwood/config.json"));
|
|
11431
11525
|
console.log();
|
|
11432
11526
|
});
|
|
11433
11527
|
program.parse();
|