@sherwoodagent/cli 0.86.1 → 0.87.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{abis-WVW2EBI3.js → abis-WJQ6IVYV.js} +2 -2
- package/dist/{agent-TBLC3WNJ.js → agent-FCGZL4GF.js} +7 -7
- package/dist/{chat-ETZSKQDI.js → chat-AKLYUJZV.js} +8 -8
- package/dist/{chunk-ZN2MHIB7.js → chunk-4ITTHKRX.js} +5 -5
- package/dist/{chunk-EVUKJ7CJ.js → chunk-6D3FNU75.js} +5 -5
- package/dist/{chunk-45FELUWY.js → chunk-6SAE2VDK.js} +2 -2
- package/dist/{chunk-EA65CDQH.js → chunk-BH3VTJ4F.js} +14 -23
- package/dist/chunk-BH3VTJ4F.js.map +1 -0
- package/dist/{chunk-QRMZRAMG.js → chunk-D4BSBW3Q.js} +221 -10
- package/dist/chunk-D4BSBW3Q.js.map +1 -0
- package/dist/{chunk-KVB5P2OE.js → chunk-EEI6FRTE.js} +2 -2
- package/dist/{chunk-IAM4VIU2.js → chunk-FWQESFFM.js} +18 -41
- package/dist/chunk-FWQESFFM.js.map +1 -0
- package/dist/{chunk-PVTZMRC6.js → chunk-G3FUEM5N.js} +2 -2
- package/dist/chunk-G3FUEM5N.js.map +1 -0
- package/dist/{chunk-YAS52NEF.js → chunk-G5MXVAFC.js} +18 -5
- package/dist/chunk-G5MXVAFC.js.map +1 -0
- package/dist/{chunk-F3MDCUGO.js → chunk-J2IZWAEW.js} +75 -227
- package/dist/chunk-J2IZWAEW.js.map +1 -0
- package/dist/{chunk-XPVXNG2K.js → chunk-N2E2Q5GL.js} +6 -6
- package/dist/{chunk-3IAZQMAW.js → chunk-QFGZQYZA.js} +29 -82
- package/dist/chunk-QFGZQYZA.js.map +1 -0
- package/dist/{chunk-OUYMZ5ZC.js → chunk-SI65QHTY.js} +5 -5
- package/dist/{chunk-6QBGTX2Q.js → chunk-VYCLG5DE.js} +264 -487
- package/dist/chunk-VYCLG5DE.js.map +1 -0
- package/dist/{client-3SE632Q2.js → client-PYFE6YQR.js} +3 -3
- package/dist/{eas-KMDAMLXW.js → eas-DV3Z7AT3.js} +6 -6
- package/dist/{governor-JNSWPJVQ.js → governor-QFBN6RYS.js} +8 -6
- package/dist/identity-M4TXM7EX.js +18 -0
- package/dist/index.js +352 -355
- package/dist/index.js.map +1 -1
- package/dist/{network-FEHRFDPJ.js → network-7445KSCF.js} +2 -2
- package/dist/{price-YNPYE2EO.js → price-EXZDYVDD.js} +6 -6
- package/dist/{providers-BMOHHVUN.js → providers-6UZKDFQD.js} +4 -4
- package/dist/{research-W74PNYPO.js → research-44GHYE4H.js} +7 -7
- package/dist/{session-HVRZ6AJM.js → session-45NQAHFI.js} +14 -12
- package/dist/session-45NQAHFI.js.map +1 -0
- package/dist/{trade-EOQY72L7.js → trade-FO52SP62.js} +15 -15
- package/dist/{vault-HWJQJW5U.js → vault-WWGSRNRP.js} +8 -8
- package/dist/{xmtp-KW73BGVO.js → xmtp-72IRK7JL.js} +6 -6
- package/package.json +1 -1
- package/dist/chunk-3IAZQMAW.js.map +0 -1
- package/dist/chunk-6QBGTX2Q.js.map +0 -1
- package/dist/chunk-EA65CDQH.js.map +0 -1
- package/dist/chunk-F3MDCUGO.js.map +0 -1
- package/dist/chunk-IAM4VIU2.js.map +0 -1
- package/dist/chunk-PVTZMRC6.js.map +0 -1
- package/dist/chunk-QRMZRAMG.js.map +0 -1
- package/dist/chunk-YAS52NEF.js.map +0 -1
- package/dist/identity-ATWV2O7Y.js +0 -15
- package/dist/session-HVRZ6AJM.js.map +0 -1
- /package/dist/{abis-WVW2EBI3.js.map → abis-WJQ6IVYV.js.map} +0 -0
- /package/dist/{agent-TBLC3WNJ.js.map → agent-FCGZL4GF.js.map} +0 -0
- /package/dist/{chat-ETZSKQDI.js.map → chat-AKLYUJZV.js.map} +0 -0
- /package/dist/{chunk-ZN2MHIB7.js.map → chunk-4ITTHKRX.js.map} +0 -0
- /package/dist/{chunk-EVUKJ7CJ.js.map → chunk-6D3FNU75.js.map} +0 -0
- /package/dist/{chunk-45FELUWY.js.map → chunk-6SAE2VDK.js.map} +0 -0
- /package/dist/{chunk-KVB5P2OE.js.map → chunk-EEI6FRTE.js.map} +0 -0
- /package/dist/{chunk-XPVXNG2K.js.map → chunk-N2E2Q5GL.js.map} +0 -0
- /package/dist/{chunk-OUYMZ5ZC.js.map → chunk-SI65QHTY.js.map} +0 -0
- /package/dist/{client-3SE632Q2.js.map → client-PYFE6YQR.js.map} +0 -0
- /package/dist/{eas-KMDAMLXW.js.map → eas-DV3Z7AT3.js.map} +0 -0
- /package/dist/{governor-JNSWPJVQ.js.map → governor-QFBN6RYS.js.map} +0 -0
- /package/dist/{identity-ATWV2O7Y.js.map → identity-M4TXM7EX.js.map} +0 -0
- /package/dist/{network-FEHRFDPJ.js.map → network-7445KSCF.js.map} +0 -0
- /package/dist/{price-YNPYE2EO.js.map → price-EXZDYVDD.js.map} +0 -0
- /package/dist/{providers-BMOHHVUN.js.map → providers-6UZKDFQD.js.map} +0 -0
- /package/dist/{research-W74PNYPO.js.map → research-44GHYE4H.js.map} +0 -0
- /package/dist/{trade-EOQY72L7.js.map → trade-FO52SP62.js.map} +0 -0
- /package/dist/{vault-HWJQJW5U.js.map → vault-WWGSRNRP.js.map} +0 -0
- /package/dist/{xmtp-KW73BGVO.js.map → xmtp-72IRK7JL.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-EEI6FRTE.js";
|
|
19
19
|
import {
|
|
20
20
|
DEFAULT_FEE_TIER,
|
|
21
21
|
HYPERLIQUID_PERP_STRATEGY_ABI,
|
|
@@ -31,10 +31,13 @@ import {
|
|
|
31
31
|
parseAlignedOverrides,
|
|
32
32
|
resolveToken,
|
|
33
33
|
selectBestFeeTier
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-QFGZQYZA.js";
|
|
35
35
|
import {
|
|
36
36
|
DEFAULT_ISSUING_CHAIN_ID,
|
|
37
|
+
STRATEGY_FACTORY_ABI,
|
|
37
38
|
appendVidTag,
|
|
39
|
+
deriveStrategySalt,
|
|
40
|
+
effectiveStrategySalt,
|
|
38
41
|
emitCalldata,
|
|
39
42
|
encodeApproveAgent,
|
|
40
43
|
encodeApproveDepositor,
|
|
@@ -50,7 +53,6 @@ import {
|
|
|
50
53
|
encodeMgmtSplit,
|
|
51
54
|
encodePerfSplit,
|
|
52
55
|
encodePropose,
|
|
53
|
-
encodeProposeWithSandbox,
|
|
54
56
|
encodeRedeem,
|
|
55
57
|
encodeRegisterAgent,
|
|
56
58
|
encodeSettle,
|
|
@@ -61,11 +63,12 @@ import {
|
|
|
61
63
|
isCalldataOnly,
|
|
62
64
|
makeOwnerOfCache,
|
|
63
65
|
parseVidTag,
|
|
66
|
+
predictStrategyCloneAddress,
|
|
64
67
|
registerIdentityCommands,
|
|
65
68
|
setCalldataOnly,
|
|
66
69
|
stripVidTag,
|
|
67
70
|
verifyVidTag
|
|
68
|
-
} from "./chunk-
|
|
71
|
+
} from "./chunk-VYCLG5DE.js";
|
|
69
72
|
import {
|
|
70
73
|
createApproval,
|
|
71
74
|
createJoinRequest,
|
|
@@ -75,20 +78,20 @@ import {
|
|
|
75
78
|
queryJoinRequests,
|
|
76
79
|
revokeJoinRequest,
|
|
77
80
|
stripReferrerPrefix
|
|
78
|
-
} from "./chunk-
|
|
81
|
+
} from "./chunk-SI65QHTY.js";
|
|
79
82
|
import {
|
|
80
83
|
createFund,
|
|
81
84
|
getActiveFunds,
|
|
82
85
|
getFund,
|
|
83
86
|
subdomainExists,
|
|
84
87
|
updateMetadata
|
|
85
|
-
} from "./chunk-
|
|
88
|
+
} from "./chunk-6D3FNU75.js";
|
|
86
89
|
import {
|
|
87
90
|
resolveFund,
|
|
88
91
|
resolveVaultFund,
|
|
89
92
|
searchFundAcrossChains,
|
|
90
93
|
setTextRecord
|
|
91
|
-
} from "./chunk-
|
|
94
|
+
} from "./chunk-4ITTHKRX.js";
|
|
92
95
|
import {
|
|
93
96
|
approveDepositor,
|
|
94
97
|
deposit,
|
|
@@ -111,8 +114,8 @@ import {
|
|
|
111
114
|
removeDepositor,
|
|
112
115
|
setAgentFeeBps,
|
|
113
116
|
setVaultAddress
|
|
114
|
-
} from "./chunk-
|
|
115
|
-
import "./chunk-
|
|
117
|
+
} from "./chunk-N2E2Q5GL.js";
|
|
118
|
+
import "./chunk-BH3VTJ4F.js";
|
|
116
119
|
import {
|
|
117
120
|
PROPOSAL_STATE,
|
|
118
121
|
PROPOSAL_STATES,
|
|
@@ -137,9 +140,9 @@ import {
|
|
|
137
140
|
getVoteWeight,
|
|
138
141
|
hasVoted,
|
|
139
142
|
parseDuration,
|
|
140
|
-
parseProposalIdFromLogs,
|
|
141
143
|
proposalCount,
|
|
142
144
|
propose,
|
|
145
|
+
reclaimProposerBond,
|
|
143
146
|
scanReviewCandidates,
|
|
144
147
|
setCooldownPeriod,
|
|
145
148
|
setExecutionWindow,
|
|
@@ -151,7 +154,7 @@ import {
|
|
|
151
154
|
setVotingPeriod,
|
|
152
155
|
settleProposal,
|
|
153
156
|
vote
|
|
154
|
-
} from "./chunk-
|
|
157
|
+
} from "./chunk-G5MXVAFC.js";
|
|
155
158
|
import {
|
|
156
159
|
AERODROME,
|
|
157
160
|
MOONWELL,
|
|
@@ -160,7 +163,7 @@ import {
|
|
|
160
163
|
TOKENS,
|
|
161
164
|
UNISWAP,
|
|
162
165
|
VENICE
|
|
163
|
-
} from "./chunk-
|
|
166
|
+
} from "./chunk-FWQESFFM.js";
|
|
164
167
|
import {
|
|
165
168
|
GAS_UNITS,
|
|
166
169
|
estimateGasCushion,
|
|
@@ -171,7 +174,7 @@ import {
|
|
|
171
174
|
sendTxWithRetry,
|
|
172
175
|
waitForReceipt,
|
|
173
176
|
writeContractWithRetry
|
|
174
|
-
} from "./chunk-
|
|
177
|
+
} from "./chunk-D4BSBW3Q.js";
|
|
175
178
|
import {
|
|
176
179
|
DEFAULT_NETWORK,
|
|
177
180
|
VALID_NETWORKS,
|
|
@@ -181,7 +184,7 @@ import {
|
|
|
181
184
|
isTestnet,
|
|
182
185
|
setNetwork,
|
|
183
186
|
withNetwork
|
|
184
|
-
} from "./chunk-
|
|
187
|
+
} from "./chunk-G3FUEM5N.js";
|
|
185
188
|
import {
|
|
186
189
|
addFund,
|
|
187
190
|
cacheGroupId,
|
|
@@ -212,9 +215,10 @@ import {
|
|
|
212
215
|
PORTFOLIO_STRATEGY_ABI,
|
|
213
216
|
SWAP_ROUTER_ABI,
|
|
214
217
|
SWAP_ROUTER_EXACT_INPUT_SINGLE_ABI,
|
|
218
|
+
SYNDICATE_GOVERNOR_ABI,
|
|
215
219
|
SYNDICATE_VAULT_ABI,
|
|
216
220
|
VENICE_STAKING_ABI
|
|
217
|
-
} from "./chunk-
|
|
221
|
+
} from "./chunk-J2IZWAEW.js";
|
|
218
222
|
import {
|
|
219
223
|
fetchMetadata,
|
|
220
224
|
mergeFundMetadata,
|
|
@@ -225,13 +229,13 @@ import {
|
|
|
225
229
|
import { config as loadDotenv } from "dotenv";
|
|
226
230
|
import { createRequire } from "module";
|
|
227
231
|
import { Command, Option } from "commander";
|
|
228
|
-
import { parseUnits as parseUnits11, formatUnits as
|
|
232
|
+
import { parseUnits as parseUnits11, formatUnits as formatUnits10, isAddress as isAddress10 } from "viem";
|
|
229
233
|
import chalk17 from "chalk";
|
|
230
234
|
import ora9 from "ora";
|
|
231
235
|
import { input, confirm, select } from "@inquirer/prompts";
|
|
232
236
|
|
|
233
237
|
// src/lib/generated/contract-constants.ts
|
|
234
|
-
var MAX_AGENT_FEE_BPS =
|
|
238
|
+
var MAX_AGENT_FEE_BPS = 2500;
|
|
235
239
|
|
|
236
240
|
// src/lib/format.ts
|
|
237
241
|
function formatDurationShort(seconds) {
|
|
@@ -291,6 +295,126 @@ async function cloneTemplate(template) {
|
|
|
291
295
|
return { clone: receipt.contractAddress, hash };
|
|
292
296
|
}
|
|
293
297
|
|
|
298
|
+
// src/lib/strategy-deploy.ts
|
|
299
|
+
var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
300
|
+
var STRATEGY_FACTORY_EXTRA_ABI = [
|
|
301
|
+
{
|
|
302
|
+
name: "registerStrategy",
|
|
303
|
+
type: "function",
|
|
304
|
+
stateMutability: "nonpayable",
|
|
305
|
+
inputs: [{ name: "strategy", type: "address" }],
|
|
306
|
+
outputs: []
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
name: "isRegisteredStrategy",
|
|
310
|
+
type: "function",
|
|
311
|
+
stateMutability: "view",
|
|
312
|
+
inputs: [{ name: "strategy", type: "address" }],
|
|
313
|
+
outputs: [{ name: "", type: "bool" }]
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
name: "approvedTemplate",
|
|
317
|
+
type: "function",
|
|
318
|
+
stateMutability: "view",
|
|
319
|
+
inputs: [{ name: "template", type: "address" }],
|
|
320
|
+
outputs: [{ name: "", type: "bool" }]
|
|
321
|
+
}
|
|
322
|
+
];
|
|
323
|
+
function planStrategyCloneDeploy(args) {
|
|
324
|
+
const factory = args.factory ?? SHERWOOD().STRATEGY_FACTORY;
|
|
325
|
+
if (!factory || factory === ZERO_ADDRESS) {
|
|
326
|
+
return {
|
|
327
|
+
mode: "raw",
|
|
328
|
+
reason: "no STRATEGY_FACTORY in this network's address book \u2014 falling back to a raw ERC-1167 clone. A governor that gates propose on strategy registration will refuse the resulting proposal."
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
const salt = args.salt ?? deriveStrategySalt(args.vault, args.proposer, args.template, args.initData);
|
|
332
|
+
const clone = predictStrategyCloneAddress(
|
|
333
|
+
factory,
|
|
334
|
+
args.template,
|
|
335
|
+
effectiveStrategySalt(args.vault, salt)
|
|
336
|
+
);
|
|
337
|
+
return {
|
|
338
|
+
mode: "factory",
|
|
339
|
+
factory,
|
|
340
|
+
clone,
|
|
341
|
+
salt,
|
|
342
|
+
to: factory,
|
|
343
|
+
functionName: "cloneAndInitDeterministic",
|
|
344
|
+
args: [args.template, args.vault, args.proposer, args.initData, salt]
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
async function deployStrategyClone(args) {
|
|
348
|
+
const plan = planStrategyCloneDeploy(args);
|
|
349
|
+
const publicClient = getPublicClient();
|
|
350
|
+
const account = getAccount();
|
|
351
|
+
if (plan.mode === "factory") {
|
|
352
|
+
const approved = await publicClient.readContract({
|
|
353
|
+
address: plan.factory,
|
|
354
|
+
abi: STRATEGY_FACTORY_EXTRA_ABI,
|
|
355
|
+
functionName: "approvedTemplate",
|
|
356
|
+
args: [args.template]
|
|
357
|
+
});
|
|
358
|
+
if (!approved) {
|
|
359
|
+
throw new Error(
|
|
360
|
+
`Template ${args.template} is not on StrategyFactory ${plan.factory}'s allowlist \u2014 cloneAndInitDeterministic would revert TemplateNotApproved. The factory owner must call setTemplateApproval(${args.template}, true).`
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
const existing = await publicClient.getCode({ address: plan.clone });
|
|
364
|
+
if (existing && existing !== "0x") {
|
|
365
|
+
throw new Error(
|
|
366
|
+
`A strategy clone already exists at the deterministic address ${plan.clone} (same vault + template + init params as an earlier deploy). Re-run with a different --salt to deploy a fresh clone.`
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
const hash2 = await writeContractWithRetry({
|
|
370
|
+
account,
|
|
371
|
+
chain: getChain(),
|
|
372
|
+
address: plan.factory,
|
|
373
|
+
abi: STRATEGY_FACTORY_ABI,
|
|
374
|
+
functionName: plan.functionName,
|
|
375
|
+
args: plan.args
|
|
376
|
+
});
|
|
377
|
+
const receipt = await waitForReceipt(hash2);
|
|
378
|
+
if (receipt.status === "reverted") {
|
|
379
|
+
throw new Error(`StrategyFactory.cloneAndInitDeterministic reverted on-chain (tx: ${hash2})`);
|
|
380
|
+
}
|
|
381
|
+
const deployed = await publicClient.getCode({ address: plan.clone });
|
|
382
|
+
if (!deployed || deployed === "0x") {
|
|
383
|
+
throw new Error(
|
|
384
|
+
`The factory deployed a clone, but not at the predicted address ${plan.clone} (tx: ${hash2}). The CLI's CREATE2 prediction and the factory's salt fold have diverged \u2014 do not propose against this address.`
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
return { clone: plan.clone, hash: hash2, viaFactory: true, salt: plan.salt };
|
|
388
|
+
}
|
|
389
|
+
const { clone, hash } = await cloneTemplate(args.template);
|
|
390
|
+
const initHash = await writeContractWithRetry({
|
|
391
|
+
account,
|
|
392
|
+
chain: getChain(),
|
|
393
|
+
address: clone,
|
|
394
|
+
abi: BASE_STRATEGY_ABI,
|
|
395
|
+
functionName: "initialize",
|
|
396
|
+
args: [args.vault, args.proposer, args.initData]
|
|
397
|
+
});
|
|
398
|
+
const initReceipt = await waitForReceipt(initHash);
|
|
399
|
+
if (initReceipt.status === "reverted") {
|
|
400
|
+
throw new Error(`Strategy initialize reverted on-chain (tx: ${initHash})`);
|
|
401
|
+
}
|
|
402
|
+
let registerHash;
|
|
403
|
+
const factory = args.factory ?? SHERWOOD().STRATEGY_FACTORY;
|
|
404
|
+
if (factory && factory !== ZERO_ADDRESS) {
|
|
405
|
+
registerHash = await writeContractWithRetry({
|
|
406
|
+
account,
|
|
407
|
+
chain: getChain(),
|
|
408
|
+
address: factory,
|
|
409
|
+
abi: STRATEGY_FACTORY_EXTRA_ABI,
|
|
410
|
+
functionName: "registerStrategy",
|
|
411
|
+
args: [clone]
|
|
412
|
+
});
|
|
413
|
+
await waitForReceipt(registerHash);
|
|
414
|
+
}
|
|
415
|
+
return { clone, hash, viaFactory: false, initHash, registerHash };
|
|
416
|
+
}
|
|
417
|
+
|
|
294
418
|
// src/grid/strategy-abi.ts
|
|
295
419
|
var HYPERLIQUID_GRID_STRATEGY_ABI = [
|
|
296
420
|
{
|
|
@@ -610,6 +734,20 @@ var TEMPLATES = [
|
|
|
610
734
|
key: "hyperliquid-grid",
|
|
611
735
|
description: "ATR-based grid trading on Hyperliquid via HyperEVM precompiles",
|
|
612
736
|
addressKey: "HYPERLIQUID_GRID"
|
|
737
|
+
},
|
|
738
|
+
{
|
|
739
|
+
name: "Morpho Supply",
|
|
740
|
+
key: "morpho-supply",
|
|
741
|
+
description: "Supply the vault asset to a Morpho market (init encoder not wired in the CLI yet)",
|
|
742
|
+
addressKey: "MORPHO_SUPPLY",
|
|
743
|
+
encoder: false
|
|
744
|
+
},
|
|
745
|
+
{
|
|
746
|
+
name: "Concentrated Liquidity",
|
|
747
|
+
key: "concentrated-liquidity",
|
|
748
|
+
description: "Uniswap-v3-style concentrated LP range (init encoder not wired in the CLI yet)",
|
|
749
|
+
addressKey: "CONCENTRATED_LIQUIDITY",
|
|
750
|
+
encoder: false
|
|
613
751
|
}
|
|
614
752
|
];
|
|
615
753
|
function resolveTemplate(key) {
|
|
@@ -624,6 +762,19 @@ function resolveTemplate(key) {
|
|
|
624
762
|
console.error(chalk2.red(`Template "${def.name}" not deployed on this network.`));
|
|
625
763
|
process.exit(1);
|
|
626
764
|
}
|
|
765
|
+
if (def.encoder === false) {
|
|
766
|
+
console.error(
|
|
767
|
+
chalk2.red(
|
|
768
|
+
`Template "${def.name}" is deployed at ${address} but the CLI cannot encode its initialize() tuple yet.`
|
|
769
|
+
)
|
|
770
|
+
);
|
|
771
|
+
console.error(
|
|
772
|
+
chalk2.yellow(
|
|
773
|
+
" Deploy it via StrategyFactory.cloneAndInitDeterministic with hand-encoded initData, then reference the clone from `sherwood proposal create`."
|
|
774
|
+
)
|
|
775
|
+
);
|
|
776
|
+
process.exit(1);
|
|
777
|
+
}
|
|
627
778
|
return { def, address };
|
|
628
779
|
}
|
|
629
780
|
async function resolvePortfolioFeeTiers(opts, deps) {
|
|
@@ -892,10 +1043,10 @@ function formatBatch(calls) {
|
|
|
892
1043
|
|
|
893
1044
|
// src/commands/strategy-propose.ts
|
|
894
1045
|
function registerStrategyProposeCommands(strategy2) {
|
|
895
|
-
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 (
|
|
1046
|
+
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 (default: derived from vault+proposer+template+initData). Pass a fresh one to re-deploy identical params.").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("--feeds <list>", "Comma-separated Chainlink AggregatorV3 push-feed addresses per token (Portfolio; auto-resolved from the registry when a slot is left empty)").option("--price-decimals <list>", "Comma-separated raw feed decimals per token (Portfolio; default 8). Must equal the feed's live decimals().").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(
|
|
896
1047
|
"--max-drawdown-bps <bps>",
|
|
897
1048
|
"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."
|
|
898
|
-
).option("--
|
|
1049
|
+
).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) => {
|
|
899
1050
|
const vault = opts.vault;
|
|
900
1051
|
if (!isAddress3(vault)) {
|
|
901
1052
|
console.error(chalk3.red("Invalid vault address"));
|
|
@@ -979,7 +1130,7 @@ function registerStrategyProposeCommands(strategy2) {
|
|
|
979
1130
|
process.exit(1);
|
|
980
1131
|
}
|
|
981
1132
|
if (!opts.agent) opts.agent = proposer;
|
|
982
|
-
const { parseDuration: parseDuration3, getGovernorOf: getGovernorOf2 } = await import("./governor-
|
|
1133
|
+
const { parseDuration: parseDuration3, getGovernorOf: getGovernorOf2 } = await import("./governor-QFBN6RYS.js");
|
|
983
1134
|
const strategyDuration2 = parseDuration3(opts.duration);
|
|
984
1135
|
const chainId = getChain().id;
|
|
985
1136
|
try {
|
|
@@ -1102,42 +1253,40 @@ function registerStrategyProposeCommands(strategy2) {
|
|
|
1102
1253
|
console.error(chalk3.red(formatContractError(err)));
|
|
1103
1254
|
process.exit(1);
|
|
1104
1255
|
}
|
|
1105
|
-
const cloneSpinner = ora2(`Cloning ${def.name} template...`).start();
|
|
1106
1256
|
let clone;
|
|
1107
|
-
try {
|
|
1108
|
-
const result = await cloneTemplate(templateAddr);
|
|
1109
|
-
clone = result.clone;
|
|
1110
|
-
cloneSpinner.succeed(`Cloned: ${chalk3.green(clone)}`);
|
|
1111
|
-
console.log(chalk3.dim(` Tx: ${getExplorerUrl(result.hash)}`));
|
|
1112
|
-
} catch (err) {
|
|
1113
|
-
cloneSpinner.fail("Clone failed");
|
|
1114
|
-
console.error(chalk3.red(formatContractError(err)));
|
|
1115
|
-
process.exit(1);
|
|
1116
|
-
}
|
|
1117
|
-
const initSpinner = ora2("Initializing strategy...").start();
|
|
1118
1257
|
let asset;
|
|
1119
1258
|
let assetAmount;
|
|
1120
1259
|
let extraApprovals;
|
|
1260
|
+
const deploySpinner = ora2(`Deploying ${def.name} strategy...`).start();
|
|
1121
1261
|
try {
|
|
1122
1262
|
const built = await resolveAndBuildInitData(templateKey, opts, vault);
|
|
1123
1263
|
asset = built.asset;
|
|
1124
1264
|
assetAmount = built.assetAmount;
|
|
1125
1265
|
extraApprovals = built.extraApprovals;
|
|
1126
|
-
const
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
args: [vault, account.address, built.initData]
|
|
1266
|
+
const deployed = await deployStrategyClone({
|
|
1267
|
+
template: templateAddr,
|
|
1268
|
+
vault,
|
|
1269
|
+
proposer: account.address,
|
|
1270
|
+
initData: built.initData,
|
|
1271
|
+
salt: opts.salt
|
|
1133
1272
|
});
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1273
|
+
clone = deployed.clone;
|
|
1274
|
+
deploySpinner.succeed(
|
|
1275
|
+
deployed.viaFactory ? `Deployed + registered: ${chalk3.green(clone)}` : `Cloned + initialized: ${chalk3.green(clone)}`
|
|
1276
|
+
);
|
|
1277
|
+
console.log(chalk3.dim(` Tx: ${getExplorerUrl(deployed.hash)}`));
|
|
1278
|
+
if (deployed.initHash) console.log(chalk3.dim(` Init tx: ${getExplorerUrl(deployed.initHash)}`));
|
|
1279
|
+
if (deployed.registerHash) console.log(chalk3.dim(` Register tx: ${getExplorerUrl(deployed.registerHash)}`));
|
|
1280
|
+
if (deployed.salt) console.log(chalk3.dim(` Salt: ${deployed.salt}`));
|
|
1281
|
+
if (!deployed.viaFactory) {
|
|
1282
|
+
console.log(
|
|
1283
|
+
chalk3.yellow(
|
|
1284
|
+
" No StrategyFactory on this network \u2014 the clone was deployed raw. A governor that gates propose on strategy registration will refuse it."
|
|
1285
|
+
)
|
|
1286
|
+
);
|
|
1137
1287
|
}
|
|
1138
|
-
initSpinner.succeed("Initialized");
|
|
1139
1288
|
} catch (err) {
|
|
1140
|
-
|
|
1289
|
+
deploySpinner.fail("Strategy deploy failed");
|
|
1141
1290
|
console.error(chalk3.red(formatContractError(err)));
|
|
1142
1291
|
process.exit(1);
|
|
1143
1292
|
}
|
|
@@ -1210,9 +1359,9 @@ function registerStrategyProposeCommands(strategy2) {
|
|
|
1210
1359
|
console.error(chalk3.red("Missing --name or --duration. Use --write-calls to skip proposal submission."));
|
|
1211
1360
|
process.exit(1);
|
|
1212
1361
|
}
|
|
1213
|
-
const { propose: propose2 } = await import("./governor-
|
|
1362
|
+
const { propose: propose2 } = await import("./governor-QFBN6RYS.js");
|
|
1214
1363
|
const { pinJSON } = await import("./ipfs-M56BLMCW.js");
|
|
1215
|
-
const { parseDuration: parseDuration2 } = await import("./governor-
|
|
1364
|
+
const { parseDuration: parseDuration2 } = await import("./governor-QFBN6RYS.js");
|
|
1216
1365
|
const strategyDuration = parseDuration2(opts.duration);
|
|
1217
1366
|
const metaSpinner = ora2("Pinning metadata to IPFS...").start();
|
|
1218
1367
|
let metadataURI;
|
|
@@ -1244,7 +1393,7 @@ function registerStrategyProposeCommands(strategy2) {
|
|
|
1244
1393
|
metadataURI = `data:application/json;base64,${Buffer.from(inline).toString("base64")}`;
|
|
1245
1394
|
}
|
|
1246
1395
|
try {
|
|
1247
|
-
const { getAgentFeeBps: getAgentFeeBps2 } = await import("./vault-
|
|
1396
|
+
const { getAgentFeeBps: getAgentFeeBps2 } = await import("./vault-WWGSRNRP.js");
|
|
1248
1397
|
const feeBps = await getAgentFeeBps2(vault);
|
|
1249
1398
|
console.log(
|
|
1250
1399
|
chalk3.yellow(
|
|
@@ -1315,6 +1464,11 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
1315
1464
|
console.log(` ${chalk4.bold(t.name)} (${chalk4.cyan(t.key)})`);
|
|
1316
1465
|
console.log(` ${t.description}`);
|
|
1317
1466
|
console.log(` Template: ${chalk4.green(addr)}`);
|
|
1467
|
+
if (t.encoder === false) {
|
|
1468
|
+
console.log(
|
|
1469
|
+
chalk4.yellow(" No CLI init encoder yet \u2014 propose it via hand-encoded initData.")
|
|
1470
|
+
);
|
|
1471
|
+
}
|
|
1318
1472
|
}
|
|
1319
1473
|
}
|
|
1320
1474
|
if (availableCount === 0) {
|
|
@@ -1334,49 +1488,45 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
1334
1488
|
console.log(chalk4.dim("Full proposal: sherwood strategy propose <template> --vault <addr> ..."));
|
|
1335
1489
|
console.log();
|
|
1336
1490
|
});
|
|
1337
|
-
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("--
|
|
1491
|
+
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("--salt <bytes32>", "CREATE2 salt for the deterministic clone (default: derived from vault+proposer+template+initData). Pass a fresh one to re-deploy identical params.").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("--feeds <list>", "Comma-separated Chainlink AggregatorV3 push-feed addresses per token (Portfolio; auto-resolved from the registry when a slot is left empty)").option("--price-decimals <list>", "Comma-separated raw feed decimals per token (Portfolio; default 8). Must equal the feed's live decimals().").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(
|
|
1338
1492
|
"--max-drawdown-bps <bps>",
|
|
1339
1493
|
"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."
|
|
1340
|
-
).option("--
|
|
1494
|
+
).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) => {
|
|
1341
1495
|
const vault = opts.vault;
|
|
1342
1496
|
if (!isAddress4(vault)) {
|
|
1343
1497
|
console.error(chalk4.red("Invalid vault address"));
|
|
1344
1498
|
process.exit(1);
|
|
1345
1499
|
}
|
|
1346
1500
|
const { def, address: templateAddr } = resolveTemplate(templateKey);
|
|
1347
|
-
const cloneSpinner = ora3(`Cloning ${def.name} template...`).start();
|
|
1348
1501
|
let clone;
|
|
1349
|
-
|
|
1350
|
-
try {
|
|
1351
|
-
const result = await cloneTemplate(templateAddr);
|
|
1352
|
-
clone = result.clone;
|
|
1353
|
-
cloneHash = result.hash;
|
|
1354
|
-
cloneSpinner.succeed(`Cloned: ${chalk4.green(clone)}`);
|
|
1355
|
-
console.log(chalk4.dim(` Tx: ${getExplorerUrl(cloneHash)}`));
|
|
1356
|
-
} catch (err) {
|
|
1357
|
-
cloneSpinner.fail("Clone failed");
|
|
1358
|
-
console.error(chalk4.red(formatContractError(err)));
|
|
1359
|
-
process.exit(1);
|
|
1360
|
-
}
|
|
1361
|
-
const initSpinner = ora3("Initializing strategy...").start();
|
|
1502
|
+
const deploySpinner = ora3(`Deploying ${def.name} strategy...`).start();
|
|
1362
1503
|
try {
|
|
1363
1504
|
const { initData } = await resolveAndBuildInitData(templateKey, opts, vault);
|
|
1364
1505
|
const account = getAccount();
|
|
1365
|
-
const
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
args: [vault, account.address, initData]
|
|
1506
|
+
const deployed = await deployStrategyClone({
|
|
1507
|
+
template: templateAddr,
|
|
1508
|
+
vault,
|
|
1509
|
+
proposer: account.address,
|
|
1510
|
+
initData,
|
|
1511
|
+
salt: opts.salt
|
|
1372
1512
|
});
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1513
|
+
clone = deployed.clone;
|
|
1514
|
+
deploySpinner.succeed(
|
|
1515
|
+
deployed.viaFactory ? `Deployed + registered: ${chalk4.green(clone)}` : `Cloned + initialized: ${chalk4.green(clone)}`
|
|
1516
|
+
);
|
|
1517
|
+
console.log(chalk4.dim(` Tx: ${getExplorerUrl(deployed.hash)}`));
|
|
1518
|
+
if (deployed.initHash) console.log(chalk4.dim(` Init tx: ${getExplorerUrl(deployed.initHash)}`));
|
|
1519
|
+
if (deployed.registerHash) console.log(chalk4.dim(` Register tx: ${getExplorerUrl(deployed.registerHash)}`));
|
|
1520
|
+
if (deployed.salt) console.log(chalk4.dim(` Salt: ${deployed.salt}`));
|
|
1521
|
+
if (!deployed.viaFactory) {
|
|
1522
|
+
console.log(
|
|
1523
|
+
chalk4.yellow(
|
|
1524
|
+
" No StrategyFactory on this network \u2014 the clone was deployed raw. A governor that gates propose on strategy registration will refuse it."
|
|
1525
|
+
)
|
|
1526
|
+
);
|
|
1376
1527
|
}
|
|
1377
|
-
initSpinner.succeed("Initialized");
|
|
1378
1528
|
} catch (err) {
|
|
1379
|
-
|
|
1529
|
+
deploySpinner.fail("Strategy deploy failed");
|
|
1380
1530
|
console.error(chalk4.red(formatContractError(err)));
|
|
1381
1531
|
process.exit(1);
|
|
1382
1532
|
}
|
|
@@ -1405,10 +1555,10 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
1405
1555
|
console.log(chalk4.dim("Use this address in your proposal batch calls."));
|
|
1406
1556
|
console.log();
|
|
1407
1557
|
});
|
|
1408
|
-
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("--
|
|
1558
|
+
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("--feeds <list>", "Comma-separated Chainlink AggregatorV3 push-feed addresses per token (Portfolio; auto-resolved from the registry when a slot is left empty)").option("--price-decimals <list>", "Comma-separated raw feed decimals per token (Portfolio; default 8). Must equal the feed's live decimals().").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(
|
|
1409
1559
|
"--max-drawdown-bps <bps>",
|
|
1410
1560
|
"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."
|
|
1411
|
-
).option("--
|
|
1561
|
+
).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) => {
|
|
1412
1562
|
const clone = opts.clone;
|
|
1413
1563
|
const vault = opts.vault;
|
|
1414
1564
|
if (!isAddress4(clone)) {
|
|
@@ -1515,7 +1665,7 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
1515
1665
|
let prices = [];
|
|
1516
1666
|
let priceSource = null;
|
|
1517
1667
|
try {
|
|
1518
|
-
const { getTokenPricesInAsset } = await import("./price-
|
|
1668
|
+
const { getTokenPricesInAsset } = await import("./price-EXZDYVDD.js");
|
|
1519
1669
|
prices = await getTokenPricesInAsset({
|
|
1520
1670
|
tokens: allocations.map((a, i) => ({
|
|
1521
1671
|
token: a.token,
|
|
@@ -1666,7 +1816,7 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
1666
1816
|
if (stateStr === "Executed") {
|
|
1667
1817
|
console.log();
|
|
1668
1818
|
console.log(chalk4.dim("Rebalance: sherwood strategy rebalance " + clone));
|
|
1669
|
-
console.log(chalk4.dim("
|
|
1819
|
+
console.log(chalk4.dim("Tighten slippage first: sherwood strategy rebalance " + clone + " --max-slippage 200"));
|
|
1670
1820
|
}
|
|
1671
1821
|
console.log();
|
|
1672
1822
|
} catch (err) {
|
|
@@ -1675,7 +1825,7 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
1675
1825
|
process.exit(1);
|
|
1676
1826
|
}
|
|
1677
1827
|
});
|
|
1678
|
-
strategy2.command("rebalance").description("Rebalance a portfolio strategy \u2014
|
|
1828
|
+
strategy2.command("rebalance").description("Rebalance a portfolio strategy \u2014 trade each allocation back to its target weight (Portfolio only)").argument("<clone>", "Strategy clone address").option("--max-slippage <bps>", "Tighten max slippage bps before rebalancing (weights and routes are frozen at init \u2014 only a LOWER tolerance is accepted)").option("--dry-run", "Show what would happen without executing").action(async (cloneArg, opts) => {
|
|
1679
1829
|
if (!isAddress4(cloneArg)) {
|
|
1680
1830
|
console.error(chalk4.red("Invalid clone address"));
|
|
1681
1831
|
process.exit(1);
|
|
@@ -1723,50 +1873,29 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
1723
1873
|
` ${chalk4.bold(symbol.padEnd(8))} ${weightPct.padEnd(8)} bal: ${formatUnits5(balance, decimals)}`
|
|
1724
1874
|
);
|
|
1725
1875
|
}
|
|
1726
|
-
|
|
1727
|
-
if (
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
console.error(chalk4.red(`--new-weights must have ${allocations.length} values (one per token)`));
|
|
1731
|
-
process.exit(1);
|
|
1732
|
-
}
|
|
1733
|
-
const weightSum = newWeightsBps.reduce((a, b) => a + b, 0);
|
|
1734
|
-
if (weightSum !== 1e4) {
|
|
1735
|
-
console.error(chalk4.red(`Weights must sum to 10000, got ${weightSum}`));
|
|
1736
|
-
process.exit(1);
|
|
1737
|
-
}
|
|
1738
|
-
console.log();
|
|
1739
|
-
console.log(chalk4.bold("New Target Weights"));
|
|
1740
|
-
console.log(chalk4.dim("\u2500".repeat(50)));
|
|
1741
|
-
for (let i = 0; i < allocations.length; i++) {
|
|
1742
|
-
const [, symbol] = tokenData[i];
|
|
1743
|
-
const oldPct = (Number(allocations[i].targetWeightBps) / 100).toFixed(1);
|
|
1744
|
-
const newPct = (newWeightsBps[i] / 100).toFixed(1);
|
|
1745
|
-
const arrow = oldPct !== newPct ? chalk4.yellow("\u2192") : chalk4.dim("\u2192");
|
|
1746
|
-
console.log(` ${chalk4.bold(symbol.padEnd(8))} ${oldPct}% ${arrow} ${newPct}%`);
|
|
1747
|
-
}
|
|
1876
|
+
const newMaxSlippageBps = opts.maxSlippage ? Number(opts.maxSlippage) : void 0;
|
|
1877
|
+
if (newMaxSlippageBps !== void 0 && (!Number.isInteger(newMaxSlippageBps) || newMaxSlippageBps <= 0)) {
|
|
1878
|
+
console.error(chalk4.red(`--max-slippage must be a positive integer bps, got '${opts.maxSlippage}'`));
|
|
1879
|
+
process.exit(1);
|
|
1748
1880
|
}
|
|
1749
1881
|
if (opts.dryRun) {
|
|
1750
1882
|
console.log();
|
|
1751
1883
|
console.log(chalk4.yellow("Dry run \u2014 no transactions sent."));
|
|
1752
1884
|
console.log(chalk4.dim("The rebalance would:"));
|
|
1753
|
-
|
|
1754
|
-
|
|
1885
|
+
if (newMaxSlippageBps !== void 0) {
|
|
1886
|
+
console.log(chalk4.dim(" 0. Tighten max slippage to " + newMaxSlippageBps + " bps"));
|
|
1887
|
+
}
|
|
1888
|
+
console.log(chalk4.dim(" 1. Price every allocation off its bound push feed"));
|
|
1889
|
+
console.log(chalk4.dim(` 2. Trade each slot's drift back to its target weight, via ${assetSymbol}`));
|
|
1755
1890
|
console.log();
|
|
1756
1891
|
return;
|
|
1757
1892
|
}
|
|
1758
|
-
if (
|
|
1759
|
-
const
|
|
1760
|
-
const swapData = await publicClient.readContract({
|
|
1761
|
-
address: clone,
|
|
1762
|
-
abi: PORTFOLIO_STRATEGY_ABI,
|
|
1763
|
-
functionName: "getSwapExtraData"
|
|
1764
|
-
});
|
|
1765
|
-
const updateSpinner = ora3("Updating target weights...").start();
|
|
1893
|
+
if (newMaxSlippageBps !== void 0) {
|
|
1894
|
+
const updateSpinner = ora3("Tightening max slippage...").start();
|
|
1766
1895
|
try {
|
|
1767
1896
|
const innerData = encodeAbiParameters3(
|
|
1768
1897
|
[{ type: "uint256[]" }, { type: "uint256" }, { type: "bytes[]" }],
|
|
1769
|
-
[
|
|
1898
|
+
[[], BigInt(newMaxSlippageBps), []]
|
|
1770
1899
|
);
|
|
1771
1900
|
const updateHash = await writeContractWithRetry({
|
|
1772
1901
|
account,
|
|
@@ -1778,25 +1907,25 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
1778
1907
|
});
|
|
1779
1908
|
const receipt = await waitForReceipt(updateHash);
|
|
1780
1909
|
if (receipt.status === "reverted") throw new Error("updateParams reverted");
|
|
1781
|
-
updateSpinner.succeed(
|
|
1910
|
+
updateSpinner.succeed(`Max slippage tightened to ${newMaxSlippageBps} bps`);
|
|
1782
1911
|
console.log(chalk4.dim(` Tx: ${getExplorerUrl(updateHash)}`));
|
|
1783
1912
|
} catch (err) {
|
|
1784
|
-
updateSpinner.fail("Failed to
|
|
1913
|
+
updateSpinner.fail("Failed to tighten slippage");
|
|
1785
1914
|
console.error(chalk4.red(formatContractError(err)));
|
|
1786
1915
|
process.exit(1);
|
|
1787
1916
|
}
|
|
1788
1917
|
}
|
|
1789
|
-
const rebalanceSpinner = ora3("Rebalancing portfolio (
|
|
1918
|
+
const rebalanceSpinner = ora3("Rebalancing portfolio (trading each slot's drift)...").start();
|
|
1790
1919
|
try {
|
|
1791
1920
|
const rebalanceHash = await writeContractWithRetry({
|
|
1792
1921
|
account,
|
|
1793
1922
|
chain,
|
|
1794
1923
|
address: clone,
|
|
1795
1924
|
abi: PORTFOLIO_STRATEGY_ABI,
|
|
1796
|
-
functionName: "
|
|
1925
|
+
functionName: "rebalanceDelta"
|
|
1797
1926
|
});
|
|
1798
1927
|
const receipt = await waitForReceipt(rebalanceHash);
|
|
1799
|
-
if (receipt.status === "reverted") throw new Error("
|
|
1928
|
+
if (receipt.status === "reverted") throw new Error("rebalanceDelta() reverted on-chain");
|
|
1800
1929
|
rebalanceSpinner.succeed("Portfolio rebalanced");
|
|
1801
1930
|
console.log(chalk4.dim(` Tx: ${getExplorerUrl(rebalanceHash)}`));
|
|
1802
1931
|
} catch (err) {
|
|
@@ -1945,7 +2074,7 @@ function registerVeniceCommands(program2) {
|
|
|
1945
2074
|
console.log(chalk5.dim(` Key: ${apiKey.slice(0, 8)}...${apiKey.slice(-4)}`));
|
|
1946
2075
|
console.log(chalk5.dim(" Saved to ~/.sherwood/config.json"));
|
|
1947
2076
|
try {
|
|
1948
|
-
const { createVeniceProvisionAttestation, getEasScanUrl } = await import("./eas-
|
|
2077
|
+
const { createVeniceProvisionAttestation, getEasScanUrl } = await import("./eas-DV3Z7AT3.js");
|
|
1949
2078
|
const attestation = await createVeniceProvisionAttestation(account.address);
|
|
1950
2079
|
if (attestation) {
|
|
1951
2080
|
console.log(chalk5.dim(` Attested: ${getEasScanUrl(attestation.uid)}`));
|
|
@@ -2301,10 +2430,10 @@ ${opts.prompt}`;
|
|
|
2301
2430
|
console.log(chalk5.dim(`Model: ${result.model} | Tokens: ${result.usage.promptTokens} in, ${result.usage.completionTokens} out, ${result.usage.totalTokens} total`));
|
|
2302
2431
|
}
|
|
2303
2432
|
try {
|
|
2304
|
-
const { createVeniceInferenceAttestation, getEasScanUrl } = await import("./eas-
|
|
2433
|
+
const { createVeniceInferenceAttestation, getEasScanUrl } = await import("./eas-DV3Z7AT3.js");
|
|
2305
2434
|
const { keccak256: keccak2562, toHex, isAddress: isAddr } = await import("viem");
|
|
2306
2435
|
const { getChainContracts: getChainContracts2 } = await import("./config-ZGFS4I7K.js");
|
|
2307
|
-
const { getChain: getActiveChain } = await import("./network-
|
|
2436
|
+
const { getChain: getActiveChain } = await import("./network-7445KSCF.js");
|
|
2308
2437
|
const vaultRecipient = opts.vault && isAddr(opts.vault) ? opts.vault : getChainContracts2(getActiveChain().id).vault;
|
|
2309
2438
|
const promptHash = keccak2562(toHex(userContent)).slice(0, 18);
|
|
2310
2439
|
const attestation = await createVeniceInferenceAttestation(
|
|
@@ -2602,7 +2731,7 @@ function registerAllowanceCommands(program2) {
|
|
|
2602
2731
|
}
|
|
2603
2732
|
|
|
2604
2733
|
// src/commands/proposal.ts
|
|
2605
|
-
import { isAddress as isAddress7, encodeFunctionData as encodeFunctionData3, parseUnits as parseUnits8 } from "viem";
|
|
2734
|
+
import { isAddress as isAddress7, encodeFunctionData as encodeFunctionData3, formatUnits as formatUnits8, parseUnits as parseUnits8 } from "viem";
|
|
2606
2735
|
import chalk8 from "chalk";
|
|
2607
2736
|
import ora6 from "ora";
|
|
2608
2737
|
import { readFileSync as readFileSync2 } from "fs";
|
|
@@ -3300,8 +3429,8 @@ function printSimulationResult(result, callType) {
|
|
|
3300
3429
|
}
|
|
3301
3430
|
async function sendSimulationAlert(subdomain, proposalId, vault, execResult, settleResult) {
|
|
3302
3431
|
try {
|
|
3303
|
-
const xmtp = await import("./xmtp-
|
|
3304
|
-
const { getAccount: getAccount2 } = await import("./client-
|
|
3432
|
+
const xmtp = await import("./xmtp-72IRK7JL.js");
|
|
3433
|
+
const { getAccount: getAccount2 } = await import("./client-PYFE6YQR.js");
|
|
3305
3434
|
const group = await xmtp.getGroup("", subdomain);
|
|
3306
3435
|
const allRisks = [...execResult.risks, ...settleResult?.risks ?? []];
|
|
3307
3436
|
const criticals = allRisks.filter((r) => r.level === "critical");
|
|
@@ -3410,47 +3539,12 @@ function parseCallsFile(path) {
|
|
|
3410
3539
|
value: BigInt(c.value || "0")
|
|
3411
3540
|
}));
|
|
3412
3541
|
}
|
|
3413
|
-
function
|
|
3414
|
-
const
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
console.error(chalk8.red(" sandbox calls file must be a JSON array of {target, data} objects"));
|
|
3418
|
-
process.exit(1);
|
|
3542
|
+
function parseWoodAmount(raw, flag) {
|
|
3543
|
+
const s = String(raw ?? "").trim();
|
|
3544
|
+
if (!/^\d+(\.\d+)?$/.test(s)) {
|
|
3545
|
+
throw new Error(`${flag} must be a non-negative decimal WOOD amount, got "${s}".`);
|
|
3419
3546
|
}
|
|
3420
|
-
return
|
|
3421
|
-
if (!c || typeof c !== "object") {
|
|
3422
|
-
console.error(chalk8.red(` sandbox.calls[${i}] must be an object`));
|
|
3423
|
-
process.exit(1);
|
|
3424
|
-
}
|
|
3425
|
-
const rec = c;
|
|
3426
|
-
if (rec.value !== void 0 && rec.value !== null) {
|
|
3427
|
-
console.error(
|
|
3428
|
-
chalk8.red(
|
|
3429
|
-
` sandbox.calls[${i}] must be (address,bytes) only \u2014 ICallSandbox.Call has no value field.`
|
|
3430
|
-
)
|
|
3431
|
-
);
|
|
3432
|
-
process.exit(1);
|
|
3433
|
-
}
|
|
3434
|
-
if (typeof rec.target !== "string" || !isAddress7(rec.target)) {
|
|
3435
|
-
console.error(chalk8.red(` sandbox.calls[${i}].target must be a 0x-prefixed address`));
|
|
3436
|
-
process.exit(1);
|
|
3437
|
-
}
|
|
3438
|
-
if (typeof rec.data !== "string" || !/^0x[0-9a-fA-F]*$/.test(rec.data)) {
|
|
3439
|
-
console.error(chalk8.red(` sandbox.calls[${i}].data must be a 0x-prefixed hex string`));
|
|
3440
|
-
process.exit(1);
|
|
3441
|
-
}
|
|
3442
|
-
return { target: rec.target, data: rec.data };
|
|
3443
|
-
});
|
|
3444
|
-
}
|
|
3445
|
-
function parseSandboxTokens(list) {
|
|
3446
|
-
if (list === void 0 || list.trim().length === 0) return [];
|
|
3447
|
-
return list.split(",").map((x) => x.trim()).filter((x) => x.length > 0).map((a, i) => {
|
|
3448
|
-
if (!isAddress7(a)) {
|
|
3449
|
-
console.error(chalk8.red(` --sandbox-tokens[${i}] is not a 0x-prefixed address: ${a}`));
|
|
3450
|
-
process.exit(1);
|
|
3451
|
-
}
|
|
3452
|
-
return a;
|
|
3453
|
-
});
|
|
3547
|
+
return parseUnits8(s, 18);
|
|
3454
3548
|
}
|
|
3455
3549
|
var GUARDIAN_REGISTRY_ABI = [
|
|
3456
3550
|
{
|
|
@@ -3488,7 +3582,10 @@ var GUARDIAN_REGISTRY_ABI = [
|
|
|
3488
3582
|
inputs: [
|
|
3489
3583
|
{ name: "governor", type: "address" },
|
|
3490
3584
|
{ name: "proposalId", type: "uint256" },
|
|
3491
|
-
{ name: "support", type: "uint8" }
|
|
3585
|
+
{ name: "support", type: "uint8" },
|
|
3586
|
+
// The approver's declaration: WOOD it puts behind this proposal,
|
|
3587
|
+
// clamped to its free budget by ExposureLedger. Ignored on a Block.
|
|
3588
|
+
{ name: "lockWood", type: "uint256" }
|
|
3492
3589
|
],
|
|
3493
3590
|
outputs: [],
|
|
3494
3591
|
stateMutability: "nonpayable"
|
|
@@ -3746,171 +3843,6 @@ function registerProposalCommands(program2) {
|
|
|
3746
3843
|
process.exit(1);
|
|
3747
3844
|
}
|
|
3748
3845
|
});
|
|
3749
|
-
proposal.command("create-sandbox").description("Submit a strategy proposal with a CallSandbox payload (ICallSandbox.Call is (address,bytes) only \u2014 no value)").requiredOption("--vault <address>", "Vault address the proposal targets").requiredOption("--name <name>", "Strategy name").requiredOption("--description <text>", "Strategy rationale and risk summary").requiredOption("--duration <duration>", "Strategy duration (e.g. 7d, 24h, 3600)").requiredOption("--execute-calls <path>", "Path to JSON file with execute Call[] array").requiredOption("--settle-calls <path>", "Path to JSON file with settlement Call[] array").requiredOption(
|
|
3750
|
-
"--sandbox-calls <path>",
|
|
3751
|
-
"Path to JSON file of ICallSandbox.Call[] \u2014 [{target, data}, ...]. A value field is refused."
|
|
3752
|
-
).requiredOption(
|
|
3753
|
-
"--sandbox-funding <amount>",
|
|
3754
|
-
"Vault-asset amount the sandbox is funded with (decimals resolved from the asset). Must be > 0 and <= --max-capital."
|
|
3755
|
-
).option("--sandbox-tokens <list>", "Comma-separated declared non-asset token addresses the payload may hold").option("--strategy <address>", "Live-NAV strategy clone address (default: none \u2014 queue-only)").option(
|
|
3756
|
-
"--max-capital <amount>",
|
|
3757
|
-
"REQUIRED. Ceiling on what the execute batch may deploy, in vault-asset units."
|
|
3758
|
-
).option(
|
|
3759
|
-
"--max-drawdown-bps <bps>",
|
|
3760
|
-
"Declared loss allowance in bps of --max-capital (0-10000, default 2000 = 20%)."
|
|
3761
|
-
).option("--execute-caps <list>", "Comma-separated per-execute-call outflow caps in asset units").option("--settle-caps <list>", "Comma-separated per-settlement-call outflow caps in asset units").option("--metadata-uri <uri>", "Override \u2014 skip IPFS upload and use this URI directly").action(async (opts) => {
|
|
3762
|
-
try {
|
|
3763
|
-
const vault = opts.vault;
|
|
3764
|
-
if (!isAddress7(vault)) {
|
|
3765
|
-
console.error(chalk8.red(`Invalid vault address: ${opts.vault}`));
|
|
3766
|
-
process.exit(1);
|
|
3767
|
-
}
|
|
3768
|
-
const strategyDuration = parseDuration(opts.duration);
|
|
3769
|
-
const executeCalls = parseCallsFile(opts.executeCalls);
|
|
3770
|
-
const settleCalls = parseCallsFile(opts.settleCalls);
|
|
3771
|
-
const sandboxCalls = parseSandboxCallsFile(opts.sandboxCalls);
|
|
3772
|
-
const declaredTokens = parseSandboxTokens(opts.sandboxTokens);
|
|
3773
|
-
const ZERO_STRATEGY = "0x0000000000000000000000000000000000000000";
|
|
3774
|
-
let strategy2 = ZERO_STRATEGY;
|
|
3775
|
-
if (opts.strategy) {
|
|
3776
|
-
if (!isAddress7(opts.strategy)) {
|
|
3777
|
-
console.error(chalk8.red(`Invalid strategy address: ${opts.strategy}`));
|
|
3778
|
-
process.exit(1);
|
|
3779
|
-
}
|
|
3780
|
-
strategy2 = opts.strategy;
|
|
3781
|
-
}
|
|
3782
|
-
const envelope = await resolveRiskEnvelope(opts, vault);
|
|
3783
|
-
const asset = await getAssetAddress();
|
|
3784
|
-
const decimals = await getPublicClient().readContract({
|
|
3785
|
-
address: asset,
|
|
3786
|
-
abi: ERC20_ABI,
|
|
3787
|
-
functionName: "decimals"
|
|
3788
|
-
});
|
|
3789
|
-
const funding = parseUnits8(String(opts.sandboxFunding), decimals);
|
|
3790
|
-
const executeCallCaps = resolveCaps(
|
|
3791
|
-
opts.executeCaps,
|
|
3792
|
-
executeCalls.length,
|
|
3793
|
-
envelope.maxCapital,
|
|
3794
|
-
"--execute-caps"
|
|
3795
|
-
);
|
|
3796
|
-
const settleCallCaps = resolveCaps(
|
|
3797
|
-
opts.settleCaps,
|
|
3798
|
-
settleCalls.length,
|
|
3799
|
-
0n,
|
|
3800
|
-
"--settle-caps"
|
|
3801
|
-
);
|
|
3802
|
-
const buildAction = async (metadataURI2) => encodeProposeWithSandbox(
|
|
3803
|
-
{
|
|
3804
|
-
vault,
|
|
3805
|
-
governor: await getGovernorOf(vault),
|
|
3806
|
-
strategy: strategy2,
|
|
3807
|
-
metadataURI: metadataURI2,
|
|
3808
|
-
strategyDuration,
|
|
3809
|
-
envelope,
|
|
3810
|
-
executeCalls,
|
|
3811
|
-
executeCallCaps,
|
|
3812
|
-
settlementCalls: settleCalls,
|
|
3813
|
-
settlementCallCaps: settleCallCaps,
|
|
3814
|
-
sandbox: { funding, calls: sandboxCalls, declaredTokens }
|
|
3815
|
-
},
|
|
3816
|
-
getChain().id
|
|
3817
|
-
);
|
|
3818
|
-
if (isCalldataOnly()) {
|
|
3819
|
-
if (!opts.metadataUri) {
|
|
3820
|
-
console.error(
|
|
3821
|
-
chalk8.red(
|
|
3822
|
-
"--calldata-only requires --metadata-uri (IPFS metadata upload needs a signing wallet)"
|
|
3823
|
-
)
|
|
3824
|
-
);
|
|
3825
|
-
process.exit(1);
|
|
3826
|
-
}
|
|
3827
|
-
try {
|
|
3828
|
-
emitCalldata(await buildAction(opts.metadataUri));
|
|
3829
|
-
} catch (err) {
|
|
3830
|
-
console.error(chalk8.red(err instanceof Error ? err.message : String(err)));
|
|
3831
|
-
process.exit(1);
|
|
3832
|
-
}
|
|
3833
|
-
return;
|
|
3834
|
-
}
|
|
3835
|
-
let metadataURI = opts.metadataUri || "";
|
|
3836
|
-
if (!metadataURI) {
|
|
3837
|
-
const spinner2 = ora6({ text: W3("Uploading metadata to IPFS..."), color: "green" }).start();
|
|
3838
|
-
try {
|
|
3839
|
-
const account = getAccount();
|
|
3840
|
-
const metadata = {
|
|
3841
|
-
schema: "sherwood/proposal/v1",
|
|
3842
|
-
name: opts.name,
|
|
3843
|
-
description: opts.description,
|
|
3844
|
-
chain: getNetwork(),
|
|
3845
|
-
strategies: [],
|
|
3846
|
-
terms: {},
|
|
3847
|
-
links: {}
|
|
3848
|
-
};
|
|
3849
|
-
const proposalMeta = {
|
|
3850
|
-
...metadata,
|
|
3851
|
-
proposer: account.address,
|
|
3852
|
-
vault,
|
|
3853
|
-
strategyDuration: Number(strategyDuration),
|
|
3854
|
-
sandbox: true,
|
|
3855
|
-
createdAt: Math.floor(Date.now() / 1e3)
|
|
3856
|
-
};
|
|
3857
|
-
metadataURI = await uploadMetadata(proposalMeta);
|
|
3858
|
-
spinner2.succeed(G3(`Metadata pinned: ${DIM3(metadataURI)}`));
|
|
3859
|
-
} catch (err) {
|
|
3860
|
-
spinner2.warn(chalk8.yellow("IPFS upload failed \u2014 using inline metadata"));
|
|
3861
|
-
const json = JSON.stringify({ name: opts.name, description: opts.description, vault, sandbox: true });
|
|
3862
|
-
metadataURI = `data:application/json;base64,${Buffer.from(json).toString("base64")}`;
|
|
3863
|
-
}
|
|
3864
|
-
}
|
|
3865
|
-
console.log();
|
|
3866
|
-
console.log(LABEL3(" \u25C6 Sandbox Proposal Summary"));
|
|
3867
|
-
SEP3();
|
|
3868
|
-
console.log(W3(` Name: ${BOLD3(opts.name)}`));
|
|
3869
|
-
console.log(W3(` Vault: ${G3(vault)}`));
|
|
3870
|
-
console.log(W3(` Duration: ${formatDurationShort(strategyDuration)}`));
|
|
3871
|
-
console.log(W3(` Calls: ${executeCalls.length} execute + ${settleCalls.length} settle + ${sandboxCalls.length} sandbox`));
|
|
3872
|
-
console.log(W3(` Sandbox funding: ${opts.sandboxFunding}`));
|
|
3873
|
-
console.log(W3(` Metadata: ${DIM3(metadataURI.length > 50 ? metadataURI.slice(0, 50) + "..." : metadataURI)}`));
|
|
3874
|
-
SEP3();
|
|
3875
|
-
const spinner = ora6({ text: W3("Submitting sandbox proposal..."), color: "green" }).start();
|
|
3876
|
-
let action;
|
|
3877
|
-
try {
|
|
3878
|
-
action = await buildAction(metadataURI);
|
|
3879
|
-
} catch (err) {
|
|
3880
|
-
spinner.fail(err instanceof Error ? err.message : String(err));
|
|
3881
|
-
process.exit(1);
|
|
3882
|
-
}
|
|
3883
|
-
const tx = action.txs[0];
|
|
3884
|
-
const hash = await sendTxWithRetry({
|
|
3885
|
-
account: getAccount(),
|
|
3886
|
-
chain: getChain(),
|
|
3887
|
-
to: tx.to,
|
|
3888
|
-
data: tx.data,
|
|
3889
|
-
value: 0n
|
|
3890
|
-
});
|
|
3891
|
-
const receipt = await waitForReceipt(hash);
|
|
3892
|
-
let proposalId = parseProposalIdFromLogs(receipt.logs);
|
|
3893
|
-
if (proposalId === void 0) {
|
|
3894
|
-
try {
|
|
3895
|
-
proposalId = await proposalCount(vault);
|
|
3896
|
-
} catch {
|
|
3897
|
-
proposalId = 0n;
|
|
3898
|
-
}
|
|
3899
|
-
}
|
|
3900
|
-
spinner.succeed(G3("Sandbox proposal submitted"));
|
|
3901
|
-
console.log();
|
|
3902
|
-
console.log(LABEL3(" \u25C6 Sandbox Proposal Created"));
|
|
3903
|
-
SEP3();
|
|
3904
|
-
console.log(W3(` Proposal ID: ${G3(`#${proposalId}`)}`));
|
|
3905
|
-
console.log(W3(` Tx: ${DIM3(getExplorerUrl(receipt.transactionHash))}`));
|
|
3906
|
-
SEP3();
|
|
3907
|
-
console.log();
|
|
3908
|
-
} catch (err) {
|
|
3909
|
-
console.error(chalk8.red(`
|
|
3910
|
-
\u2716 ${formatContractError(err)}`));
|
|
3911
|
-
process.exit(1);
|
|
3912
|
-
}
|
|
3913
|
-
});
|
|
3914
3846
|
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) => {
|
|
3915
3847
|
const spinner = ora6("Loading proposals...").start();
|
|
3916
3848
|
try {
|
|
@@ -3943,7 +3875,7 @@ function registerProposalCommands(program2) {
|
|
|
3943
3875
|
console.log(BOLD3(`Proposals (${proposals.length})`));
|
|
3944
3876
|
console.log(DIM3("\u2500".repeat(90)));
|
|
3945
3877
|
console.log(
|
|
3946
|
-
DIM3(" ID ") + DIM3("Agent".padEnd(14)) + DIM3("State".padEnd(12)) + DIM3("Fee".padEnd(8)) + DIM3("
|
|
3878
|
+
DIM3(" ID ") + DIM3("Agent".padEnd(14)) + DIM3("State".padEnd(12)) + DIM3("Fee".padEnd(8)) + DIM3("Against/Votable".padEnd(22)) + DIM3("Duration".padEnd(10)) + DIM3("Created")
|
|
3947
3879
|
);
|
|
3948
3880
|
console.log(DIM3("\u2500".repeat(90)));
|
|
3949
3881
|
for (const p of proposals) {
|
|
@@ -3951,7 +3883,7 @@ function registerProposalCommands(program2) {
|
|
|
3951
3883
|
const created = p.snapshotTimestamp > 0n ? new Date(Number(p.snapshotTimestamp) * 1e3).toLocaleDateString() : "\u2014";
|
|
3952
3884
|
const agent = `${p.proposer.slice(0, 6)}...${p.proposer.slice(-4)}`;
|
|
3953
3885
|
const dur = formatDurationShort(p.strategyDuration);
|
|
3954
|
-
const votes = `${formatShares(p.
|
|
3886
|
+
const votes = `${formatShares(p.votesAgainst)}/${formatShares(p.votableSupply)}`;
|
|
3955
3887
|
const fee = `${Number(p.performanceFeeBps) / 100}%`;
|
|
3956
3888
|
console.log(
|
|
3957
3889
|
` ${String(p.id).padEnd(4)}${agent.padEnd(14)}${state.padEnd(12)}${fee.padEnd(8)}${votes.padEnd(22)}${dur.padEnd(10)}${created}`
|
|
@@ -3977,11 +3909,9 @@ function registerProposalCommands(program2) {
|
|
|
3977
3909
|
}
|
|
3978
3910
|
const p = await getProposal(vault, id);
|
|
3979
3911
|
const state = await getProposalState(vault, id);
|
|
3980
|
-
const params = await getGovernorParams(vault);
|
|
3981
3912
|
spinner.stop();
|
|
3982
3913
|
const stateLabel = PROPOSAL_STATES[state] || "Unknown";
|
|
3983
|
-
const
|
|
3984
|
-
const quorumNeeded = totalVotes > 0n ? `${Number(params.vetoThresholdBps) / 100}%` : "\u2014";
|
|
3914
|
+
const vetoBar = p.votableSupply > 0n ? p.votableSupply * p.vetoThresholdBps / 10000n || 1n : null;
|
|
3985
3915
|
console.log();
|
|
3986
3916
|
console.log(LABEL3(` \u25C6 Proposal #${p.id}`));
|
|
3987
3917
|
SEP3();
|
|
@@ -4009,10 +3939,11 @@ function registerProposalCommands(program2) {
|
|
|
4009
3939
|
console.log(W3(` Executed At: ${formatTimestamp(p.executedAt)}`));
|
|
4010
3940
|
console.log();
|
|
4011
3941
|
console.log(LABEL3(" Votes"));
|
|
4012
|
-
console.log(W3(` For: ${formatShares(p.votesFor)}`));
|
|
4013
3942
|
console.log(W3(` Against: ${formatShares(p.votesAgainst)}`));
|
|
4014
|
-
console.log(W3(`
|
|
4015
|
-
console.log(W3(
|
|
3943
|
+
console.log(W3(` Votable Supply: ${formatShares(p.votableSupply)}`));
|
|
3944
|
+
console.log(W3(
|
|
3945
|
+
` Veto Threshold: ${vetoBar === null ? DIM3("\u2014 (no votable supply \u2014 veto check skipped)") : `${Number(p.vetoThresholdBps) / 100}% (${formatShares(vetoBar)})`}`
|
|
3946
|
+
));
|
|
4016
3947
|
if (state === PROPOSAL_STATE.Executed || state === PROPOSAL_STATE.Settled) {
|
|
4017
3948
|
try {
|
|
4018
3949
|
const cap = await getCapitalSnapshot(vault, id);
|
|
@@ -4099,15 +4030,19 @@ function registerProposalCommands(program2) {
|
|
|
4099
4030
|
process.exit(1);
|
|
4100
4031
|
}
|
|
4101
4032
|
});
|
|
4102
|
-
proposal.command("review-vote").description("Cast a guardian review vote (Approve or Block) on a live review").requiredOption("--id <proposalId>", "Proposal ID").requiredOption("--vault <address>", "Vault the proposal belongs to (per-vault governor)").requiredOption("--support <approve|block>", "Guardian review vote: approve or block").
|
|
4033
|
+
proposal.command("review-vote").description("Cast a guardian review vote (Approve or Block) on a live review").requiredOption("--id <proposalId>", "Proposal ID").requiredOption("--vault <address>", "Vault the proposal belongs to (per-vault governor)").requiredOption("--support <approve|block>", "Guardian review vote: approve or block").requiredOption(
|
|
4034
|
+
"--lock-wood <wood>",
|
|
4035
|
+
"WOOD you declare behind an Approve (decimal WOOD). Clamped to your free budget by ExposureLedger; ignored on a Block."
|
|
4036
|
+
).action(async (opts) => {
|
|
4103
4037
|
try {
|
|
4104
4038
|
const proposalId = parseBigIntArg(opts.id, "proposal ID");
|
|
4105
4039
|
const vault = parseVaultOption(opts.vault);
|
|
4106
4040
|
const vote2 = parseGuardianReviewVote(String(opts.support));
|
|
4041
|
+
const lockWood = parseWoodAmount(opts.lockWood, "--lock-wood");
|
|
4107
4042
|
const chainId = getChain().id;
|
|
4108
4043
|
const governor = await getGovernorOf(vault);
|
|
4109
4044
|
if (isCalldataOnly()) {
|
|
4110
|
-
emitCalldata(encodeVoteOnProposal({ governor, proposalId, vote: vote2 }, chainId));
|
|
4045
|
+
emitCalldata(encodeVoteOnProposal({ governor, proposalId, vote: vote2, lockWood }, chainId));
|
|
4111
4046
|
return;
|
|
4112
4047
|
}
|
|
4113
4048
|
const account = getAccount();
|
|
@@ -4168,6 +4103,9 @@ function registerProposalCommands(program2) {
|
|
|
4168
4103
|
SEP3();
|
|
4169
4104
|
if (vote2 === "Approve") {
|
|
4170
4105
|
console.log(chalk8.yellow.bold(" \u26A0 UNDERWRITING"));
|
|
4106
|
+
console.log(chalk8.yellow(
|
|
4107
|
+
` Declared lock: ${formatUnits8(lockWood, 18)} WOOD`
|
|
4108
|
+
));
|
|
4171
4109
|
console.log(chalk8.yellow(
|
|
4172
4110
|
" Approve books this proposal's coverage against YOUR own slashable"
|
|
4173
4111
|
));
|
|
@@ -4185,7 +4123,7 @@ function registerProposalCommands(program2) {
|
|
|
4185
4123
|
));
|
|
4186
4124
|
SEP3();
|
|
4187
4125
|
}
|
|
4188
|
-
const action = encodeVoteOnProposal({ governor, proposalId, vote: vote2 }, chainId);
|
|
4126
|
+
const action = encodeVoteOnProposal({ governor, proposalId, vote: vote2, lockWood }, chainId);
|
|
4189
4127
|
const tx = action.txs[0];
|
|
4190
4128
|
const voteSpinner = ora6({ text: W3("Submitting review vote..."), color: "green" }).start();
|
|
4191
4129
|
const hash = await sendTxWithRetry({
|
|
@@ -4776,6 +4714,65 @@ function registerProposalCommands(program2) {
|
|
|
4776
4714
|
process.exit(1);
|
|
4777
4715
|
}
|
|
4778
4716
|
});
|
|
4717
|
+
proposal.command("reclaim-bond").description("Reclaim the proposer bond of a terminal proposal (cancelled / rejected / expired / settled)").requiredOption("--id <proposalId>", "Proposal ID").requiredOption("--vault <address>", "Vault the proposal belongs to (per-vault governor)").action(async (opts) => {
|
|
4718
|
+
try {
|
|
4719
|
+
const proposalId = parseBigIntArg(opts.id, "proposal ID");
|
|
4720
|
+
const vault = parseVaultOption(opts.vault);
|
|
4721
|
+
if (isCalldataOnly()) {
|
|
4722
|
+
const governor = await getGovernorOf(vault);
|
|
4723
|
+
emitCalldata({
|
|
4724
|
+
txs: [
|
|
4725
|
+
{
|
|
4726
|
+
to: governor,
|
|
4727
|
+
data: encodeFunctionData3({
|
|
4728
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
4729
|
+
functionName: "reclaimProposerBond",
|
|
4730
|
+
args: [proposalId]
|
|
4731
|
+
}),
|
|
4732
|
+
value: "0x0",
|
|
4733
|
+
chainId: getChain().id
|
|
4734
|
+
}
|
|
4735
|
+
],
|
|
4736
|
+
preconditions: [],
|
|
4737
|
+
description: `Release proposal #${proposalId}'s proposer bond from escrow back to the proposer.`,
|
|
4738
|
+
note: "Permissionless \u2014 any wallet may broadcast it; the WOOD goes to whoever posted the bond. The proposal must be Rejected / Expired / Cancelled / Settled, and for an EXECUTED proposal the challenge window (executedAt + strategyDuration + challengeWindow) must have closed."
|
|
4739
|
+
});
|
|
4740
|
+
return;
|
|
4741
|
+
}
|
|
4742
|
+
const spinner = ora6("Loading proposal...").start();
|
|
4743
|
+
const [p, state] = await Promise.all([
|
|
4744
|
+
getProposal(vault, proposalId),
|
|
4745
|
+
getProposalState(vault, proposalId)
|
|
4746
|
+
]);
|
|
4747
|
+
const TERMINAL = /* @__PURE__ */ new Set([
|
|
4748
|
+
PROPOSAL_STATE.Rejected,
|
|
4749
|
+
PROPOSAL_STATE.Expired,
|
|
4750
|
+
PROPOSAL_STATE.Cancelled,
|
|
4751
|
+
PROPOSAL_STATE.Settled
|
|
4752
|
+
]);
|
|
4753
|
+
if (!TERMINAL.has(state)) {
|
|
4754
|
+
spinner.fail(
|
|
4755
|
+
`Proposal is ${PROPOSAL_STATES[state] || "Unknown"} \u2014 a bond is only reclaimable from Rejected / Expired / Cancelled / Settled.`
|
|
4756
|
+
);
|
|
4757
|
+
process.exit(1);
|
|
4758
|
+
}
|
|
4759
|
+
if (p.proposerBondWood === 0n) {
|
|
4760
|
+
spinner.fail(
|
|
4761
|
+
"This proposal has no proposer bond left to reclaim (never bonded, already reclaimed, or forfeited)."
|
|
4762
|
+
);
|
|
4763
|
+
process.exit(1);
|
|
4764
|
+
}
|
|
4765
|
+
spinner.text = W3(`Reclaiming ${formatUnits8(p.proposerBondWood, 18)} WOOD...`);
|
|
4766
|
+
const hash = await reclaimProposerBond(vault, proposalId);
|
|
4767
|
+
spinner.succeed(G3(`Bond released: ${formatUnits8(p.proposerBondWood, 18)} WOOD \u2192 ${p.proposer}`));
|
|
4768
|
+
console.log(DIM3(` ${getExplorerUrl(hash)}`));
|
|
4769
|
+
console.log();
|
|
4770
|
+
} catch (err) {
|
|
4771
|
+
console.error(chalk8.red(`
|
|
4772
|
+
\u2716 ${formatContractError(err)}`));
|
|
4773
|
+
process.exit(1);
|
|
4774
|
+
}
|
|
4775
|
+
});
|
|
4779
4776
|
proposal.command("simulate").description("Simulate proposal calls via Tenderly (or eth_call fallback)").option("--id <proposalId>", "Proposal ID to simulate").option("--vault <address>", "Vault address (required \u2014 proposal IDs are per-vault)").option("--execute-calls <path>", "Path to JSON execute calls file").option("--settle-calls <path>", "Path to JSON settlement calls file").option("--notify <subdomain>", "Send results to vault XMTP chat").action(async (opts) => {
|
|
4780
4777
|
try {
|
|
4781
4778
|
let vault;
|
|
@@ -4851,7 +4848,7 @@ function registerProposalCommands(program2) {
|
|
|
4851
4848
|
}
|
|
4852
4849
|
|
|
4853
4850
|
// src/commands/queue.ts
|
|
4854
|
-
import { parseUnits as parseUnits9, formatUnits as
|
|
4851
|
+
import { parseUnits as parseUnits9, formatUnits as formatUnits9, isAddress as isAddress8, parseEventLogs } from "viem";
|
|
4855
4852
|
import chalk9 from "chalk";
|
|
4856
4853
|
import ora7 from "ora";
|
|
4857
4854
|
var WETH_ABI = [
|
|
@@ -5040,7 +5037,7 @@ function registerQueueCommands(program2) {
|
|
|
5040
5037
|
);
|
|
5041
5038
|
if (ethBalance < shortfall + minGas) {
|
|
5042
5039
|
throw new Error(
|
|
5043
|
-
`Insufficient ETH on ${getChain().name}. Have ${
|
|
5040
|
+
`Insufficient ETH on ${getChain().name}. Have ${formatUnits9(ethBalance, 18)} ETH, need ${formatUnits9(shortfall, 18)} to wrap + ~${formatUnits9(minGas, 18)} for gas.`
|
|
5044
5041
|
);
|
|
5045
5042
|
}
|
|
5046
5043
|
spinner.text = "Wrapping ETH \u2192 WETH...";
|
|
@@ -8051,9 +8048,9 @@ try {
|
|
|
8051
8048
|
} catch {
|
|
8052
8049
|
}
|
|
8053
8050
|
var require2 = createRequire(import.meta.url);
|
|
8054
|
-
var CLI_VERSION = "0.
|
|
8051
|
+
var CLI_VERSION = "0.87.1";
|
|
8055
8052
|
async function loadXmtp() {
|
|
8056
|
-
return import("./xmtp-
|
|
8053
|
+
return import("./xmtp-72IRK7JL.js");
|
|
8057
8054
|
}
|
|
8058
8055
|
async function loadCron() {
|
|
8059
8056
|
return import("./cron-E3UAZF5B.js");
|
|
@@ -8099,7 +8096,7 @@ program.name("sherwood").description("CLI for agent-managed onchain vaults").ver
|
|
|
8099
8096
|
}).addOption(
|
|
8100
8097
|
new Option(
|
|
8101
8098
|
"--chain <network>",
|
|
8102
|
-
"Target network (default: robinhood-fork, chain 9994663 \u2014 where the product lives; robinhood-testnet is chain 46630)"
|
|
8099
|
+
"Target network (default: robinhood-fork, chain 9994663 \u2014 where the product lives; robinhood-testnet is chain 46630 and still runs a PRE-V1 stack this CLI cannot talk to)"
|
|
8103
8100
|
).choices(VALID_NETWORKS).default(DEFAULT_NETWORK)
|
|
8104
8101
|
).option(
|
|
8105
8102
|
"--calldata-only",
|
|
@@ -8724,7 +8721,7 @@ vaultCmd.command("add").description("Register an agent on a vault (creator only)
|
|
|
8724
8721
|
agentId = BigInt(opts.agentId);
|
|
8725
8722
|
} else {
|
|
8726
8723
|
spinner.text = "Looking up ERC-8004 identity from wallet...";
|
|
8727
|
-
const { lookupAgentIdByWallet } = await import("./identity-
|
|
8724
|
+
const { lookupAgentIdByWallet } = await import("./identity-M4TXM7EX.js");
|
|
8728
8725
|
const resolved = await lookupAgentIdByWallet(agentWallet);
|
|
8729
8726
|
if (resolved === null) {
|
|
8730
8727
|
spinner.fail("Agent wallet does not own an ERC-8004 identity NFT");
|
|
@@ -9408,7 +9405,7 @@ vaultCmd.command("redeem").description("Redeem vault shares for the underlying a
|
|
|
9408
9405
|
`));
|
|
9409
9406
|
process.exit(1);
|
|
9410
9407
|
}
|
|
9411
|
-
const sharesDisplay =
|
|
9408
|
+
const sharesDisplay = formatUnits10(shares, shareDecimals);
|
|
9412
9409
|
const spinner = ora9(`Redeeming ${sharesDisplay} shares...`).start();
|
|
9413
9410
|
try {
|
|
9414
9411
|
const { hash, assetsOut } = await redeem(
|
|
@@ -9417,7 +9414,7 @@ vaultCmd.command("redeem").description("Redeem vault shares for the underlying a
|
|
|
9417
9414
|
);
|
|
9418
9415
|
spinner.succeed(`Redeemed: ${hash}`);
|
|
9419
9416
|
console.log(chalk17.dim(` ${getExplorerUrl(hash)}`));
|
|
9420
|
-
console.log(` Assets received: ${
|
|
9417
|
+
console.log(` Assets received: ${formatUnits10(assetsOut, assetDecimals)}`);
|
|
9421
9418
|
} catch (err) {
|
|
9422
9419
|
spinner.fail("Redeem failed");
|
|
9423
9420
|
console.error(chalk17.red(formatContractError(err)));
|
|
@@ -9427,7 +9424,7 @@ vaultCmd.command("redeem").description("Redeem vault shares for the underlying a
|
|
|
9427
9424
|
var strategy = program.command("strategy").description("Strategy templates \u2014 list, clone, propose");
|
|
9428
9425
|
registerStrategyTemplateCommands(strategy);
|
|
9429
9426
|
program.command("providers").description("List available providers (research + swap routing)").action(async () => {
|
|
9430
|
-
const { listProviders } = await import("./providers-
|
|
9427
|
+
const { listProviders } = await import("./providers-6UZKDFQD.js");
|
|
9431
9428
|
for (const info of listProviders()) {
|
|
9432
9429
|
console.log(`
|
|
9433
9430
|
${info.name} (${info.type})`);
|
|
@@ -9437,7 +9434,7 @@ ${info.name} (${info.type})`);
|
|
|
9437
9434
|
}
|
|
9438
9435
|
});
|
|
9439
9436
|
try {
|
|
9440
|
-
const { registerChatCommands } = await import("./chat-
|
|
9437
|
+
const { registerChatCommands } = await import("./chat-AKLYUJZV.js");
|
|
9441
9438
|
registerChatCommands(program);
|
|
9442
9439
|
} catch {
|
|
9443
9440
|
program.command("chat <name> [action] [actionArgs...]").description("Vault chat (XMTP) \u2014 requires @xmtp/cli").action(() => {
|
|
@@ -9447,7 +9444,7 @@ try {
|
|
|
9447
9444
|
process.exit(1);
|
|
9448
9445
|
});
|
|
9449
9446
|
}
|
|
9450
|
-
var { registerSessionCommands } = await import("./session-
|
|
9447
|
+
var { registerSessionCommands } = await import("./session-45NQAHFI.js");
|
|
9451
9448
|
registerSessionCommands(program);
|
|
9452
9449
|
registerVeniceCommands(program);
|
|
9453
9450
|
registerAllowanceCommands(program);
|
|
@@ -9456,11 +9453,11 @@ registerProposalCommands(program);
|
|
|
9456
9453
|
registerQueueCommands(program);
|
|
9457
9454
|
registerGovernorCommands(program);
|
|
9458
9455
|
registerGuardianCommands(program);
|
|
9459
|
-
var { registerResearchCommands } = await import("./research-
|
|
9456
|
+
var { registerResearchCommands } = await import("./research-44GHYE4H.js");
|
|
9460
9457
|
registerResearchCommands(program);
|
|
9461
|
-
var { registerAgentCommands } = await import("./agent-
|
|
9458
|
+
var { registerAgentCommands } = await import("./agent-FCGZL4GF.js");
|
|
9462
9459
|
registerAgentCommands(program);
|
|
9463
|
-
var { registerTradeCommands } = await import("./trade-
|
|
9460
|
+
var { registerTradeCommands } = await import("./trade-FO52SP62.js");
|
|
9464
9461
|
registerTradeCommands(program);
|
|
9465
9462
|
registerGridCommand(program);
|
|
9466
9463
|
var configCmd = program.command("config");
|