@sherwoodagent/cli 0.7.3 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chat-BSLMAYM5.js → chat-BNYWD3EL.js} +5 -5
- package/dist/{chunk-EDR5AHSC.js → chunk-4MTHXGTK.js} +2 -2
- package/dist/{chunk-QMWMT6EH.js → chunk-5ADWTXNT.js} +115 -31
- package/dist/chunk-5ADWTXNT.js.map +1 -0
- package/dist/{chunk-P3GYAMGZ.js → chunk-HKZONPXW.js} +15 -5
- package/dist/chunk-HKZONPXW.js.map +1 -0
- package/dist/{chunk-6MEYSN2W.js → chunk-OUES74ID.js} +488 -47
- package/dist/chunk-OUES74ID.js.map +1 -0
- package/dist/{chunk-B7XDUFI3.js → chunk-Q37V65B6.js} +7 -7
- package/dist/chunk-Q37V65B6.js.map +1 -0
- package/dist/{eas-TMHFTX43.js → eas-EL3XCEXQ.js} +4 -4
- package/dist/index.js +1077 -228
- package/dist/index.js.map +1 -1
- package/dist/{research-ILKLSHVP.js → research-57SKO27M.js} +5 -5
- package/dist/{research-5QALNYVS.js → research-ZR7HXITG.js} +3 -3
- package/dist/{session-XUOMZWOT.js → session-QQSHCGNK.js} +7 -7
- package/dist/{session-XUOMZWOT.js.map → session-QQSHCGNK.js.map} +1 -1
- package/dist/{xmtp-4XTQQ7RE.js → xmtp-S4VRXMFK.js} +6 -6
- package/dist/xmtp-S4VRXMFK.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-6MEYSN2W.js.map +0 -1
- package/dist/chunk-B7XDUFI3.js.map +0 -1
- package/dist/chunk-P3GYAMGZ.js.map +0 -1
- package/dist/chunk-QMWMT6EH.js.map +0 -1
- package/dist/xmtp-4XTQQ7RE.js.map +0 -1
- /package/dist/{chat-BSLMAYM5.js.map → chat-BNYWD3EL.js.map} +0 -0
- /package/dist/{chunk-EDR5AHSC.js.map → chunk-4MTHXGTK.js.map} +0 -0
- /package/dist/{eas-TMHFTX43.js.map → eas-EL3XCEXQ.js.map} +0 -0
- /package/dist/{research-ILKLSHVP.js.map → research-57SKO27M.js.map} +0 -0
- /package/dist/{research-5QALNYVS.js.map → research-ZR7HXITG.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
fetchMetadata,
|
|
3
4
|
uploadMetadata
|
|
4
5
|
} from "./chunk-DVWORPEY.js";
|
|
5
6
|
import {
|
|
@@ -9,7 +10,7 @@ import {
|
|
|
9
10
|
queryApprovals,
|
|
10
11
|
queryJoinRequests,
|
|
11
12
|
revokeAttestation
|
|
12
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-HKZONPXW.js";
|
|
13
14
|
import {
|
|
14
15
|
approveDepositor,
|
|
15
16
|
deposit,
|
|
@@ -27,7 +28,7 @@ import {
|
|
|
27
28
|
setTextRecord,
|
|
28
29
|
setVaultAddress,
|
|
29
30
|
simulateBatch
|
|
30
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-Q37V65B6.js";
|
|
31
32
|
import {
|
|
32
33
|
AGENT_REGISTRY,
|
|
33
34
|
EAS_SCHEMAS,
|
|
@@ -36,20 +37,23 @@ import {
|
|
|
36
37
|
SHERWOOD,
|
|
37
38
|
STRATEGY_REGISTRY_ABI,
|
|
38
39
|
SYNDICATE_FACTORY_ABI,
|
|
40
|
+
SYNDICATE_GOVERNOR_ABI,
|
|
39
41
|
SYNDICATE_VAULT_ABI,
|
|
40
42
|
TOKENS,
|
|
41
43
|
UNISWAP,
|
|
42
44
|
UNISWAP_QUOTER_V2_ABI,
|
|
43
45
|
VENICE,
|
|
44
46
|
VENICE_STAKING_ABI
|
|
45
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-OUES74ID.js";
|
|
46
48
|
import {
|
|
49
|
+
VALID_NETWORKS,
|
|
47
50
|
cacheGroupId,
|
|
48
51
|
getAccount,
|
|
49
52
|
getAgentId,
|
|
50
53
|
getChain,
|
|
51
54
|
getChainContracts,
|
|
52
55
|
getExplorerUrl,
|
|
56
|
+
getNetwork,
|
|
53
57
|
getPublicClient,
|
|
54
58
|
getRpcUrl,
|
|
55
59
|
getVeniceApiKey,
|
|
@@ -57,17 +61,18 @@ import {
|
|
|
57
61
|
loadConfig,
|
|
58
62
|
setAgentId,
|
|
59
63
|
setChainContract,
|
|
64
|
+
setConfigRpcUrl,
|
|
60
65
|
setNetwork,
|
|
61
66
|
setPrivateKey,
|
|
62
67
|
setVeniceApiKey
|
|
63
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-5ADWTXNT.js";
|
|
64
69
|
|
|
65
70
|
// src/index.ts
|
|
66
71
|
import { config as loadDotenv } from "dotenv";
|
|
67
|
-
import { Command } from "commander";
|
|
72
|
+
import { Command, Option } from "commander";
|
|
68
73
|
import { parseUnits as parseUnits8 } from "viem";
|
|
69
|
-
import
|
|
70
|
-
import
|
|
74
|
+
import chalk7 from "chalk";
|
|
75
|
+
import ora7 from "ora";
|
|
71
76
|
import { input, confirm, select } from "@inquirer/prompts";
|
|
72
77
|
|
|
73
78
|
// src/providers/moonwell.ts
|
|
@@ -929,7 +934,7 @@ function registerVeniceCommands(program2) {
|
|
|
929
934
|
[assetAddress, totalDeposited, agents] = await Promise.all([
|
|
930
935
|
client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "asset" }),
|
|
931
936
|
client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "totalDeposited" }),
|
|
932
|
-
client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "
|
|
937
|
+
client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "getAgentAddresses" })
|
|
933
938
|
]);
|
|
934
939
|
[assetDecimals, assetSymbol, assetBalance] = await Promise.all([
|
|
935
940
|
client.readContract({ address: assetAddress, abi: ERC20_ABI, functionName: "decimals" }),
|
|
@@ -1096,7 +1101,7 @@ function registerVeniceCommands(program2) {
|
|
|
1096
1101
|
const [assetAddress, totalDeposited, agents] = await Promise.all([
|
|
1097
1102
|
client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "asset" }),
|
|
1098
1103
|
client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "totalDeposited" }),
|
|
1099
|
-
client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "
|
|
1104
|
+
client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "getAgentAddresses" })
|
|
1100
1105
|
]);
|
|
1101
1106
|
const [assetDecimals, assetSymbol, assetBalance] = await Promise.all([
|
|
1102
1107
|
client.readContract({ address: assetAddress, abi: ERC20_ABI, functionName: "decimals" }),
|
|
@@ -1330,7 +1335,7 @@ function registerAllowanceCommands(program2) {
|
|
|
1330
1335
|
[assetAddress, totalDeposited, agents] = await Promise.all([
|
|
1331
1336
|
client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "asset" }),
|
|
1332
1337
|
client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "totalDeposited" }),
|
|
1333
|
-
client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "
|
|
1338
|
+
client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "getAgentAddresses" })
|
|
1334
1339
|
]);
|
|
1335
1340
|
[assetDecimals, assetSymbol, assetBalance] = await Promise.all([
|
|
1336
1341
|
client.readContract({ address: assetAddress, abi: ERC20_ABI, functionName: "decimals" }),
|
|
@@ -1473,7 +1478,7 @@ function registerAllowanceCommands(program2) {
|
|
|
1473
1478
|
const [assetAddress, totalDeposited, agents] = await Promise.all([
|
|
1474
1479
|
client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "asset" }),
|
|
1475
1480
|
client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "totalDeposited" }),
|
|
1476
|
-
client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "
|
|
1481
|
+
client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "getAgentAddresses" })
|
|
1477
1482
|
]);
|
|
1478
1483
|
const [assetDecimals, assetSymbol, assetBalance] = await Promise.all([
|
|
1479
1484
|
client.readContract({ address: assetAddress, abi: ERC20_ABI, functionName: "decimals" }),
|
|
@@ -1674,60 +1679,892 @@ function registerIdentityCommands(program2) {
|
|
|
1674
1679
|
});
|
|
1675
1680
|
}
|
|
1676
1681
|
|
|
1677
|
-
// src/
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1682
|
+
// src/commands/proposal.ts
|
|
1683
|
+
import { isAddress as isAddress4 } from "viem";
|
|
1684
|
+
import chalk5 from "chalk";
|
|
1685
|
+
import ora5 from "ora";
|
|
1686
|
+
import { readFileSync } from "fs";
|
|
1687
|
+
|
|
1688
|
+
// src/lib/governor.ts
|
|
1689
|
+
var PROPOSAL_STATES = [
|
|
1690
|
+
"Pending",
|
|
1691
|
+
"Approved",
|
|
1692
|
+
"Rejected",
|
|
1693
|
+
"Expired",
|
|
1694
|
+
"Executed",
|
|
1695
|
+
"Settled",
|
|
1696
|
+
"Cancelled"
|
|
1697
|
+
];
|
|
1698
|
+
function parseDuration(input2) {
|
|
1699
|
+
const match = input2.match(/^(\d+)(d|h|m|s)?$/);
|
|
1700
|
+
if (!match) throw new Error(`Invalid duration: ${input2}`);
|
|
1701
|
+
const value = BigInt(match[1]);
|
|
1702
|
+
switch (match[2]) {
|
|
1703
|
+
case "d":
|
|
1704
|
+
return value * 86400n;
|
|
1705
|
+
case "h":
|
|
1706
|
+
return value * 3600n;
|
|
1707
|
+
case "m":
|
|
1708
|
+
return value * 60n;
|
|
1709
|
+
case "s":
|
|
1710
|
+
case void 0:
|
|
1711
|
+
return value;
|
|
1712
|
+
default:
|
|
1713
|
+
throw new Error(`Unknown duration unit: ${match[2]}`);
|
|
1714
|
+
}
|
|
1681
1715
|
}
|
|
1682
|
-
|
|
1683
|
-
return
|
|
1716
|
+
function getGovernorAddress() {
|
|
1717
|
+
return SHERWOOD().GOVERNOR;
|
|
1718
|
+
}
|
|
1719
|
+
async function getGovernorParams() {
|
|
1720
|
+
const client = getPublicClient();
|
|
1721
|
+
const result = await client.readContract({
|
|
1722
|
+
address: getGovernorAddress(),
|
|
1723
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
1724
|
+
functionName: "getGovernorParams"
|
|
1725
|
+
});
|
|
1726
|
+
return result;
|
|
1727
|
+
}
|
|
1728
|
+
async function getProposal(id) {
|
|
1729
|
+
const client = getPublicClient();
|
|
1730
|
+
const result = await client.readContract({
|
|
1731
|
+
address: getGovernorAddress(),
|
|
1732
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
1733
|
+
functionName: "getProposal",
|
|
1734
|
+
args: [id]
|
|
1735
|
+
});
|
|
1736
|
+
return result;
|
|
1737
|
+
}
|
|
1738
|
+
async function getProposalState(id) {
|
|
1739
|
+
const client = getPublicClient();
|
|
1740
|
+
return client.readContract({
|
|
1741
|
+
address: getGovernorAddress(),
|
|
1742
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
1743
|
+
functionName: "getProposalState",
|
|
1744
|
+
args: [id]
|
|
1745
|
+
});
|
|
1746
|
+
}
|
|
1747
|
+
async function proposalCount() {
|
|
1748
|
+
const client = getPublicClient();
|
|
1749
|
+
return client.readContract({
|
|
1750
|
+
address: getGovernorAddress(),
|
|
1751
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
1752
|
+
functionName: "proposalCount"
|
|
1753
|
+
});
|
|
1754
|
+
}
|
|
1755
|
+
async function getVoteWeight(proposalId, voter) {
|
|
1756
|
+
const client = getPublicClient();
|
|
1757
|
+
return client.readContract({
|
|
1758
|
+
address: getGovernorAddress(),
|
|
1759
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
1760
|
+
functionName: "getVoteWeight",
|
|
1761
|
+
args: [proposalId, voter]
|
|
1762
|
+
});
|
|
1763
|
+
}
|
|
1764
|
+
async function hasVoted(proposalId, voter) {
|
|
1765
|
+
const client = getPublicClient();
|
|
1766
|
+
return client.readContract({
|
|
1767
|
+
address: getGovernorAddress(),
|
|
1768
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
1769
|
+
functionName: "hasVoted",
|
|
1770
|
+
args: [proposalId, voter]
|
|
1771
|
+
});
|
|
1684
1772
|
}
|
|
1773
|
+
async function getProposalCalls(proposalId) {
|
|
1774
|
+
const client = getPublicClient();
|
|
1775
|
+
const result = await client.readContract({
|
|
1776
|
+
address: getGovernorAddress(),
|
|
1777
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
1778
|
+
functionName: "getProposalCalls",
|
|
1779
|
+
args: [proposalId]
|
|
1780
|
+
});
|
|
1781
|
+
return result.map((c) => ({ target: c.target, data: c.data, value: c.value }));
|
|
1782
|
+
}
|
|
1783
|
+
async function getRegisteredVaults() {
|
|
1784
|
+
const client = getPublicClient();
|
|
1785
|
+
return client.readContract({
|
|
1786
|
+
address: getGovernorAddress(),
|
|
1787
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
1788
|
+
functionName: "getRegisteredVaults"
|
|
1789
|
+
});
|
|
1790
|
+
}
|
|
1791
|
+
async function getCapitalSnapshot(proposalId) {
|
|
1792
|
+
const client = getPublicClient();
|
|
1793
|
+
return client.readContract({
|
|
1794
|
+
address: getGovernorAddress(),
|
|
1795
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
1796
|
+
functionName: "getCapitalSnapshot",
|
|
1797
|
+
args: [proposalId]
|
|
1798
|
+
});
|
|
1799
|
+
}
|
|
1800
|
+
async function propose(vault, metadataURI, performanceFeeBps, strategyDuration, calls, splitIndex) {
|
|
1801
|
+
const wallet = getWalletClient();
|
|
1802
|
+
const client = getPublicClient();
|
|
1803
|
+
const hash = await wallet.writeContract({
|
|
1804
|
+
account: getAccount(),
|
|
1805
|
+
chain: getChain(),
|
|
1806
|
+
address: getGovernorAddress(),
|
|
1807
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
1808
|
+
functionName: "propose",
|
|
1809
|
+
args: [vault, metadataURI, performanceFeeBps, strategyDuration, calls, splitIndex]
|
|
1810
|
+
});
|
|
1811
|
+
const receipt = await client.waitForTransactionReceipt({ hash });
|
|
1812
|
+
let proposalId;
|
|
1813
|
+
try {
|
|
1814
|
+
proposalId = await proposalCount();
|
|
1815
|
+
} catch {
|
|
1816
|
+
proposalId = 0n;
|
|
1817
|
+
}
|
|
1818
|
+
return { hash: receipt.transactionHash, proposalId };
|
|
1819
|
+
}
|
|
1820
|
+
async function vote(proposalId, support) {
|
|
1821
|
+
const wallet = getWalletClient();
|
|
1822
|
+
const client = getPublicClient();
|
|
1823
|
+
const hash = await wallet.writeContract({
|
|
1824
|
+
account: getAccount(),
|
|
1825
|
+
chain: getChain(),
|
|
1826
|
+
address: getGovernorAddress(),
|
|
1827
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
1828
|
+
functionName: "vote",
|
|
1829
|
+
args: [proposalId, support]
|
|
1830
|
+
});
|
|
1831
|
+
const receipt = await client.waitForTransactionReceipt({ hash });
|
|
1832
|
+
return receipt.transactionHash;
|
|
1833
|
+
}
|
|
1834
|
+
async function executeProposal(proposalId) {
|
|
1835
|
+
const wallet = getWalletClient();
|
|
1836
|
+
const client = getPublicClient();
|
|
1837
|
+
const hash = await wallet.writeContract({
|
|
1838
|
+
account: getAccount(),
|
|
1839
|
+
chain: getChain(),
|
|
1840
|
+
address: getGovernorAddress(),
|
|
1841
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
1842
|
+
functionName: "executeProposal",
|
|
1843
|
+
args: [proposalId]
|
|
1844
|
+
});
|
|
1845
|
+
const receipt = await client.waitForTransactionReceipt({ hash });
|
|
1846
|
+
return receipt.transactionHash;
|
|
1847
|
+
}
|
|
1848
|
+
async function settleProposal(proposalId) {
|
|
1849
|
+
const wallet = getWalletClient();
|
|
1850
|
+
const client = getPublicClient();
|
|
1851
|
+
const hash = await wallet.writeContract({
|
|
1852
|
+
account: getAccount(),
|
|
1853
|
+
chain: getChain(),
|
|
1854
|
+
address: getGovernorAddress(),
|
|
1855
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
1856
|
+
functionName: "settleProposal",
|
|
1857
|
+
args: [proposalId]
|
|
1858
|
+
});
|
|
1859
|
+
const receipt = await client.waitForTransactionReceipt({ hash });
|
|
1860
|
+
return receipt.transactionHash;
|
|
1861
|
+
}
|
|
1862
|
+
async function settleByAgent(proposalId, calls) {
|
|
1863
|
+
const wallet = getWalletClient();
|
|
1864
|
+
const client = getPublicClient();
|
|
1865
|
+
const hash = await wallet.writeContract({
|
|
1866
|
+
account: getAccount(),
|
|
1867
|
+
chain: getChain(),
|
|
1868
|
+
address: getGovernorAddress(),
|
|
1869
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
1870
|
+
functionName: "settleByAgent",
|
|
1871
|
+
args: [proposalId, calls]
|
|
1872
|
+
});
|
|
1873
|
+
const receipt = await client.waitForTransactionReceipt({ hash });
|
|
1874
|
+
return receipt.transactionHash;
|
|
1875
|
+
}
|
|
1876
|
+
async function emergencySettle(proposalId, calls) {
|
|
1877
|
+
const wallet = getWalletClient();
|
|
1878
|
+
const client = getPublicClient();
|
|
1879
|
+
const hash = await wallet.writeContract({
|
|
1880
|
+
account: getAccount(),
|
|
1881
|
+
chain: getChain(),
|
|
1882
|
+
address: getGovernorAddress(),
|
|
1883
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
1884
|
+
functionName: "emergencySettle",
|
|
1885
|
+
args: [proposalId, calls]
|
|
1886
|
+
});
|
|
1887
|
+
const receipt = await client.waitForTransactionReceipt({ hash });
|
|
1888
|
+
return receipt.transactionHash;
|
|
1889
|
+
}
|
|
1890
|
+
async function cancelProposal(proposalId) {
|
|
1891
|
+
const wallet = getWalletClient();
|
|
1892
|
+
const client = getPublicClient();
|
|
1893
|
+
const hash = await wallet.writeContract({
|
|
1894
|
+
account: getAccount(),
|
|
1895
|
+
chain: getChain(),
|
|
1896
|
+
address: getGovernorAddress(),
|
|
1897
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
1898
|
+
functionName: "cancelProposal",
|
|
1899
|
+
args: [proposalId]
|
|
1900
|
+
});
|
|
1901
|
+
const receipt = await client.waitForTransactionReceipt({ hash });
|
|
1902
|
+
return receipt.transactionHash;
|
|
1903
|
+
}
|
|
1904
|
+
async function emergencyCancel(proposalId) {
|
|
1905
|
+
const wallet = getWalletClient();
|
|
1906
|
+
const client = getPublicClient();
|
|
1907
|
+
const hash = await wallet.writeContract({
|
|
1908
|
+
account: getAccount(),
|
|
1909
|
+
chain: getChain(),
|
|
1910
|
+
address: getGovernorAddress(),
|
|
1911
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
1912
|
+
functionName: "emergencyCancel",
|
|
1913
|
+
args: [proposalId]
|
|
1914
|
+
});
|
|
1915
|
+
const receipt = await client.waitForTransactionReceipt({ hash });
|
|
1916
|
+
return receipt.transactionHash;
|
|
1917
|
+
}
|
|
1918
|
+
async function setVotingPeriod(seconds) {
|
|
1919
|
+
const wallet = getWalletClient();
|
|
1920
|
+
const client = getPublicClient();
|
|
1921
|
+
const hash = await wallet.writeContract({
|
|
1922
|
+
account: getAccount(),
|
|
1923
|
+
chain: getChain(),
|
|
1924
|
+
address: getGovernorAddress(),
|
|
1925
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
1926
|
+
functionName: "setVotingPeriod",
|
|
1927
|
+
args: [seconds]
|
|
1928
|
+
});
|
|
1929
|
+
const receipt = await client.waitForTransactionReceipt({ hash });
|
|
1930
|
+
return receipt.transactionHash;
|
|
1931
|
+
}
|
|
1932
|
+
async function setExecutionWindow(seconds) {
|
|
1933
|
+
const wallet = getWalletClient();
|
|
1934
|
+
const client = getPublicClient();
|
|
1935
|
+
const hash = await wallet.writeContract({
|
|
1936
|
+
account: getAccount(),
|
|
1937
|
+
chain: getChain(),
|
|
1938
|
+
address: getGovernorAddress(),
|
|
1939
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
1940
|
+
functionName: "setExecutionWindow",
|
|
1941
|
+
args: [seconds]
|
|
1942
|
+
});
|
|
1943
|
+
const receipt = await client.waitForTransactionReceipt({ hash });
|
|
1944
|
+
return receipt.transactionHash;
|
|
1945
|
+
}
|
|
1946
|
+
async function setQuorumBps(bps) {
|
|
1947
|
+
const wallet = getWalletClient();
|
|
1948
|
+
const client = getPublicClient();
|
|
1949
|
+
const hash = await wallet.writeContract({
|
|
1950
|
+
account: getAccount(),
|
|
1951
|
+
chain: getChain(),
|
|
1952
|
+
address: getGovernorAddress(),
|
|
1953
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
1954
|
+
functionName: "setQuorumBps",
|
|
1955
|
+
args: [bps]
|
|
1956
|
+
});
|
|
1957
|
+
const receipt = await client.waitForTransactionReceipt({ hash });
|
|
1958
|
+
return receipt.transactionHash;
|
|
1959
|
+
}
|
|
1960
|
+
async function setMaxPerformanceFeeBps(bps) {
|
|
1961
|
+
const wallet = getWalletClient();
|
|
1962
|
+
const client = getPublicClient();
|
|
1963
|
+
const hash = await wallet.writeContract({
|
|
1964
|
+
account: getAccount(),
|
|
1965
|
+
chain: getChain(),
|
|
1966
|
+
address: getGovernorAddress(),
|
|
1967
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
1968
|
+
functionName: "setMaxPerformanceFeeBps",
|
|
1969
|
+
args: [bps]
|
|
1970
|
+
});
|
|
1971
|
+
const receipt = await client.waitForTransactionReceipt({ hash });
|
|
1972
|
+
return receipt.transactionHash;
|
|
1973
|
+
}
|
|
1974
|
+
async function setMaxStrategyDuration(seconds) {
|
|
1975
|
+
const wallet = getWalletClient();
|
|
1976
|
+
const client = getPublicClient();
|
|
1977
|
+
const hash = await wallet.writeContract({
|
|
1978
|
+
account: getAccount(),
|
|
1979
|
+
chain: getChain(),
|
|
1980
|
+
address: getGovernorAddress(),
|
|
1981
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
1982
|
+
functionName: "setMaxStrategyDuration",
|
|
1983
|
+
args: [seconds]
|
|
1984
|
+
});
|
|
1985
|
+
const receipt = await client.waitForTransactionReceipt({ hash });
|
|
1986
|
+
return receipt.transactionHash;
|
|
1987
|
+
}
|
|
1988
|
+
async function setCooldownPeriod(seconds) {
|
|
1989
|
+
const wallet = getWalletClient();
|
|
1990
|
+
const client = getPublicClient();
|
|
1991
|
+
const hash = await wallet.writeContract({
|
|
1992
|
+
account: getAccount(),
|
|
1993
|
+
chain: getChain(),
|
|
1994
|
+
address: getGovernorAddress(),
|
|
1995
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
1996
|
+
functionName: "setCooldownPeriod",
|
|
1997
|
+
args: [seconds]
|
|
1998
|
+
});
|
|
1999
|
+
const receipt = await client.waitForTransactionReceipt({ hash });
|
|
2000
|
+
return receipt.transactionHash;
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
// src/lib/format.ts
|
|
2004
|
+
function formatDurationShort(seconds) {
|
|
2005
|
+
const s = Number(seconds);
|
|
2006
|
+
if (s >= 86400) return `${(s / 86400).toFixed(s % 86400 === 0 ? 0 : 1)}d`;
|
|
2007
|
+
if (s >= 3600) return `${(s / 3600).toFixed(s % 3600 === 0 ? 0 : 1)}h`;
|
|
2008
|
+
if (s >= 60) return `${(s / 60).toFixed(0)}m`;
|
|
2009
|
+
return `${s}s`;
|
|
2010
|
+
}
|
|
2011
|
+
function formatDurationLong(seconds) {
|
|
2012
|
+
const s = Number(seconds);
|
|
2013
|
+
if (s >= 86400) return `${(s / 86400).toFixed(s % 86400 === 0 ? 0 : 1)} day${s >= 172800 ? "s" : ""}`;
|
|
2014
|
+
if (s >= 3600) return `${(s / 3600).toFixed(s % 3600 === 0 ? 0 : 1)} hour${s >= 7200 ? "s" : ""}`;
|
|
2015
|
+
if (s >= 60) return `${(s / 60).toFixed(0)} min`;
|
|
2016
|
+
return `${s}s`;
|
|
2017
|
+
}
|
|
2018
|
+
function formatShares(raw) {
|
|
2019
|
+
const num = Number(raw) / 1e6;
|
|
2020
|
+
return num.toLocaleString("en-US", { minimumFractionDigits: 0, maximumFractionDigits: 2 });
|
|
2021
|
+
}
|
|
2022
|
+
function formatUSDC(raw) {
|
|
2023
|
+
const num = Number(raw) / 1e6;
|
|
2024
|
+
return `$${num.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
|
2025
|
+
}
|
|
2026
|
+
function parseBigIntArg(value, name) {
|
|
2027
|
+
if (!/^-?\d+$/.test(value)) {
|
|
2028
|
+
throw new Error(`Invalid ${name}: "${value}" is not a valid integer`);
|
|
2029
|
+
}
|
|
2030
|
+
return BigInt(value);
|
|
2031
|
+
}
|
|
2032
|
+
|
|
2033
|
+
// src/commands/proposal.ts
|
|
1685
2034
|
var G = chalk5.green;
|
|
1686
2035
|
var W = chalk5.white;
|
|
1687
2036
|
var DIM = chalk5.gray;
|
|
1688
2037
|
var BOLD = chalk5.white.bold;
|
|
1689
2038
|
var LABEL = chalk5.green.bold;
|
|
1690
2039
|
var SEP = () => console.log(DIM("\u2500".repeat(60)));
|
|
2040
|
+
function formatTimestamp(ts) {
|
|
2041
|
+
if (ts === 0n) return "\u2014";
|
|
2042
|
+
return new Date(Number(ts) * 1e3).toLocaleString();
|
|
2043
|
+
}
|
|
2044
|
+
function parseCallsFile(path) {
|
|
2045
|
+
const raw = readFileSync(path, "utf-8");
|
|
2046
|
+
const parsed = JSON.parse(raw);
|
|
2047
|
+
return parsed.map((c) => ({
|
|
2048
|
+
target: c.target,
|
|
2049
|
+
data: c.data,
|
|
2050
|
+
value: BigInt(c.value || "0")
|
|
2051
|
+
}));
|
|
2052
|
+
}
|
|
2053
|
+
function registerProposalCommands(program2) {
|
|
2054
|
+
const proposal = program2.command("proposal").description("Governance proposals \u2014 create, vote, execute, settle");
|
|
2055
|
+
proposal.command("create").description("Submit a strategy proposal").requiredOption("--vault <address>", "Vault address the proposal targets").requiredOption("--name <name>", "Strategy name").requiredOption("--description <text>", "Strategy rationale and risk summary").requiredOption("--performance-fee <bps>", "Agent fee in bps (e.g. 1500 = 15%)").requiredOption("--duration <duration>", "Strategy duration (e.g. 7d, 24h, 3600)").requiredOption("--calls <path>", "Path to JSON file with Call[] array").requiredOption("--split-index <n>", "Index where execute calls end and settle calls begin").option("--metadata-uri <uri>", "Override \u2014 skip IPFS upload and use this URI directly").action(async (opts) => {
|
|
2056
|
+
try {
|
|
2057
|
+
const vault = opts.vault;
|
|
2058
|
+
if (!isAddress4(vault)) {
|
|
2059
|
+
console.error(chalk5.red(`Invalid vault address: ${opts.vault}`));
|
|
2060
|
+
process.exit(1);
|
|
2061
|
+
}
|
|
2062
|
+
const performanceFeeBps = parseBigIntArg(opts.performanceFee, "performance-fee");
|
|
2063
|
+
const strategyDuration = parseDuration(opts.duration);
|
|
2064
|
+
const splitIndex = parseBigIntArg(opts.splitIndex, "split-index");
|
|
2065
|
+
const calls = parseCallsFile(opts.calls);
|
|
2066
|
+
let metadataURI = opts.metadataUri || "";
|
|
2067
|
+
if (!metadataURI) {
|
|
2068
|
+
const spinner2 = ora5({ text: W("Uploading metadata to IPFS..."), color: "green" }).start();
|
|
2069
|
+
try {
|
|
2070
|
+
const account = getAccount();
|
|
2071
|
+
const metadata = {
|
|
2072
|
+
schema: "sherwood/proposal/v1",
|
|
2073
|
+
name: opts.name,
|
|
2074
|
+
description: opts.description,
|
|
2075
|
+
chain: getNetwork(),
|
|
2076
|
+
strategies: [],
|
|
2077
|
+
terms: { ragequitEnabled: true },
|
|
2078
|
+
links: {}
|
|
2079
|
+
};
|
|
2080
|
+
const proposalMeta = {
|
|
2081
|
+
...metadata,
|
|
2082
|
+
proposer: account.address,
|
|
2083
|
+
vault,
|
|
2084
|
+
performanceFeeBps: Number(performanceFeeBps),
|
|
2085
|
+
strategyDuration: Number(strategyDuration),
|
|
2086
|
+
createdAt: Math.floor(Date.now() / 1e3)
|
|
2087
|
+
};
|
|
2088
|
+
metadataURI = await uploadMetadata(proposalMeta);
|
|
2089
|
+
spinner2.succeed(G(`Metadata pinned: ${DIM(metadataURI)}`));
|
|
2090
|
+
} catch (err) {
|
|
2091
|
+
spinner2.warn(chalk5.yellow("IPFS upload failed \u2014 using inline metadata"));
|
|
2092
|
+
const json = JSON.stringify({ name: opts.name, description: opts.description, vault });
|
|
2093
|
+
metadataURI = `data:application/json;base64,${Buffer.from(json).toString("base64")}`;
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
console.log();
|
|
2097
|
+
console.log(LABEL(" \u25C6 Proposal Summary"));
|
|
2098
|
+
SEP();
|
|
2099
|
+
console.log(W(` Name: ${BOLD(opts.name)}`));
|
|
2100
|
+
console.log(W(` Vault: ${G(vault)}`));
|
|
2101
|
+
console.log(W(` Performance Fee: ${Number(performanceFeeBps) / 100}%`));
|
|
2102
|
+
console.log(W(` Duration: ${formatDurationShort(strategyDuration)}`));
|
|
2103
|
+
console.log(W(` Calls: ${calls.length} (split at ${splitIndex})`));
|
|
2104
|
+
console.log(W(` Metadata: ${DIM(metadataURI.length > 50 ? metadataURI.slice(0, 50) + "..." : metadataURI)}`));
|
|
2105
|
+
SEP();
|
|
2106
|
+
const spinner = ora5({ text: W("Submitting proposal..."), color: "green" }).start();
|
|
2107
|
+
const result = await propose(vault, metadataURI, performanceFeeBps, strategyDuration, calls, splitIndex);
|
|
2108
|
+
spinner.succeed(G("Proposal submitted"));
|
|
2109
|
+
console.log();
|
|
2110
|
+
console.log(LABEL(" \u25C6 Proposal Created"));
|
|
2111
|
+
SEP();
|
|
2112
|
+
console.log(W(` Proposal ID: ${G(`#${result.proposalId}`)}`));
|
|
2113
|
+
console.log(W(` Tx: ${DIM(getExplorerUrl(result.hash))}`));
|
|
2114
|
+
SEP();
|
|
2115
|
+
console.log();
|
|
2116
|
+
} catch (err) {
|
|
2117
|
+
console.error(chalk5.red(`
|
|
2118
|
+
\u2716 ${err instanceof Error ? err.message : String(err)}`));
|
|
2119
|
+
process.exit(1);
|
|
2120
|
+
}
|
|
2121
|
+
});
|
|
2122
|
+
proposal.command("list").description("List proposals").option("--vault <address>", "Filter by vault").option("--state <filter>", "Filter by state: pending, approved, executed, settled, all", "all").action(async (opts) => {
|
|
2123
|
+
const spinner = ora5("Loading proposals...").start();
|
|
2124
|
+
try {
|
|
2125
|
+
const count = await proposalCount();
|
|
2126
|
+
if (count === 0n) {
|
|
2127
|
+
spinner.stop();
|
|
2128
|
+
console.log(DIM("\n No proposals found.\n"));
|
|
2129
|
+
return;
|
|
2130
|
+
}
|
|
2131
|
+
const vaultFilter = opts.vault ? opts.vault.toLowerCase() : null;
|
|
2132
|
+
const stateFilter = opts.state.toLowerCase();
|
|
2133
|
+
const stateIndex = PROPOSAL_STATES.findIndex((s) => s.toLowerCase() === stateFilter);
|
|
2134
|
+
const ids = Array.from({ length: Number(count) }, (_, i) => BigInt(i + 1));
|
|
2135
|
+
const results = await Promise.all(
|
|
2136
|
+
ids.map(async (id) => {
|
|
2137
|
+
const [p, state] = await Promise.all([getProposal(id), getProposalState(id)]);
|
|
2138
|
+
return { ...p, computedState: state };
|
|
2139
|
+
})
|
|
2140
|
+
);
|
|
2141
|
+
const proposals = results.filter((p) => {
|
|
2142
|
+
if (vaultFilter && p.vault.toLowerCase() !== vaultFilter) return false;
|
|
2143
|
+
if (stateFilter !== "all" && stateIndex >= 0 && p.computedState !== stateIndex) return false;
|
|
2144
|
+
return true;
|
|
2145
|
+
});
|
|
2146
|
+
spinner.stop();
|
|
2147
|
+
if (proposals.length === 0) {
|
|
2148
|
+
console.log(DIM("\n No matching proposals.\n"));
|
|
2149
|
+
return;
|
|
2150
|
+
}
|
|
2151
|
+
console.log();
|
|
2152
|
+
console.log(BOLD(`Proposals (${proposals.length})`));
|
|
2153
|
+
console.log(DIM("\u2500".repeat(90)));
|
|
2154
|
+
console.log(
|
|
2155
|
+
DIM(" ID ") + DIM("Agent".padEnd(14)) + DIM("State".padEnd(12)) + DIM("Votes (For/Against)".padEnd(22)) + DIM("Fee".padEnd(8)) + DIM("Duration".padEnd(10)) + DIM("Created")
|
|
2156
|
+
);
|
|
2157
|
+
console.log(DIM("\u2500".repeat(90)));
|
|
2158
|
+
for (const p of proposals) {
|
|
2159
|
+
const state = PROPOSAL_STATES[p.computedState] || "Unknown";
|
|
2160
|
+
const created = p.snapshotTimestamp > 0n ? new Date(Number(p.snapshotTimestamp) * 1e3).toLocaleDateString() : "\u2014";
|
|
2161
|
+
const agent = `${p.proposer.slice(0, 6)}...${p.proposer.slice(-4)}`;
|
|
2162
|
+
const fee = `${Number(p.performanceFeeBps) / 100}%`;
|
|
2163
|
+
const dur = formatDurationShort(p.strategyDuration);
|
|
2164
|
+
const votes = `${formatShares(p.votesFor)}/${formatShares(p.votesAgainst)}`;
|
|
2165
|
+
console.log(
|
|
2166
|
+
` ${String(p.id).padEnd(4)}${agent.padEnd(14)}${state.padEnd(12)}${votes.padEnd(22)}${fee.padEnd(8)}${dur.padEnd(10)}${created}`
|
|
2167
|
+
);
|
|
2168
|
+
}
|
|
2169
|
+
console.log();
|
|
2170
|
+
} catch (err) {
|
|
2171
|
+
spinner.fail("Failed to load proposals");
|
|
2172
|
+
console.error(chalk5.red(err instanceof Error ? err.message : String(err)));
|
|
2173
|
+
process.exit(1);
|
|
2174
|
+
}
|
|
2175
|
+
});
|
|
2176
|
+
proposal.command("show").description("Show full proposal details").argument("<id>", "Proposal ID").action(async (idStr) => {
|
|
2177
|
+
const spinner = ora5("Loading proposal...").start();
|
|
2178
|
+
try {
|
|
2179
|
+
const id = parseBigIntArg(idStr, "proposal ID");
|
|
2180
|
+
const p = await getProposal(id);
|
|
2181
|
+
const state = await getProposalState(id);
|
|
2182
|
+
const calls = await getProposalCalls(id);
|
|
2183
|
+
const params = await getGovernorParams();
|
|
2184
|
+
spinner.stop();
|
|
2185
|
+
const stateLabel = PROPOSAL_STATES[state] || "Unknown";
|
|
2186
|
+
const totalVotes = p.votesFor + p.votesAgainst;
|
|
2187
|
+
const quorumNeeded = totalVotes > 0n ? `${Number(params.quorumBps) / 100}%` : "\u2014";
|
|
2188
|
+
console.log();
|
|
2189
|
+
console.log(LABEL(` \u25C6 Proposal #${p.id}`));
|
|
2190
|
+
SEP();
|
|
2191
|
+
if (p.metadataURI && p.metadataURI.startsWith("ipfs://")) {
|
|
2192
|
+
try {
|
|
2193
|
+
const meta = await fetchMetadata(p.metadataURI);
|
|
2194
|
+
console.log(W(` Name: ${BOLD(meta.name)}`));
|
|
2195
|
+
console.log(W(` Description: ${DIM(meta.description)}`));
|
|
2196
|
+
} catch {
|
|
2197
|
+
console.log(W(` Metadata: ${DIM(p.metadataURI)}`));
|
|
2198
|
+
}
|
|
2199
|
+
} else if (p.metadataURI) {
|
|
2200
|
+
console.log(W(` Metadata: ${DIM(p.metadataURI)}`));
|
|
2201
|
+
}
|
|
2202
|
+
console.log(W(` State: ${BOLD(stateLabel)}`));
|
|
2203
|
+
console.log(W(` Proposer: ${G(p.proposer)}`));
|
|
2204
|
+
console.log(W(` Vault: ${G(p.vault)}`));
|
|
2205
|
+
console.log(W(` Performance Fee: ${Number(p.performanceFeeBps) / 100}%`));
|
|
2206
|
+
console.log(W(` Duration: ${formatDurationShort(p.strategyDuration)}`));
|
|
2207
|
+
console.log();
|
|
2208
|
+
console.log(LABEL(" Timestamps"));
|
|
2209
|
+
console.log(W(` Snapshot: ${formatTimestamp(p.snapshotTimestamp)}`));
|
|
2210
|
+
console.log(W(` Vote End: ${formatTimestamp(p.voteEnd)}`));
|
|
2211
|
+
console.log(W(` Execute By: ${formatTimestamp(p.executeBy)}`));
|
|
2212
|
+
console.log(W(` Executed At: ${formatTimestamp(p.executedAt)}`));
|
|
2213
|
+
console.log();
|
|
2214
|
+
console.log(LABEL(" Votes"));
|
|
2215
|
+
console.log(W(` For: ${formatShares(p.votesFor)}`));
|
|
2216
|
+
console.log(W(` Against: ${formatShares(p.votesAgainst)}`));
|
|
2217
|
+
console.log(W(` Quorum: ${quorumNeeded}`));
|
|
2218
|
+
if (state === 4 || state === 5) {
|
|
2219
|
+
try {
|
|
2220
|
+
const cap = await getCapitalSnapshot(id);
|
|
2221
|
+
console.log();
|
|
2222
|
+
console.log(LABEL(" Capital"));
|
|
2223
|
+
console.log(W(` Snapshot: ${formatUSDC(cap)}`));
|
|
2224
|
+
} catch {
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
console.log();
|
|
2228
|
+
console.log(LABEL(` Calls (${calls.length}, split at ${p.splitIndex})`));
|
|
2229
|
+
for (let i = 0; i < calls.length; i++) {
|
|
2230
|
+
const phase = BigInt(i) < p.splitIndex ? "execute" : "settle";
|
|
2231
|
+
console.log(DIM(` [${i}] (${phase}) target=${calls[i].target}`));
|
|
2232
|
+
console.log(DIM(` data=${calls[i].data.slice(0, 20)}... value=${calls[i].value}`));
|
|
2233
|
+
}
|
|
2234
|
+
SEP();
|
|
2235
|
+
console.log();
|
|
2236
|
+
} catch (err) {
|
|
2237
|
+
spinner.fail("Failed to load proposal");
|
|
2238
|
+
console.error(chalk5.red(err instanceof Error ? err.message : String(err)));
|
|
2239
|
+
process.exit(1);
|
|
2240
|
+
}
|
|
2241
|
+
});
|
|
2242
|
+
proposal.command("vote").description("Cast a vote on a pending proposal").requiredOption("--id <proposalId>", "Proposal ID").requiredOption("--support <yes|no>", "Vote direction: yes or no").action(async (opts) => {
|
|
2243
|
+
try {
|
|
2244
|
+
const proposalId = parseBigIntArg(opts.id, "proposal ID");
|
|
2245
|
+
const support = opts.support.toLowerCase() === "yes";
|
|
2246
|
+
const account = getAccount();
|
|
2247
|
+
const spinner = ora5("Loading proposal...").start();
|
|
2248
|
+
const p = await getProposal(proposalId);
|
|
2249
|
+
const state = await getProposalState(proposalId);
|
|
2250
|
+
if (state !== 0) {
|
|
2251
|
+
spinner.fail(`Proposal is ${PROPOSAL_STATES[state] || "Unknown"}, not Pending`);
|
|
2252
|
+
process.exit(1);
|
|
2253
|
+
}
|
|
2254
|
+
const alreadyVoted = await hasVoted(proposalId, account.address);
|
|
2255
|
+
if (alreadyVoted) {
|
|
2256
|
+
spinner.fail("You have already voted on this proposal");
|
|
2257
|
+
process.exit(1);
|
|
2258
|
+
}
|
|
2259
|
+
const weight = await getVoteWeight(proposalId, account.address);
|
|
2260
|
+
spinner.stop();
|
|
2261
|
+
console.log();
|
|
2262
|
+
console.log(LABEL(" \u25C6 Cast Vote"));
|
|
2263
|
+
SEP();
|
|
2264
|
+
console.log(W(` Proposal: #${proposalId}`));
|
|
2265
|
+
console.log(W(` Vault: ${G(p.vault)}`));
|
|
2266
|
+
console.log(W(` Support: ${support ? G("YES") : chalk5.red("NO")}`));
|
|
2267
|
+
console.log(W(` Weight: ${formatShares(weight)} shares`));
|
|
2268
|
+
SEP();
|
|
2269
|
+
const voteSpinner = ora5({ text: W("Submitting vote..."), color: "green" }).start();
|
|
2270
|
+
const hash = await vote(proposalId, support);
|
|
2271
|
+
voteSpinner.succeed(G("Vote cast"));
|
|
2272
|
+
console.log(DIM(` ${getExplorerUrl(hash)}`));
|
|
2273
|
+
console.log();
|
|
2274
|
+
} catch (err) {
|
|
2275
|
+
console.error(chalk5.red(`
|
|
2276
|
+
\u2716 ${err instanceof Error ? err.message : String(err)}`));
|
|
2277
|
+
process.exit(1);
|
|
2278
|
+
}
|
|
2279
|
+
});
|
|
2280
|
+
proposal.command("execute").description("Execute an approved proposal").requiredOption("--id <proposalId>", "Proposal ID").action(async (opts) => {
|
|
2281
|
+
try {
|
|
2282
|
+
const proposalId = parseBigIntArg(opts.id, "proposal ID");
|
|
2283
|
+
const spinner = ora5("Loading proposal...").start();
|
|
2284
|
+
const state = await getProposalState(proposalId);
|
|
2285
|
+
if (state !== 1) {
|
|
2286
|
+
spinner.fail(`Proposal is ${PROPOSAL_STATES[state] || "Unknown"}, not Approved`);
|
|
2287
|
+
process.exit(1);
|
|
2288
|
+
}
|
|
2289
|
+
spinner.text = W("Executing proposal...");
|
|
2290
|
+
const hash = await executeProposal(proposalId);
|
|
2291
|
+
spinner.succeed(G("Proposal executed"));
|
|
2292
|
+
console.log(DIM(` ${getExplorerUrl(hash)}`));
|
|
2293
|
+
try {
|
|
2294
|
+
const cap = await getCapitalSnapshot(proposalId);
|
|
2295
|
+
console.log(DIM(` Capital snapshot: ${formatUSDC(cap)}`));
|
|
2296
|
+
} catch {
|
|
2297
|
+
}
|
|
2298
|
+
console.log();
|
|
2299
|
+
} catch (err) {
|
|
2300
|
+
console.error(chalk5.red(`
|
|
2301
|
+
\u2716 ${err instanceof Error ? err.message : String(err)}`));
|
|
2302
|
+
process.exit(1);
|
|
2303
|
+
}
|
|
2304
|
+
});
|
|
2305
|
+
proposal.command("settle").description("Settle an executed proposal (auto-routes settlement path)").requiredOption("--id <proposalId>", "Proposal ID").option("--calls <path>", "Path to JSON file with settle Call[] (for agent/emergency settle)").action(async (opts) => {
|
|
2306
|
+
try {
|
|
2307
|
+
const proposalId = parseBigIntArg(opts.id, "proposal ID");
|
|
2308
|
+
const account = getAccount();
|
|
2309
|
+
const spinner = ora5("Loading proposal...").start();
|
|
2310
|
+
const p = await getProposal(proposalId);
|
|
2311
|
+
const state = await getProposalState(proposalId);
|
|
2312
|
+
if (state !== 4) {
|
|
2313
|
+
spinner.fail(`Proposal is ${PROPOSAL_STATES[state] || "Unknown"}, not Executed`);
|
|
2314
|
+
process.exit(1);
|
|
2315
|
+
}
|
|
2316
|
+
const isProposer = account.address.toLowerCase() === p.proposer.toLowerCase();
|
|
2317
|
+
const now = BigInt(Math.floor(Date.now() / 1e3));
|
|
2318
|
+
const durationElapsed = p.executedAt > 0n && now >= p.executedAt + p.strategyDuration;
|
|
2319
|
+
let hash;
|
|
2320
|
+
if (isProposer && opts.calls) {
|
|
2321
|
+
spinner.text = W("Settling by agent...");
|
|
2322
|
+
const calls = parseCallsFile(opts.calls);
|
|
2323
|
+
hash = await settleByAgent(proposalId, calls);
|
|
2324
|
+
spinner.succeed(G("Settled by agent"));
|
|
2325
|
+
} else if (durationElapsed && !opts.calls) {
|
|
2326
|
+
spinner.text = W("Settling (permissionless)...");
|
|
2327
|
+
hash = await settleProposal(proposalId);
|
|
2328
|
+
spinner.succeed(G("Settled (permissionless)"));
|
|
2329
|
+
} else if (durationElapsed && opts.calls) {
|
|
2330
|
+
spinner.text = W("Emergency settling...");
|
|
2331
|
+
const calls = parseCallsFile(opts.calls);
|
|
2332
|
+
hash = await emergencySettle(proposalId, calls);
|
|
2333
|
+
spinner.succeed(G("Emergency settled"));
|
|
2334
|
+
} else {
|
|
2335
|
+
spinner.fail("Cannot settle: duration not elapsed. If you are the proposer, provide --calls.");
|
|
2336
|
+
process.exit(1);
|
|
2337
|
+
}
|
|
2338
|
+
console.log(DIM(` ${getExplorerUrl(hash)}`));
|
|
2339
|
+
console.log();
|
|
2340
|
+
} catch (err) {
|
|
2341
|
+
console.error(chalk5.red(`
|
|
2342
|
+
\u2716 ${err instanceof Error ? err.message : String(err)}`));
|
|
2343
|
+
process.exit(1);
|
|
2344
|
+
}
|
|
2345
|
+
});
|
|
2346
|
+
proposal.command("cancel").description("Cancel a proposal (proposer or vault owner)").requiredOption("--id <proposalId>", "Proposal ID").option("--emergency", "Emergency cancel (vault owner only, any non-settled state)").action(async (opts) => {
|
|
2347
|
+
try {
|
|
2348
|
+
const proposalId = parseBigIntArg(opts.id, "proposal ID");
|
|
2349
|
+
const spinner = ora5("Loading proposal...").start();
|
|
2350
|
+
const state = await getProposalState(proposalId);
|
|
2351
|
+
if (state === 5 || state === 6) {
|
|
2352
|
+
spinner.fail(`Proposal is already ${PROPOSAL_STATES[state]}`);
|
|
2353
|
+
process.exit(1);
|
|
2354
|
+
}
|
|
2355
|
+
let hash;
|
|
2356
|
+
if (opts.emergency) {
|
|
2357
|
+
spinner.text = W("Emergency cancelling...");
|
|
2358
|
+
hash = await emergencyCancel(proposalId);
|
|
2359
|
+
spinner.succeed(G("Emergency cancelled"));
|
|
2360
|
+
} else {
|
|
2361
|
+
if (state !== 0 && state !== 1) {
|
|
2362
|
+
spinner.fail(`Proposal is ${PROPOSAL_STATES[state] || "Unknown"} \u2014 use --emergency for non-pending/approved`);
|
|
2363
|
+
process.exit(1);
|
|
2364
|
+
}
|
|
2365
|
+
spinner.text = W("Cancelling proposal...");
|
|
2366
|
+
hash = await cancelProposal(proposalId);
|
|
2367
|
+
spinner.succeed(G("Proposal cancelled"));
|
|
2368
|
+
}
|
|
2369
|
+
console.log(DIM(` ${getExplorerUrl(hash)}`));
|
|
2370
|
+
console.log();
|
|
2371
|
+
} catch (err) {
|
|
2372
|
+
console.error(chalk5.red(`
|
|
2373
|
+
\u2716 ${err instanceof Error ? err.message : String(err)}`));
|
|
2374
|
+
process.exit(1);
|
|
2375
|
+
}
|
|
2376
|
+
});
|
|
2377
|
+
}
|
|
2378
|
+
|
|
2379
|
+
// src/commands/governor.ts
|
|
2380
|
+
import chalk6 from "chalk";
|
|
2381
|
+
import ora6 from "ora";
|
|
2382
|
+
var G2 = chalk6.green;
|
|
2383
|
+
var W2 = chalk6.white;
|
|
2384
|
+
var DIM2 = chalk6.gray;
|
|
2385
|
+
var BOLD2 = chalk6.white.bold;
|
|
2386
|
+
var LABEL2 = chalk6.green.bold;
|
|
2387
|
+
var SEP2 = () => console.log(DIM2("\u2500".repeat(60)));
|
|
2388
|
+
function registerGovernorCommands(program2) {
|
|
2389
|
+
const governor = program2.command("governor").description("Governor parameters and vault management");
|
|
2390
|
+
governor.command("info").description("Display current governor parameters and registered vaults").action(async () => {
|
|
2391
|
+
const spinner = ora6("Loading governor info...").start();
|
|
2392
|
+
try {
|
|
2393
|
+
const [params, vaults] = await Promise.all([
|
|
2394
|
+
getGovernorParams(),
|
|
2395
|
+
getRegisteredVaults()
|
|
2396
|
+
]);
|
|
2397
|
+
spinner.stop();
|
|
2398
|
+
console.log();
|
|
2399
|
+
console.log(LABEL2(" \u25C6 Governor Parameters"));
|
|
2400
|
+
SEP2();
|
|
2401
|
+
console.log(W2(` Address: ${G2(getGovernorAddress())}`));
|
|
2402
|
+
console.log(W2(` Voting Period: ${BOLD2(formatDurationLong(params.votingPeriod))}`));
|
|
2403
|
+
console.log(W2(` Execution Window: ${BOLD2(formatDurationLong(params.executionWindow))}`));
|
|
2404
|
+
console.log(W2(` Quorum: ${BOLD2(`${Number(params.quorumBps) / 100}%`)}`));
|
|
2405
|
+
console.log(W2(` Max Performance Fee: ${BOLD2(`${Number(params.maxPerformanceFeeBps) / 100}%`)}`));
|
|
2406
|
+
console.log(W2(` Max Strategy Duration:${BOLD2(` ${formatDurationLong(params.maxStrategyDuration)}`)}`));
|
|
2407
|
+
console.log(W2(` Cooldown Period: ${BOLD2(formatDurationLong(params.cooldownPeriod))}`));
|
|
2408
|
+
console.log();
|
|
2409
|
+
console.log(LABEL2(` Registered Vaults (${vaults.length})`));
|
|
2410
|
+
if (vaults.length === 0) {
|
|
2411
|
+
console.log(DIM2(" (none)"));
|
|
2412
|
+
} else {
|
|
2413
|
+
for (const v of vaults) {
|
|
2414
|
+
console.log(W2(` ${G2(v)}`));
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2417
|
+
SEP2();
|
|
2418
|
+
console.log();
|
|
2419
|
+
} catch (err) {
|
|
2420
|
+
spinner.fail("Failed to load governor info");
|
|
2421
|
+
console.error(chalk6.red(err instanceof Error ? err.message : String(err)));
|
|
2422
|
+
process.exit(1);
|
|
2423
|
+
}
|
|
2424
|
+
});
|
|
2425
|
+
governor.command("set-voting-period").description("Set the voting period (owner only)").requiredOption("--seconds <n>", "New voting period in seconds").action(async (opts) => {
|
|
2426
|
+
const spinner = ora6("Setting voting period...").start();
|
|
2427
|
+
try {
|
|
2428
|
+
const hash = await setVotingPeriod(parseBigIntArg(opts.seconds, "seconds"));
|
|
2429
|
+
spinner.succeed(G2(`Voting period updated to ${opts.seconds}s`));
|
|
2430
|
+
console.log(DIM2(` ${getExplorerUrl(hash)}`));
|
|
2431
|
+
} catch (err) {
|
|
2432
|
+
spinner.fail("Failed to set voting period");
|
|
2433
|
+
console.error(chalk6.red(err instanceof Error ? err.message : String(err)));
|
|
2434
|
+
process.exit(1);
|
|
2435
|
+
}
|
|
2436
|
+
});
|
|
2437
|
+
governor.command("set-execution-window").description("Set the execution window (owner only)").requiredOption("--seconds <n>", "New execution window in seconds").action(async (opts) => {
|
|
2438
|
+
const spinner = ora6("Setting execution window...").start();
|
|
2439
|
+
try {
|
|
2440
|
+
const hash = await setExecutionWindow(parseBigIntArg(opts.seconds, "seconds"));
|
|
2441
|
+
spinner.succeed(G2(`Execution window updated to ${opts.seconds}s`));
|
|
2442
|
+
console.log(DIM2(` ${getExplorerUrl(hash)}`));
|
|
2443
|
+
} catch (err) {
|
|
2444
|
+
spinner.fail("Failed to set execution window");
|
|
2445
|
+
console.error(chalk6.red(err instanceof Error ? err.message : String(err)));
|
|
2446
|
+
process.exit(1);
|
|
2447
|
+
}
|
|
2448
|
+
});
|
|
2449
|
+
governor.command("set-quorum").description("Set the quorum threshold in bps (owner only)").requiredOption("--bps <n>", "New quorum in bps (e.g. 4000 = 40%)").action(async (opts) => {
|
|
2450
|
+
const spinner = ora6("Setting quorum...").start();
|
|
2451
|
+
try {
|
|
2452
|
+
const hash = await setQuorumBps(parseBigIntArg(opts.bps, "bps"));
|
|
2453
|
+
spinner.succeed(G2(`Quorum updated to ${Number(opts.bps) / 100}%`));
|
|
2454
|
+
console.log(DIM2(` ${getExplorerUrl(hash)}`));
|
|
2455
|
+
} catch (err) {
|
|
2456
|
+
spinner.fail("Failed to set quorum");
|
|
2457
|
+
console.error(chalk6.red(err instanceof Error ? err.message : String(err)));
|
|
2458
|
+
process.exit(1);
|
|
2459
|
+
}
|
|
2460
|
+
});
|
|
2461
|
+
governor.command("set-max-fee").description("Set the max performance fee in bps (owner only)").requiredOption("--bps <n>", "New max fee in bps (e.g. 3000 = 30%)").action(async (opts) => {
|
|
2462
|
+
const spinner = ora6("Setting max fee...").start();
|
|
2463
|
+
try {
|
|
2464
|
+
const hash = await setMaxPerformanceFeeBps(parseBigIntArg(opts.bps, "bps"));
|
|
2465
|
+
spinner.succeed(G2(`Max performance fee updated to ${Number(opts.bps) / 100}%`));
|
|
2466
|
+
console.log(DIM2(` ${getExplorerUrl(hash)}`));
|
|
2467
|
+
} catch (err) {
|
|
2468
|
+
spinner.fail("Failed to set max fee");
|
|
2469
|
+
console.error(chalk6.red(err instanceof Error ? err.message : String(err)));
|
|
2470
|
+
process.exit(1);
|
|
2471
|
+
}
|
|
2472
|
+
});
|
|
2473
|
+
governor.command("set-max-duration").description("Set the max strategy duration in seconds (owner only)").requiredOption("--seconds <n>", "New max duration in seconds").action(async (opts) => {
|
|
2474
|
+
const spinner = ora6("Setting max duration...").start();
|
|
2475
|
+
try {
|
|
2476
|
+
const hash = await setMaxStrategyDuration(parseBigIntArg(opts.seconds, "seconds"));
|
|
2477
|
+
spinner.succeed(G2(`Max strategy duration updated to ${opts.seconds}s`));
|
|
2478
|
+
console.log(DIM2(` ${getExplorerUrl(hash)}`));
|
|
2479
|
+
} catch (err) {
|
|
2480
|
+
spinner.fail("Failed to set max duration");
|
|
2481
|
+
console.error(chalk6.red(err instanceof Error ? err.message : String(err)));
|
|
2482
|
+
process.exit(1);
|
|
2483
|
+
}
|
|
2484
|
+
});
|
|
2485
|
+
governor.command("set-cooldown").description("Set the cooldown period in seconds (owner only)").requiredOption("--seconds <n>", "New cooldown in seconds").action(async (opts) => {
|
|
2486
|
+
const spinner = ora6("Setting cooldown...").start();
|
|
2487
|
+
try {
|
|
2488
|
+
const hash = await setCooldownPeriod(parseBigIntArg(opts.seconds, "seconds"));
|
|
2489
|
+
spinner.succeed(G2(`Cooldown period updated to ${opts.seconds}s`));
|
|
2490
|
+
console.log(DIM2(` ${getExplorerUrl(hash)}`));
|
|
2491
|
+
} catch (err) {
|
|
2492
|
+
spinner.fail("Failed to set cooldown");
|
|
2493
|
+
console.error(chalk6.red(err instanceof Error ? err.message : String(err)));
|
|
2494
|
+
process.exit(1);
|
|
2495
|
+
}
|
|
2496
|
+
});
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2499
|
+
// src/index.ts
|
|
2500
|
+
try {
|
|
2501
|
+
loadDotenv();
|
|
2502
|
+
} catch {
|
|
2503
|
+
}
|
|
2504
|
+
async function loadXmtp() {
|
|
2505
|
+
return import("./xmtp-S4VRXMFK.js");
|
|
2506
|
+
}
|
|
2507
|
+
var G3 = chalk7.green;
|
|
2508
|
+
var W3 = chalk7.white;
|
|
2509
|
+
var DIM3 = chalk7.gray;
|
|
2510
|
+
var BOLD3 = chalk7.white.bold;
|
|
2511
|
+
var LABEL3 = chalk7.green.bold;
|
|
2512
|
+
var SEP3 = () => console.log(DIM3("\u2500".repeat(60)));
|
|
1691
2513
|
function resolveVault(opts) {
|
|
1692
2514
|
if (opts.vault) {
|
|
1693
2515
|
setVaultAddress(opts.vault);
|
|
1694
2516
|
}
|
|
1695
2517
|
}
|
|
1696
2518
|
var program = new Command();
|
|
1697
|
-
program.name("sherwood").description("CLI for agent-managed investment syndicates").version("0.1.0").
|
|
2519
|
+
program.name("sherwood").description("CLI for agent-managed investment syndicates").version("0.1.0").addOption(
|
|
2520
|
+
new Option("--chain <network>", "Target network").choices(VALID_NETWORKS).default("base")
|
|
2521
|
+
).option("--testnet", "Alias for --chain base-sepolia (deprecated)", false).hook("preAction", (thisCommand) => {
|
|
1698
2522
|
const opts = thisCommand.optsWithGlobals();
|
|
1699
|
-
|
|
2523
|
+
let network = opts.chain;
|
|
1700
2524
|
if (opts.testnet) {
|
|
1701
|
-
|
|
2525
|
+
if (network !== "base") {
|
|
2526
|
+
console.warn(
|
|
2527
|
+
chalk7.yellow("[warn] --testnet ignored, --chain takes precedence")
|
|
2528
|
+
);
|
|
2529
|
+
} else {
|
|
2530
|
+
network = "base-sepolia";
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
setNetwork(network);
|
|
2534
|
+
if (getNetwork() !== "base") {
|
|
2535
|
+
console.log(chalk7.yellow(`[${getNetwork()}]`));
|
|
1702
2536
|
}
|
|
1703
2537
|
});
|
|
1704
2538
|
var syndicate = program.command("syndicate");
|
|
1705
|
-
syndicate.command("create").description("Create a new syndicate via the factory (interactive)").option("--subdomain <name>", "ENS subdomain (skip prompt)").option("--name <name>", "Syndicate name (skip prompt)").option("--agent-id <id>", "ERC-8004 agent identity token ID (skip prompt)").option("--asset <symbol-or-address>", "Vault asset: USDC, WETH, or a token address").option("--description <text>", "Short description").option("--metadata-uri <uri>", "Override metadata URI (skip IPFS upload)").option("--open-deposits", "Allow anyone to deposit (no whitelist)").option("--public-chat", "Enable dashboard spectator mode", false).action(async (opts) => {
|
|
2539
|
+
syndicate.command("create").description("Create a new syndicate via the factory (interactive)").option("--subdomain <name>", "ENS subdomain (skip prompt)").option("--name <name>", "Syndicate name (skip prompt)").option("--agent-id <id>", "ERC-8004 agent identity token ID (skip prompt)").option("--asset <symbol-or-address>", "Vault asset: USDC, WETH, or a token address").option("--description <text>", "Short description").option("--metadata-uri <uri>", "Override metadata URI (skip IPFS upload)").option("--open-deposits", "Allow anyone to deposit (no whitelist)").option("--public-chat", "Enable dashboard spectator mode", false).option("-y, --yes", "Skip confirmation prompt (non-interactive mode)", false).action(async (opts) => {
|
|
1706
2540
|
try {
|
|
1707
2541
|
console.log();
|
|
1708
|
-
console.log(
|
|
1709
|
-
|
|
2542
|
+
console.log(LABEL3(" \u25C6 Create Syndicate"));
|
|
2543
|
+
SEP3();
|
|
1710
2544
|
const wallet = getAccount();
|
|
1711
|
-
console.log(
|
|
1712
|
-
console.log(
|
|
1713
|
-
|
|
2545
|
+
console.log(DIM3(` Wallet: ${wallet.address}`));
|
|
2546
|
+
console.log(DIM3(` Network: ${getChain().name}`));
|
|
2547
|
+
SEP3();
|
|
1714
2548
|
const savedAgentId = getAgentId();
|
|
1715
|
-
const
|
|
1716
|
-
|
|
2549
|
+
const nonInteractive = opts.yes;
|
|
2550
|
+
const name = opts.name || (nonInteractive ? (() => {
|
|
2551
|
+
throw new Error("--name is required in non-interactive mode (-y)");
|
|
2552
|
+
})() : await input({
|
|
2553
|
+
message: G3("Syndicate name"),
|
|
1717
2554
|
validate: (v) => v.length > 0 || "Name is required"
|
|
1718
|
-
});
|
|
1719
|
-
const subdomain = opts.subdomain || await input({
|
|
1720
|
-
message:
|
|
2555
|
+
}));
|
|
2556
|
+
const subdomain = opts.subdomain || (nonInteractive ? name.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "") : await input({
|
|
2557
|
+
message: G3("ENS subdomain"),
|
|
1721
2558
|
default: name.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, ""),
|
|
1722
2559
|
validate: (v) => v.length >= 3 || "Must be at least 3 characters"
|
|
1723
|
-
});
|
|
1724
|
-
const description = opts.description || await input({
|
|
1725
|
-
message:
|
|
2560
|
+
}));
|
|
2561
|
+
const description = opts.description || (nonInteractive ? `${name} \u2014 a Sherwood syndicate` : await input({
|
|
2562
|
+
message: G3("Description"),
|
|
1726
2563
|
default: `${name} \u2014 a Sherwood syndicate`
|
|
1727
|
-
});
|
|
1728
|
-
const agentIdStr = opts.agentId || (savedAgentId ? await input({ message:
|
|
1729
|
-
const openDeposits = opts.openDeposits !== void 0 ? opts.openDeposits : await confirm({
|
|
1730
|
-
message:
|
|
2564
|
+
}));
|
|
2565
|
+
const agentIdStr = opts.agentId || (savedAgentId ? nonInteractive ? String(savedAgentId) : await input({ message: G3("Agent ID (ERC-8004)"), default: String(savedAgentId) }) : nonInteractive ? "0" : await input({ message: G3("Agent ID (ERC-8004)"), validate: (v) => /^\d+$/.test(v) || "Must be a number" }));
|
|
2566
|
+
const openDeposits = opts.openDeposits !== void 0 ? opts.openDeposits : nonInteractive ? true : await confirm({
|
|
2567
|
+
message: G3("Open deposits? (anyone can deposit)"),
|
|
1731
2568
|
default: true
|
|
1732
2569
|
});
|
|
1733
2570
|
const ASSET_SYMBOLS = {
|
|
@@ -1743,12 +2580,14 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
1743
2580
|
asset = opts.asset;
|
|
1744
2581
|
} else {
|
|
1745
2582
|
const supported = Object.keys(ASSET_SYMBOLS).join(", ");
|
|
1746
|
-
console.error(
|
|
2583
|
+
console.error(chalk7.red(` Unknown asset "${opts.asset}". Use a symbol (${supported}) or a 0x address.`));
|
|
1747
2584
|
process.exit(1);
|
|
1748
2585
|
}
|
|
2586
|
+
} else if (nonInteractive) {
|
|
2587
|
+
asset = TOKENS().USDC !== "0x0000000000000000000000000000000000000000" ? ASSET_SYMBOLS.USDC : ASSET_SYMBOLS.WETH;
|
|
1749
2588
|
} else {
|
|
1750
2589
|
const assetChoice = await select({
|
|
1751
|
-
message:
|
|
2590
|
+
message: G3("Vault asset (what token do depositors provide?)"),
|
|
1752
2591
|
choices: [
|
|
1753
2592
|
{ name: "USDC", value: "USDC", description: "USD Coin (6 decimals)" },
|
|
1754
2593
|
{ name: "WETH", value: "WETH", description: "Wrapped Ether (18 decimals)" }
|
|
@@ -1763,24 +2602,26 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
1763
2602
|
]);
|
|
1764
2603
|
const symbol = `sw${assetSymbol}`;
|
|
1765
2604
|
console.log();
|
|
1766
|
-
console.log(
|
|
1767
|
-
|
|
1768
|
-
console.log(
|
|
1769
|
-
console.log(
|
|
1770
|
-
console.log(
|
|
1771
|
-
console.log(
|
|
1772
|
-
console.log(
|
|
1773
|
-
console.log(
|
|
1774
|
-
console.log(
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
2605
|
+
console.log(LABEL3(" \u25C6 Review"));
|
|
2606
|
+
SEP3();
|
|
2607
|
+
console.log(W3(` Name: ${BOLD3(name)}`));
|
|
2608
|
+
console.log(W3(` ENS: ${G3(`${subdomain}.sherwoodagent.eth`)}`));
|
|
2609
|
+
console.log(W3(` Description: ${DIM3(description)}`));
|
|
2610
|
+
console.log(W3(` Agent ID: #${agentIdStr}`));
|
|
2611
|
+
console.log(W3(` Asset: ${assetSymbol} (${asset.slice(0, 10)}...)`));
|
|
2612
|
+
console.log(W3(` Share token: ${symbol}`));
|
|
2613
|
+
console.log(W3(` Open deposits: ${openDeposits ? G3("yes") : chalk7.red("no (whitelist)")}`));
|
|
2614
|
+
SEP3();
|
|
2615
|
+
if (!nonInteractive) {
|
|
2616
|
+
const go = await confirm({ message: G3("Deploy syndicate?"), default: true });
|
|
2617
|
+
if (!go) {
|
|
2618
|
+
console.log(DIM3(" Cancelled."));
|
|
2619
|
+
return;
|
|
2620
|
+
}
|
|
1780
2621
|
}
|
|
1781
2622
|
let metadataURI = opts.metadataUri || "";
|
|
1782
2623
|
if (!metadataURI) {
|
|
1783
|
-
const spinner2 =
|
|
2624
|
+
const spinner2 = ora7({ text: W3("Uploading metadata to IPFS..."), color: "green" }).start();
|
|
1784
2625
|
try {
|
|
1785
2626
|
const metadata = {
|
|
1786
2627
|
schema: "sherwood/syndicate/v1",
|
|
@@ -1795,14 +2636,14 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
1795
2636
|
links: {}
|
|
1796
2637
|
};
|
|
1797
2638
|
metadataURI = await uploadMetadata(metadata);
|
|
1798
|
-
spinner2.succeed(
|
|
2639
|
+
spinner2.succeed(G3(`Metadata pinned: ${DIM3(metadataURI)}`));
|
|
1799
2640
|
} catch (err) {
|
|
1800
|
-
spinner2.warn(
|
|
2641
|
+
spinner2.warn(chalk7.yellow(`IPFS upload failed \u2014 using inline metadata`));
|
|
1801
2642
|
const json = JSON.stringify({ name, description, subdomain, asset: assetSymbol, openDeposits, createdBy: "@sherwoodagent/cli" });
|
|
1802
2643
|
metadataURI = `data:application/json;base64,${Buffer.from(json).toString("base64")}`;
|
|
1803
2644
|
}
|
|
1804
2645
|
}
|
|
1805
|
-
const spinner =
|
|
2646
|
+
const spinner = ora7({ text: W3("Deploying vault via factory..."), color: "green" }).start();
|
|
1806
2647
|
const result = await createSyndicate({
|
|
1807
2648
|
creatorAgentId: BigInt(agentIdStr),
|
|
1808
2649
|
metadataURI,
|
|
@@ -1813,21 +2654,19 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
1813
2654
|
subdomain
|
|
1814
2655
|
});
|
|
1815
2656
|
setChainContract(getChain().id, "vault", result.vault);
|
|
1816
|
-
spinner.text =
|
|
2657
|
+
spinner.text = W3("Registering creator as agent...");
|
|
1817
2658
|
try {
|
|
1818
2659
|
setVaultAddress(result.vault);
|
|
1819
2660
|
const creatorAddress = getAccount().address;
|
|
1820
2661
|
await registerAgent(
|
|
1821
2662
|
BigInt(agentIdStr),
|
|
1822
|
-
creatorAddress,
|
|
1823
|
-
// pkp = creator EOA (direct execution)
|
|
1824
2663
|
creatorAddress
|
|
1825
|
-
//
|
|
2664
|
+
// agentAddress = creator EOA (direct execution)
|
|
1826
2665
|
);
|
|
1827
2666
|
} catch (regErr) {
|
|
1828
|
-
console.warn(
|
|
2667
|
+
console.warn(chalk7.yellow("\n \u26A0 Could not auto-register creator as agent \u2014 register manually with `syndicate add`"));
|
|
1829
2668
|
}
|
|
1830
|
-
spinner.text =
|
|
2669
|
+
spinner.text = W3("Setting up chat...");
|
|
1831
2670
|
try {
|
|
1832
2671
|
const xmtp = await loadXmtp();
|
|
1833
2672
|
const xmtpClient = await xmtp.getXmtpClient();
|
|
@@ -1835,30 +2674,30 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
1835
2674
|
await setTextRecord(subdomain, "xmtpGroupId", groupId, result.vault);
|
|
1836
2675
|
cacheGroupId(subdomain, groupId);
|
|
1837
2676
|
} catch {
|
|
1838
|
-
console.warn(
|
|
1839
|
-
console.warn(
|
|
2677
|
+
console.warn(chalk7.yellow("\n \u26A0 Could not create XMTP chat group"));
|
|
2678
|
+
console.warn(chalk7.dim(` Recover later with: sherwood chat ${subdomain} init`));
|
|
1840
2679
|
}
|
|
1841
2680
|
spinner.stop();
|
|
1842
2681
|
console.log();
|
|
1843
|
-
console.log(
|
|
1844
|
-
|
|
1845
|
-
console.log(
|
|
1846
|
-
console.log(
|
|
1847
|
-
console.log(
|
|
1848
|
-
console.log(
|
|
1849
|
-
console.log(
|
|
1850
|
-
console.log(
|
|
1851
|
-
|
|
1852
|
-
console.log(
|
|
2682
|
+
console.log(LABEL3(" \u25C6 Syndicate Created"));
|
|
2683
|
+
SEP3();
|
|
2684
|
+
console.log(W3(` ID: ${G3(`#${result.syndicateId}`)}`));
|
|
2685
|
+
console.log(W3(` Vault: ${G3(result.vault)}`));
|
|
2686
|
+
console.log(W3(` ENS: ${G3(`${subdomain}.sherwoodagent.eth`)}`));
|
|
2687
|
+
console.log(W3(` Metadata: ${DIM3(metadataURI.length > 50 ? metadataURI.slice(0, 50) + "..." : metadataURI)}`));
|
|
2688
|
+
console.log(W3(` Explorer: ${DIM3(getExplorerUrl(result.hash))}`));
|
|
2689
|
+
console.log(W3(` Chat: ${DIM3(`sherwood chat ${subdomain}`)}`));
|
|
2690
|
+
SEP3();
|
|
2691
|
+
console.log(G3(" \u2713 Vault saved to ~/.sherwood/config.json"));
|
|
1853
2692
|
console.log();
|
|
1854
2693
|
} catch (err) {
|
|
1855
|
-
console.error(
|
|
2694
|
+
console.error(chalk7.red(`
|
|
1856
2695
|
\u2716 ${err instanceof Error ? err.message : String(err)}`));
|
|
1857
2696
|
process.exit(1);
|
|
1858
2697
|
}
|
|
1859
2698
|
});
|
|
1860
2699
|
syndicate.command("list").description("List active syndicates (queries subgraph, falls back to on-chain)").option("--creator <address>", "Filter by creator address").action(async (opts) => {
|
|
1861
|
-
const spinner =
|
|
2700
|
+
const spinner = ora7("Loading syndicates...").start();
|
|
1862
2701
|
try {
|
|
1863
2702
|
let syndicates;
|
|
1864
2703
|
if (process.env.SUBGRAPH_URL) {
|
|
@@ -1877,65 +2716,65 @@ syndicate.command("list").description("List active syndicates (queries subgraph,
|
|
|
1877
2716
|
}
|
|
1878
2717
|
spinner.stop();
|
|
1879
2718
|
if (syndicates.length === 0) {
|
|
1880
|
-
console.log(
|
|
2719
|
+
console.log(chalk7.dim("No active syndicates found."));
|
|
1881
2720
|
return;
|
|
1882
2721
|
}
|
|
1883
2722
|
console.log();
|
|
1884
|
-
console.log(
|
|
2723
|
+
console.log(chalk7.bold(`Active Syndicates (${syndicates.length})`));
|
|
1885
2724
|
if (!process.env.SUBGRAPH_URL) {
|
|
1886
|
-
console.log(
|
|
2725
|
+
console.log(chalk7.dim(" (Set SUBGRAPH_URL for faster indexed queries)"));
|
|
1887
2726
|
}
|
|
1888
|
-
console.log(
|
|
2727
|
+
console.log(chalk7.dim("\u2500".repeat(70)));
|
|
1889
2728
|
for (const s of syndicates) {
|
|
1890
2729
|
const ts = typeof s.createdAt === "string" ? Number(s.createdAt) : Number(s.createdAt);
|
|
1891
2730
|
const date = new Date(ts * 1e3).toLocaleDateString();
|
|
1892
2731
|
const ensName = s.subdomain ? `${s.subdomain}.sherwoodagent.eth` : "";
|
|
1893
|
-
console.log(` #${s.id} ${
|
|
1894
|
-
if (ensName) console.log(` Vault: ${
|
|
2732
|
+
console.log(` #${s.id} ${chalk7.bold(ensName || String(s.vault))}`);
|
|
2733
|
+
if (ensName) console.log(` Vault: ${chalk7.cyan(String(s.vault))}`);
|
|
1895
2734
|
console.log(` Creator: ${s.creator}`);
|
|
1896
2735
|
console.log(` Created: ${date}`);
|
|
1897
2736
|
if (s.totalDeposits) {
|
|
1898
2737
|
console.log(` Deposits: ${s.totalDeposits} USDC`);
|
|
1899
2738
|
}
|
|
1900
2739
|
if (s.metadataURI) {
|
|
1901
|
-
console.log(` Metadata: ${
|
|
2740
|
+
console.log(` Metadata: ${chalk7.dim(s.metadataURI)}`);
|
|
1902
2741
|
}
|
|
1903
2742
|
console.log();
|
|
1904
2743
|
}
|
|
1905
2744
|
} catch (err) {
|
|
1906
2745
|
spinner.fail("Failed to load syndicates");
|
|
1907
|
-
console.error(
|
|
2746
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
1908
2747
|
process.exit(1);
|
|
1909
2748
|
}
|
|
1910
2749
|
});
|
|
1911
2750
|
syndicate.command("info").description("Display syndicate details by ID").argument("<id>", "Syndicate ID").action(async (idStr) => {
|
|
1912
|
-
const spinner =
|
|
2751
|
+
const spinner = ora7("Loading syndicate info...").start();
|
|
1913
2752
|
try {
|
|
1914
2753
|
const id = BigInt(idStr);
|
|
1915
2754
|
const info = await getSyndicate(id);
|
|
1916
2755
|
spinner.stop();
|
|
1917
2756
|
if (!info.vault || info.vault === "0x0000000000000000000000000000000000000000") {
|
|
1918
|
-
console.log(
|
|
2757
|
+
console.log(chalk7.red(`Syndicate #${id} not found.`));
|
|
1919
2758
|
process.exit(1);
|
|
1920
2759
|
}
|
|
1921
2760
|
const date = new Date(Number(info.createdAt) * 1e3).toLocaleDateString();
|
|
1922
2761
|
console.log();
|
|
1923
|
-
console.log(
|
|
1924
|
-
console.log(
|
|
2762
|
+
console.log(chalk7.bold(`Syndicate #${info.id}`));
|
|
2763
|
+
console.log(chalk7.dim("\u2500".repeat(40)));
|
|
1925
2764
|
if (info.subdomain) {
|
|
1926
|
-
console.log(` ENS: ${
|
|
2765
|
+
console.log(` ENS: ${chalk7.bold(`${info.subdomain}.sherwoodagent.eth`)}`);
|
|
1927
2766
|
}
|
|
1928
|
-
console.log(` Vault: ${
|
|
2767
|
+
console.log(` Vault: ${chalk7.cyan(info.vault)}`);
|
|
1929
2768
|
console.log(` Creator: ${info.creator}`);
|
|
1930
2769
|
console.log(` Created: ${date}`);
|
|
1931
|
-
console.log(` Active: ${info.active ?
|
|
2770
|
+
console.log(` Active: ${info.active ? chalk7.green("yes") : chalk7.red("no")}`);
|
|
1932
2771
|
if (info.metadataURI) {
|
|
1933
|
-
console.log(` Metadata: ${
|
|
2772
|
+
console.log(` Metadata: ${chalk7.dim(info.metadataURI)}`);
|
|
1934
2773
|
}
|
|
1935
2774
|
setVaultAddress(info.vault);
|
|
1936
2775
|
const vaultInfo = await getVaultInfo();
|
|
1937
2776
|
console.log();
|
|
1938
|
-
console.log(
|
|
2777
|
+
console.log(chalk7.bold(" Vault Stats"));
|
|
1939
2778
|
console.log(` Total Assets: ${vaultInfo.totalAssets}`);
|
|
1940
2779
|
console.log(` Agent Count: ${vaultInfo.agentCount}`);
|
|
1941
2780
|
console.log(` Redemptions Locked: ${vaultInfo.redemptionsLocked}`);
|
|
@@ -1943,12 +2782,12 @@ syndicate.command("info").description("Display syndicate details by ID").argumen
|
|
|
1943
2782
|
console.log();
|
|
1944
2783
|
} catch (err) {
|
|
1945
2784
|
spinner.fail("Failed to load syndicate info");
|
|
1946
|
-
console.error(
|
|
2785
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
1947
2786
|
process.exit(1);
|
|
1948
2787
|
}
|
|
1949
2788
|
});
|
|
1950
2789
|
syndicate.command("update-metadata").description("Update syndicate metadata (creator only)").requiredOption("--id <id>", "Syndicate ID").option("--name <name>", "Syndicate name").option("--description <text>", "Short description").option("--uri <uri>", "Direct metadata URI (skips IPFS upload)").action(async (opts) => {
|
|
1951
|
-
const spinner =
|
|
2790
|
+
const spinner = ora7({ text: W3("Loading syndicate..."), color: "green" }).start();
|
|
1952
2791
|
try {
|
|
1953
2792
|
const syndicateId = BigInt(opts.id);
|
|
1954
2793
|
let metadataURI = opts.uri;
|
|
@@ -1960,7 +2799,7 @@ syndicate.command("update-metadata").description("Update syndicate metadata (cre
|
|
|
1960
2799
|
}
|
|
1961
2800
|
const name = opts.name || info.subdomain;
|
|
1962
2801
|
const description = opts.description || `${name} \u2014 a Sherwood syndicate on ${info.subdomain}.sherwoodagent.eth`;
|
|
1963
|
-
spinner.text =
|
|
2802
|
+
spinner.text = W3("Uploading metadata to IPFS...");
|
|
1964
2803
|
const metadata = {
|
|
1965
2804
|
schema: "sherwood/syndicate/v1",
|
|
1966
2805
|
name,
|
|
@@ -1971,46 +2810,46 @@ syndicate.command("update-metadata").description("Update syndicate metadata (cre
|
|
|
1971
2810
|
links: {}
|
|
1972
2811
|
};
|
|
1973
2812
|
metadataURI = await uploadMetadata(metadata);
|
|
1974
|
-
spinner.text =
|
|
2813
|
+
spinner.text = W3("Updating on-chain metadata...");
|
|
1975
2814
|
}
|
|
1976
2815
|
const hash = await updateMetadata(syndicateId, metadataURI);
|
|
1977
|
-
spinner.succeed(
|
|
1978
|
-
console.log(
|
|
1979
|
-
console.log(
|
|
2816
|
+
spinner.succeed(G3(`Metadata updated`));
|
|
2817
|
+
console.log(DIM3(` IPFS: ${metadataURI}`));
|
|
2818
|
+
console.log(DIM3(` ${getExplorerUrl(hash)}`));
|
|
1980
2819
|
} catch (err) {
|
|
1981
2820
|
spinner.fail("Metadata update failed");
|
|
1982
|
-
console.error(
|
|
2821
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
1983
2822
|
process.exit(1);
|
|
1984
2823
|
}
|
|
1985
2824
|
});
|
|
1986
2825
|
syndicate.command("approve-depositor").description("Approve an address to deposit (owner only)").option("--vault <address>", "Vault address (default: from config)").requiredOption("--depositor <address>", "Address to approve").action(async (opts) => {
|
|
1987
2826
|
resolveVault(opts);
|
|
1988
|
-
const spinner =
|
|
2827
|
+
const spinner = ora7("Approving depositor...").start();
|
|
1989
2828
|
try {
|
|
1990
2829
|
const hash = await approveDepositor(opts.depositor);
|
|
1991
2830
|
spinner.succeed(`Depositor approved: ${hash}`);
|
|
1992
|
-
console.log(
|
|
2831
|
+
console.log(chalk7.dim(` ${getExplorerUrl(hash)}`));
|
|
1993
2832
|
} catch (err) {
|
|
1994
2833
|
spinner.fail("Approval failed");
|
|
1995
|
-
console.error(
|
|
2834
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
1996
2835
|
process.exit(1);
|
|
1997
2836
|
}
|
|
1998
2837
|
});
|
|
1999
2838
|
syndicate.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) => {
|
|
2000
2839
|
resolveVault(opts);
|
|
2001
|
-
const spinner =
|
|
2840
|
+
const spinner = ora7("Removing depositor...").start();
|
|
2002
2841
|
try {
|
|
2003
2842
|
const hash = await removeDepositor(opts.depositor);
|
|
2004
2843
|
spinner.succeed(`Depositor removed: ${hash}`);
|
|
2005
|
-
console.log(
|
|
2844
|
+
console.log(chalk7.dim(` ${getExplorerUrl(hash)}`));
|
|
2006
2845
|
} catch (err) {
|
|
2007
2846
|
spinner.fail("Removal failed");
|
|
2008
|
-
console.error(
|
|
2847
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
2009
2848
|
process.exit(1);
|
|
2010
2849
|
}
|
|
2011
2850
|
});
|
|
2012
|
-
syndicate.command("add").description("Register an agent on a syndicate vault (creator only)").option("--vault <address>", "Vault address (default: from config)").requiredOption("--agent-id <id>", "Agent's ERC-8004 identity token ID").requiredOption("--
|
|
2013
|
-
const spinner =
|
|
2851
|
+
syndicate.command("add").description("Register an agent on a syndicate vault (creator only)").option("--vault <address>", "Vault address (default: from config)").requiredOption("--agent-id <id>", "Agent's ERC-8004 identity token ID").requiredOption("--wallet <address>", "Agent wallet address").action(async (opts) => {
|
|
2852
|
+
const spinner = ora7("Verifying creator...").start();
|
|
2014
2853
|
try {
|
|
2015
2854
|
resolveVault(opts);
|
|
2016
2855
|
const vaultAddress = getVaultAddress();
|
|
@@ -2023,35 +2862,34 @@ syndicate.command("add").description("Register an agent on a syndicate vault (cr
|
|
|
2023
2862
|
spinner.text = "Registering agent...";
|
|
2024
2863
|
const hash = await registerAgent(
|
|
2025
2864
|
BigInt(opts.agentId),
|
|
2026
|
-
opts.
|
|
2027
|
-
opts.eoa
|
|
2865
|
+
opts.wallet
|
|
2028
2866
|
);
|
|
2029
2867
|
spinner.succeed(`Agent registered: ${hash}`);
|
|
2030
|
-
console.log(
|
|
2868
|
+
console.log(chalk7.dim(` ${getExplorerUrl(hash)}`));
|
|
2031
2869
|
try {
|
|
2032
2870
|
const xmtp = await loadXmtp();
|
|
2033
2871
|
const xmtpClient = await xmtp.getXmtpClient();
|
|
2034
2872
|
const group = await xmtp.getGroup(xmtpClient, subdomain);
|
|
2035
|
-
await xmtp.addMember(group, opts.
|
|
2873
|
+
await xmtp.addMember(group, opts.wallet);
|
|
2036
2874
|
await xmtp.sendEnvelope(group, {
|
|
2037
2875
|
type: "AGENT_REGISTERED",
|
|
2038
|
-
agent: { erc8004Id: Number(opts.agentId), address: opts.
|
|
2876
|
+
agent: { erc8004Id: Number(opts.agentId), address: opts.wallet },
|
|
2039
2877
|
syndicate: subdomain,
|
|
2040
2878
|
timestamp: Math.floor(Date.now() / 1e3)
|
|
2041
2879
|
});
|
|
2042
|
-
console.log(
|
|
2880
|
+
console.log(chalk7.dim(` Added to chat: ${subdomain}`));
|
|
2043
2881
|
} catch {
|
|
2044
|
-
console.warn(
|
|
2045
|
-
console.warn(
|
|
2882
|
+
console.warn(chalk7.yellow(" \u26A0 Could not add agent to chat group"));
|
|
2883
|
+
console.warn(chalk7.dim(` If no group exists, run: sherwood chat ${subdomain} init`));
|
|
2046
2884
|
}
|
|
2047
2885
|
} catch (err) {
|
|
2048
2886
|
spinner.fail("Registration failed");
|
|
2049
|
-
console.error(
|
|
2887
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
2050
2888
|
process.exit(1);
|
|
2051
2889
|
}
|
|
2052
2890
|
});
|
|
2053
2891
|
syndicate.command("join").description("Request to join a syndicate (creates an EAS attestation)").requiredOption("--subdomain <name>", "Syndicate subdomain to join").option("--message <text>", "Message to the creator", "Requesting to join your syndicate").action(async (opts) => {
|
|
2054
|
-
const spinner =
|
|
2892
|
+
const spinner = ora7("Resolving syndicate...").start();
|
|
2055
2893
|
try {
|
|
2056
2894
|
const agentId = getAgentId();
|
|
2057
2895
|
if (!agentId) {
|
|
@@ -2068,9 +2906,9 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
|
|
|
2068
2906
|
try {
|
|
2069
2907
|
const xmtp = await loadXmtp();
|
|
2070
2908
|
await xmtp.getXmtpClient();
|
|
2071
|
-
console.log(
|
|
2909
|
+
console.log(chalk7.dim(" XMTP identity ready"));
|
|
2072
2910
|
} catch {
|
|
2073
|
-
console.warn(
|
|
2911
|
+
console.warn(chalk7.yellow(" \u26A0 Could not initialize XMTP identity"));
|
|
2074
2912
|
}
|
|
2075
2913
|
return;
|
|
2076
2914
|
}
|
|
@@ -2081,14 +2919,14 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
|
|
|
2081
2919
|
);
|
|
2082
2920
|
if (existingRequest) {
|
|
2083
2921
|
spinner.succeed("You already have a pending join request for this syndicate");
|
|
2084
|
-
console.log(
|
|
2085
|
-
console.log(
|
|
2922
|
+
console.log(chalk7.dim(` Attestation: ${existingRequest.uid}`));
|
|
2923
|
+
console.log(chalk7.dim(` Submitted: ${new Date(existingRequest.time * 1e3).toLocaleString()}`));
|
|
2086
2924
|
try {
|
|
2087
2925
|
const xmtp = await loadXmtp();
|
|
2088
2926
|
await xmtp.getXmtpClient();
|
|
2089
|
-
console.log(
|
|
2927
|
+
console.log(chalk7.dim(" XMTP identity ready"));
|
|
2090
2928
|
} catch {
|
|
2091
|
-
console.warn(
|
|
2929
|
+
console.warn(chalk7.yellow(" \u26A0 Could not initialize XMTP identity"));
|
|
2092
2930
|
}
|
|
2093
2931
|
return;
|
|
2094
2932
|
}
|
|
@@ -2107,29 +2945,29 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
|
|
|
2107
2945
|
spinner.succeed("Join request created (XMTP identity ready)");
|
|
2108
2946
|
} catch {
|
|
2109
2947
|
spinner.succeed("Join request created");
|
|
2110
|
-
console.warn(
|
|
2948
|
+
console.warn(chalk7.yellow(" \u26A0 Could not initialize XMTP identity \u2014 creator may not be able to auto-add you to chat"));
|
|
2111
2949
|
}
|
|
2112
2950
|
console.log();
|
|
2113
|
-
console.log(
|
|
2114
|
-
|
|
2115
|
-
console.log(
|
|
2116
|
-
console.log(
|
|
2117
|
-
console.log(
|
|
2118
|
-
console.log(
|
|
2119
|
-
console.log(
|
|
2120
|
-
console.log(
|
|
2121
|
-
|
|
2122
|
-
console.log(
|
|
2123
|
-
console.log(
|
|
2951
|
+
console.log(LABEL3(" \u25C6 Join Request Submitted"));
|
|
2952
|
+
SEP3();
|
|
2953
|
+
console.log(W3(` Syndicate: ${G3(`${opts.subdomain}.sherwoodagent.eth`)}`));
|
|
2954
|
+
console.log(W3(` Agent ID: #${agentId}`));
|
|
2955
|
+
console.log(W3(` Creator: ${DIM3(syndicate2.creator)}`));
|
|
2956
|
+
console.log(W3(` Attestation: ${DIM3(uid)}`));
|
|
2957
|
+
console.log(W3(` EAS Scan: ${DIM3(getEasScanUrl(uid))}`));
|
|
2958
|
+
console.log(W3(` Explorer: ${DIM3(getExplorerUrl(hash))}`));
|
|
2959
|
+
SEP3();
|
|
2960
|
+
console.log(G3(" \u2713 The creator can review with:"));
|
|
2961
|
+
console.log(DIM3(` sherwood syndicate requests --subdomain ${opts.subdomain}`));
|
|
2124
2962
|
console.log();
|
|
2125
2963
|
} catch (err) {
|
|
2126
2964
|
spinner.fail("Join request failed");
|
|
2127
|
-
console.error(
|
|
2965
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
2128
2966
|
process.exit(1);
|
|
2129
2967
|
}
|
|
2130
2968
|
});
|
|
2131
2969
|
syndicate.command("requests").description("View pending join requests for a syndicate (creator only)").option("--subdomain <name>", "Syndicate subdomain").option("--vault <address>", "Vault address (default: from config)").action(async (opts) => {
|
|
2132
|
-
const spinner =
|
|
2970
|
+
const spinner = ora7("Loading join requests...").start();
|
|
2133
2971
|
try {
|
|
2134
2972
|
let creatorAddress;
|
|
2135
2973
|
let subdomain;
|
|
@@ -2160,34 +2998,34 @@ syndicate.command("requests").description("View pending join requests for a synd
|
|
|
2160
2998
|
);
|
|
2161
2999
|
spinner.stop();
|
|
2162
3000
|
if (requests.length === 0) {
|
|
2163
|
-
console.log(
|
|
3001
|
+
console.log(DIM3("\n No pending join requests.\n"));
|
|
2164
3002
|
return;
|
|
2165
3003
|
}
|
|
2166
3004
|
console.log();
|
|
2167
|
-
console.log(
|
|
2168
|
-
|
|
3005
|
+
console.log(LABEL3(` \u25C6 Pending Join Requests (${requests.length})`));
|
|
3006
|
+
SEP3();
|
|
2169
3007
|
for (let i = 0; i < requests.length; i++) {
|
|
2170
3008
|
const req = requests[i];
|
|
2171
3009
|
const date = new Date(req.time * 1e3).toLocaleString();
|
|
2172
|
-
console.log(
|
|
2173
|
-
console.log(
|
|
2174
|
-
console.log(
|
|
2175
|
-
console.log(
|
|
3010
|
+
console.log(W3(` ${i + 1}. Agent #${req.decoded.agentId} ${DIM3(`(${req.attester})`)}`));
|
|
3011
|
+
console.log(DIM3(` Message: "${req.decoded.message}"`));
|
|
3012
|
+
console.log(DIM3(` Requested: ${date}`));
|
|
3013
|
+
console.log(DIM3(` Attestation: ${req.uid}`));
|
|
2176
3014
|
console.log();
|
|
2177
3015
|
}
|
|
2178
|
-
console.log(
|
|
2179
|
-
console.log(
|
|
2180
|
-
console.log(
|
|
2181
|
-
console.log(
|
|
3016
|
+
console.log(G3(" To approve:"));
|
|
3017
|
+
console.log(DIM3(` sherwood syndicate approve --agent-id <id> --wallet <addr>`));
|
|
3018
|
+
console.log(G3(" To reject:"));
|
|
3019
|
+
console.log(DIM3(` sherwood syndicate reject --attestation <uid>`));
|
|
2182
3020
|
console.log();
|
|
2183
3021
|
} catch (err) {
|
|
2184
3022
|
spinner.fail("Failed to load requests");
|
|
2185
|
-
console.error(
|
|
3023
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
2186
3024
|
process.exit(1);
|
|
2187
3025
|
}
|
|
2188
3026
|
});
|
|
2189
|
-
syndicate.command("approve").description("Approve an agent join request (registers agent + creates EAS approval)").option("--vault <address>", "Vault address (default: from config)").option("--subdomain <name>", "Syndicate subdomain (alternative to --vault)").requiredOption("--agent-id <id>", "Agent's ERC-8004 identity token ID").requiredOption("--
|
|
2190
|
-
const spinner =
|
|
3027
|
+
syndicate.command("approve").description("Approve an agent join request (registers agent + creates EAS approval)").option("--vault <address>", "Vault address (default: from config)").option("--subdomain <name>", "Syndicate subdomain (alternative to --vault)").requiredOption("--agent-id <id>", "Agent's ERC-8004 identity token ID").requiredOption("--wallet <address>", "Agent wallet address").action(async (opts) => {
|
|
3028
|
+
const spinner = ora7("Verifying creator...").start();
|
|
2191
3029
|
try {
|
|
2192
3030
|
if (opts.subdomain && !opts.vault) {
|
|
2193
3031
|
const syndicateInfo = await resolveSyndicate(opts.subdomain);
|
|
@@ -2206,14 +3044,13 @@ syndicate.command("approve").description("Approve an agent join request (registe
|
|
|
2206
3044
|
try {
|
|
2207
3045
|
const regHash = await registerAgent(
|
|
2208
3046
|
BigInt(opts.agentId),
|
|
2209
|
-
opts.
|
|
2210
|
-
opts.eoa
|
|
3047
|
+
opts.wallet
|
|
2211
3048
|
);
|
|
2212
|
-
console.log(
|
|
3049
|
+
console.log(DIM3(` Agent registered: ${getExplorerUrl(regHash)}`));
|
|
2213
3050
|
} catch (regErr) {
|
|
2214
3051
|
const msg = regErr instanceof Error ? regErr.message : String(regErr);
|
|
2215
3052
|
if (msg.includes("0xe098d3ee") || msg.includes("AgentAlreadyRegistered")) {
|
|
2216
|
-
console.log(
|
|
3053
|
+
console.log(DIM3(" Agent already registered on vault \u2014 skipping"));
|
|
2217
3054
|
} else {
|
|
2218
3055
|
throw regErr;
|
|
2219
3056
|
}
|
|
@@ -2226,14 +3063,14 @@ syndicate.command("approve").description("Approve an agent join request (registe
|
|
|
2226
3063
|
let approvalUid;
|
|
2227
3064
|
if (alreadyApproved) {
|
|
2228
3065
|
approvalUid = alreadyApproved.uid;
|
|
2229
|
-
console.log(
|
|
3066
|
+
console.log(DIM3(` Approval attestation already exists \u2014 skipping`));
|
|
2230
3067
|
} else {
|
|
2231
3068
|
spinner.text = "Creating approval attestation...";
|
|
2232
3069
|
const result = await createApproval(
|
|
2233
3070
|
syndicateId,
|
|
2234
3071
|
BigInt(opts.agentId),
|
|
2235
3072
|
vaultAddress,
|
|
2236
|
-
opts.
|
|
3073
|
+
opts.wallet
|
|
2237
3074
|
);
|
|
2238
3075
|
approvalUid = result.uid;
|
|
2239
3076
|
}
|
|
@@ -2242,46 +3079,45 @@ syndicate.command("approve").description("Approve an agent join request (registe
|
|
|
2242
3079
|
const xmtp = await loadXmtp();
|
|
2243
3080
|
const xmtpClient = await xmtp.getXmtpClient();
|
|
2244
3081
|
const group = await xmtp.getGroup(xmtpClient, subdomain);
|
|
2245
|
-
await xmtp.addMember(group, opts.
|
|
3082
|
+
await xmtp.addMember(group, opts.wallet);
|
|
2246
3083
|
await xmtp.sendEnvelope(group, {
|
|
2247
3084
|
type: "AGENT_REGISTERED",
|
|
2248
|
-
agent: { erc8004Id: Number(opts.agentId), address: opts.
|
|
3085
|
+
agent: { erc8004Id: Number(opts.agentId), address: opts.wallet },
|
|
2249
3086
|
syndicate: subdomain,
|
|
2250
3087
|
timestamp: Math.floor(Date.now() / 1e3)
|
|
2251
3088
|
});
|
|
2252
|
-
console.log(
|
|
3089
|
+
console.log(DIM3(` Added to chat: ${subdomain}`));
|
|
2253
3090
|
} catch {
|
|
2254
|
-
console.warn(
|
|
2255
|
-
console.warn(
|
|
3091
|
+
console.warn(chalk7.yellow(" \u26A0 Could not add agent to chat group"));
|
|
3092
|
+
console.warn(chalk7.dim(` If no group exists, run: sherwood chat ${subdomain} init`));
|
|
2256
3093
|
}
|
|
2257
3094
|
spinner.succeed("Agent approved and registered");
|
|
2258
3095
|
console.log();
|
|
2259
|
-
console.log(
|
|
2260
|
-
|
|
2261
|
-
console.log(
|
|
2262
|
-
console.log(
|
|
2263
|
-
console.log(
|
|
2264
|
-
console.log(
|
|
2265
|
-
|
|
2266
|
-
SEP();
|
|
3096
|
+
console.log(LABEL3(" \u25C6 Agent Approved"));
|
|
3097
|
+
SEP3();
|
|
3098
|
+
console.log(W3(` Agent ID: #${opts.agentId}`));
|
|
3099
|
+
console.log(W3(` Wallet: ${G3(opts.wallet)}`));
|
|
3100
|
+
console.log(W3(` Approval: ${DIM3(approvalUid)}`));
|
|
3101
|
+
console.log(W3(` EAS Scan: ${DIM3(getEasScanUrl(approvalUid))}`));
|
|
3102
|
+
SEP3();
|
|
2267
3103
|
} catch (err) {
|
|
2268
3104
|
spinner.fail("Approval failed");
|
|
2269
|
-
console.error(
|
|
3105
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
2270
3106
|
process.exit(1);
|
|
2271
3107
|
}
|
|
2272
3108
|
});
|
|
2273
3109
|
syndicate.command("reject").description("Reject a join request by revoking its attestation").requiredOption("--attestation <uid>", "Join request attestation UID to revoke").action(async (opts) => {
|
|
2274
|
-
const spinner =
|
|
3110
|
+
const spinner = ora7("Revoking attestation...").start();
|
|
2275
3111
|
try {
|
|
2276
3112
|
const hash = await revokeAttestation(
|
|
2277
3113
|
EAS_SCHEMAS().SYNDICATE_JOIN_REQUEST,
|
|
2278
3114
|
opts.attestation
|
|
2279
3115
|
);
|
|
2280
3116
|
spinner.succeed("Join request rejected");
|
|
2281
|
-
console.log(
|
|
3117
|
+
console.log(DIM3(` ${getExplorerUrl(hash)}`));
|
|
2282
3118
|
} catch (err) {
|
|
2283
3119
|
spinner.fail("Rejection failed");
|
|
2284
|
-
console.error(
|
|
3120
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
2285
3121
|
process.exit(1);
|
|
2286
3122
|
}
|
|
2287
3123
|
});
|
|
@@ -2290,39 +3126,39 @@ vaultCmd.command("deposit").description("Deposit into a vault").option("--vault
|
|
|
2290
3126
|
resolveVault(opts);
|
|
2291
3127
|
const decimals = await getAssetDecimals();
|
|
2292
3128
|
const amount = parseUnits8(opts.amount, decimals);
|
|
2293
|
-
const spinner =
|
|
3129
|
+
const spinner = ora7(`Depositing ${opts.amount}...`).start();
|
|
2294
3130
|
try {
|
|
2295
3131
|
const hash = await deposit(amount);
|
|
2296
3132
|
spinner.succeed(`Deposited: ${hash}`);
|
|
2297
|
-
console.log(
|
|
3133
|
+
console.log(chalk7.dim(` ${getExplorerUrl(hash)}`));
|
|
2298
3134
|
} catch (err) {
|
|
2299
3135
|
spinner.fail("Deposit failed");
|
|
2300
|
-
console.error(
|
|
3136
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
2301
3137
|
process.exit(1);
|
|
2302
3138
|
}
|
|
2303
3139
|
});
|
|
2304
3140
|
vaultCmd.command("ragequit").description("Withdraw all shares from a vault").option("--vault <address>", "Vault address (default: from config)").action(async (opts) => {
|
|
2305
3141
|
resolveVault(opts);
|
|
2306
|
-
const spinner =
|
|
3142
|
+
const spinner = ora7("Ragequitting...").start();
|
|
2307
3143
|
try {
|
|
2308
3144
|
const hash = await ragequit();
|
|
2309
3145
|
spinner.succeed(`Ragequit: ${hash}`);
|
|
2310
|
-
console.log(
|
|
3146
|
+
console.log(chalk7.dim(` ${getExplorerUrl(hash)}`));
|
|
2311
3147
|
} catch (err) {
|
|
2312
3148
|
spinner.fail("Ragequit failed");
|
|
2313
|
-
console.error(
|
|
3149
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
2314
3150
|
process.exit(1);
|
|
2315
3151
|
}
|
|
2316
3152
|
});
|
|
2317
3153
|
vaultCmd.command("info").description("Display vault state").option("--vault <address>", "Vault address (default: from config)").action(async (opts) => {
|
|
2318
3154
|
resolveVault(opts);
|
|
2319
|
-
const spinner =
|
|
3155
|
+
const spinner = ora7("Loading vault info...").start();
|
|
2320
3156
|
try {
|
|
2321
3157
|
const info = await getVaultInfo();
|
|
2322
3158
|
spinner.stop();
|
|
2323
3159
|
console.log();
|
|
2324
|
-
console.log(
|
|
2325
|
-
console.log(
|
|
3160
|
+
console.log(chalk7.bold("Vault Info"));
|
|
3161
|
+
console.log(chalk7.dim("\u2500".repeat(40)));
|
|
2326
3162
|
console.log(` Address: ${info.address}`);
|
|
2327
3163
|
console.log(` Total Assets: ${info.totalAssets}`);
|
|
2328
3164
|
console.log(` Agent Count: ${info.agentCount}`);
|
|
@@ -2331,85 +3167,85 @@ vaultCmd.command("info").description("Display vault state").option("--vault <add
|
|
|
2331
3167
|
console.log();
|
|
2332
3168
|
} catch (err) {
|
|
2333
3169
|
spinner.fail("Failed to load vault info");
|
|
2334
|
-
console.error(
|
|
3170
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
2335
3171
|
process.exit(1);
|
|
2336
3172
|
}
|
|
2337
3173
|
});
|
|
2338
3174
|
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) => {
|
|
2339
3175
|
resolveVault(opts);
|
|
2340
|
-
const spinner =
|
|
3176
|
+
const spinner = ora7("Loading balance...").start();
|
|
2341
3177
|
try {
|
|
2342
3178
|
const balance = await getBalance(opts.address);
|
|
2343
3179
|
spinner.stop();
|
|
2344
3180
|
console.log();
|
|
2345
|
-
console.log(
|
|
2346
|
-
console.log(
|
|
3181
|
+
console.log(chalk7.bold("LP Position"));
|
|
3182
|
+
console.log(chalk7.dim("\u2500".repeat(40)));
|
|
2347
3183
|
console.log(` Shares: ${balance.shares.toString()}`);
|
|
2348
3184
|
console.log(` Asset Value: ${balance.assetsValue}`);
|
|
2349
3185
|
console.log(` % of Vault: ${balance.percentOfVault}`);
|
|
2350
3186
|
console.log();
|
|
2351
3187
|
} catch (err) {
|
|
2352
3188
|
spinner.fail("Failed to load balance");
|
|
2353
|
-
console.error(
|
|
3189
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
2354
3190
|
process.exit(1);
|
|
2355
3191
|
}
|
|
2356
3192
|
});
|
|
2357
3193
|
var strategy = program.command("strategy");
|
|
2358
3194
|
strategy.command("list").description("List registered strategies").option("--type <id>", "Filter by strategy type").action(async (opts) => {
|
|
2359
|
-
const spinner =
|
|
3195
|
+
const spinner = ora7("Loading strategies...").start();
|
|
2360
3196
|
try {
|
|
2361
3197
|
const strategies = await listStrategies(
|
|
2362
3198
|
opts.type ? BigInt(opts.type) : void 0
|
|
2363
3199
|
);
|
|
2364
3200
|
spinner.stop();
|
|
2365
3201
|
if (strategies.length === 0) {
|
|
2366
|
-
console.log(
|
|
3202
|
+
console.log(chalk7.dim("No strategies registered."));
|
|
2367
3203
|
return;
|
|
2368
3204
|
}
|
|
2369
3205
|
console.log();
|
|
2370
|
-
console.log(
|
|
2371
|
-
console.log(
|
|
3206
|
+
console.log(chalk7.bold(`Strategies (${strategies.length})`));
|
|
3207
|
+
console.log(chalk7.dim("\u2500".repeat(70)));
|
|
2372
3208
|
for (const s of strategies) {
|
|
2373
|
-
const status = s.active ?
|
|
2374
|
-
console.log(` #${s.id} ${
|
|
3209
|
+
const status = s.active ? chalk7.green("active") : chalk7.red("inactive");
|
|
3210
|
+
console.log(` #${s.id} ${chalk7.bold(s.name)} [type: ${s.strategyTypeId}] ${status}`);
|
|
2375
3211
|
console.log(` Creator: ${s.creator}`);
|
|
2376
3212
|
console.log(` Implementation: ${s.implementation}`);
|
|
2377
3213
|
if (s.metadataURI) {
|
|
2378
|
-
console.log(` Metadata: ${
|
|
3214
|
+
console.log(` Metadata: ${chalk7.dim(s.metadataURI)}`);
|
|
2379
3215
|
}
|
|
2380
3216
|
console.log();
|
|
2381
3217
|
}
|
|
2382
3218
|
} catch (err) {
|
|
2383
3219
|
spinner.fail("Failed to load strategies");
|
|
2384
|
-
console.error(
|
|
3220
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
2385
3221
|
process.exit(1);
|
|
2386
3222
|
}
|
|
2387
3223
|
});
|
|
2388
3224
|
strategy.command("info").description("Show strategy details").argument("<id>", "Strategy ID").action(async (idStr) => {
|
|
2389
|
-
const spinner =
|
|
3225
|
+
const spinner = ora7("Loading strategy...").start();
|
|
2390
3226
|
try {
|
|
2391
3227
|
const s = await getStrategy(BigInt(idStr));
|
|
2392
3228
|
spinner.stop();
|
|
2393
3229
|
console.log();
|
|
2394
|
-
console.log(
|
|
2395
|
-
console.log(
|
|
3230
|
+
console.log(chalk7.bold(`Strategy #${s.id}`));
|
|
3231
|
+
console.log(chalk7.dim("\u2500".repeat(40)));
|
|
2396
3232
|
console.log(` Name: ${s.name}`);
|
|
2397
3233
|
console.log(` Type: ${s.strategyTypeId}`);
|
|
2398
|
-
console.log(` Active: ${s.active ?
|
|
3234
|
+
console.log(` Active: ${s.active ? chalk7.green("yes") : chalk7.red("no")}`);
|
|
2399
3235
|
console.log(` Creator: ${s.creator}`);
|
|
2400
3236
|
console.log(` Implementation: ${s.implementation}`);
|
|
2401
3237
|
if (s.metadataURI) {
|
|
2402
|
-
console.log(` Metadata: ${
|
|
3238
|
+
console.log(` Metadata: ${chalk7.dim(s.metadataURI)}`);
|
|
2403
3239
|
}
|
|
2404
3240
|
console.log();
|
|
2405
3241
|
} catch (err) {
|
|
2406
3242
|
spinner.fail("Failed to load strategy");
|
|
2407
|
-
console.error(
|
|
3243
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
2408
3244
|
process.exit(1);
|
|
2409
3245
|
}
|
|
2410
3246
|
});
|
|
2411
3247
|
strategy.command("register").description("Register a new strategy on-chain").requiredOption("--implementation <address>", "Strategy contract address").requiredOption("--type <id>", "Strategy type ID").requiredOption("--name <name>", "Strategy name").option("--metadata <uri>", "Metadata URI (IPFS/Arweave)", "").action(async (opts) => {
|
|
2412
|
-
const spinner =
|
|
3248
|
+
const spinner = ora7("Registering strategy...").start();
|
|
2413
3249
|
try {
|
|
2414
3250
|
const hash = await registerStrategy(
|
|
2415
3251
|
opts.implementation,
|
|
@@ -2418,10 +3254,10 @@ strategy.command("register").description("Register a new strategy on-chain").req
|
|
|
2418
3254
|
opts.metadata
|
|
2419
3255
|
);
|
|
2420
3256
|
spinner.succeed(`Strategy registered: ${hash}`);
|
|
2421
|
-
console.log(
|
|
3257
|
+
console.log(chalk7.dim(` ${getExplorerUrl(hash)}`));
|
|
2422
3258
|
} catch (err) {
|
|
2423
3259
|
spinner.fail("Registration failed");
|
|
2424
|
-
console.error(
|
|
3260
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
2425
3261
|
process.exit(1);
|
|
2426
3262
|
}
|
|
2427
3263
|
});
|
|
@@ -2430,7 +3266,7 @@ strategy.command("run").description("Execute the levered swap strategy").option(
|
|
|
2430
3266
|
await runLeveredSwap(opts);
|
|
2431
3267
|
});
|
|
2432
3268
|
program.command("providers").description("List available DeFi providers").action(async () => {
|
|
2433
|
-
const { MessariProvider, NansenProvider } = await import("./research-
|
|
3269
|
+
const { MessariProvider, NansenProvider } = await import("./research-ZR7HXITG.js");
|
|
2434
3270
|
const providers = [new MoonwellProvider(), new UniswapProvider(), new MessariProvider(), new NansenProvider()];
|
|
2435
3271
|
for (const p of providers) {
|
|
2436
3272
|
const info = p.info();
|
|
@@ -2441,57 +3277,70 @@ ${info.name} (${info.type})`);
|
|
|
2441
3277
|
}
|
|
2442
3278
|
});
|
|
2443
3279
|
try {
|
|
2444
|
-
const { registerChatCommands } = await import("./chat-
|
|
3280
|
+
const { registerChatCommands } = await import("./chat-BNYWD3EL.js");
|
|
2445
3281
|
registerChatCommands(program);
|
|
2446
3282
|
} catch {
|
|
2447
3283
|
program.command("chat <name> [action] [actionArgs...]").description("Syndicate chat (XMTP) \u2014 requires @xmtp/cli").action(() => {
|
|
2448
|
-
console.error(
|
|
2449
|
-
console.error(
|
|
2450
|
-
console.error(
|
|
3284
|
+
console.error(chalk7.red("XMTP CLI not available."));
|
|
3285
|
+
console.error(chalk7.dim("Install with: npm install -g @xmtp/cli"));
|
|
3286
|
+
console.error(chalk7.dim("Or reinstall: npm i -g @sherwoodagent/cli"));
|
|
2451
3287
|
process.exit(1);
|
|
2452
3288
|
});
|
|
2453
3289
|
}
|
|
2454
|
-
var { registerSessionCommands } = await import("./session-
|
|
3290
|
+
var { registerSessionCommands } = await import("./session-QQSHCGNK.js");
|
|
2455
3291
|
registerSessionCommands(program);
|
|
2456
3292
|
registerVeniceCommands(program);
|
|
2457
3293
|
registerAllowanceCommands(program);
|
|
2458
3294
|
registerIdentityCommands(program);
|
|
2459
|
-
|
|
3295
|
+
registerProposalCommands(program);
|
|
3296
|
+
registerGovernorCommands(program);
|
|
3297
|
+
var { registerResearchCommands } = await import("./research-57SKO27M.js");
|
|
2460
3298
|
registerResearchCommands(program);
|
|
2461
3299
|
var configCmd = program.command("config");
|
|
2462
|
-
configCmd.command("set").description("Save settings to ~/.sherwood/config.json (persists across sessions)").option("--private-key <key>", "Wallet private key (0x-prefixed)").option("--vault <address>", "Default SyndicateVault address").action((opts) => {
|
|
3300
|
+
configCmd.command("set").description("Save settings to ~/.sherwood/config.json (persists across sessions)").option("--private-key <key>", "Wallet private key (0x-prefixed)").option("--vault <address>", "Default SyndicateVault address").option("--rpc <url>", "Custom RPC URL for the active --chain network").action((opts) => {
|
|
2463
3301
|
let saved = false;
|
|
2464
3302
|
if (opts.privateKey) {
|
|
2465
3303
|
setPrivateKey(opts.privateKey);
|
|
2466
3304
|
const account = getAccount();
|
|
2467
|
-
console.log(
|
|
2468
|
-
console.log(
|
|
3305
|
+
console.log(chalk7.green("Private key saved to ~/.sherwood/config.json"));
|
|
3306
|
+
console.log(chalk7.dim(` Wallet: ${account.address}`));
|
|
2469
3307
|
saved = true;
|
|
2470
3308
|
}
|
|
2471
3309
|
if (opts.vault) {
|
|
2472
3310
|
const chainId = getChain().id;
|
|
2473
3311
|
setChainContract(chainId, "vault", opts.vault);
|
|
2474
|
-
console.log(
|
|
2475
|
-
console.log(
|
|
3312
|
+
console.log(chalk7.green(`Vault saved to ~/.sherwood/config.json (chain ${chainId})`));
|
|
3313
|
+
console.log(chalk7.dim(` Vault: ${opts.vault}`));
|
|
3314
|
+
saved = true;
|
|
3315
|
+
}
|
|
3316
|
+
if (opts.rpc) {
|
|
3317
|
+
const network = getNetwork();
|
|
3318
|
+
setConfigRpcUrl(network, opts.rpc);
|
|
3319
|
+
console.log(chalk7.green(`RPC URL saved for ${network}`));
|
|
3320
|
+
console.log(chalk7.dim(` RPC: ${opts.rpc}`));
|
|
2476
3321
|
saved = true;
|
|
2477
3322
|
}
|
|
2478
3323
|
if (!saved) {
|
|
2479
|
-
console.log(
|
|
3324
|
+
console.log(chalk7.red("Provide at least one of: --private-key, --vault, --rpc"));
|
|
2480
3325
|
process.exit(1);
|
|
2481
3326
|
}
|
|
2482
3327
|
});
|
|
2483
3328
|
configCmd.command("show").description("Display current config for the active network").action(() => {
|
|
3329
|
+
const network = getNetwork();
|
|
2484
3330
|
const chainId = getChain().id;
|
|
2485
3331
|
const contracts = getChainContracts(chainId);
|
|
2486
3332
|
const config = loadConfig();
|
|
3333
|
+
const customRpc = config.rpc?.[network];
|
|
2487
3334
|
console.log();
|
|
2488
|
-
console.log(
|
|
2489
|
-
console.log(
|
|
2490
|
-
console.log(`
|
|
2491
|
-
console.log(`
|
|
2492
|
-
console.log(`
|
|
3335
|
+
console.log(chalk7.bold(`Sherwood Config`));
|
|
3336
|
+
console.log(chalk7.dim("\u2500".repeat(50)));
|
|
3337
|
+
console.log(` Network: ${chalk7.cyan(network)} (chain ${chainId})`);
|
|
3338
|
+
console.log(` RPC: ${customRpc ? chalk7.green(customRpc) : chalk7.dim("default")}`);
|
|
3339
|
+
console.log(` Wallet: ${config.privateKey ? chalk7.green("configured") : chalk7.dim("not set")}`);
|
|
3340
|
+
console.log(` Agent ID: ${config.agentId ?? chalk7.dim("not set")}`);
|
|
3341
|
+
console.log(` Vault: ${contracts.vault ?? chalk7.dim("not set")}`);
|
|
2493
3342
|
console.log();
|
|
2494
|
-
console.log(
|
|
3343
|
+
console.log(chalk7.dim(" Config file: ~/.sherwood/config.json"));
|
|
2495
3344
|
console.log();
|
|
2496
3345
|
});
|
|
2497
3346
|
program.parse();
|