@sherwoodagent/cli 0.7.2 → 0.8.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-O34BTHII.js} +5 -5
- package/dist/{chunk-5F5LFNIC.js → chunk-4MTHXGTK.js} +20 -11
- package/dist/chunk-4MTHXGTK.js.map +1 -0
- package/dist/{chunk-QMWMT6EH.js → chunk-5ADWTXNT.js} +115 -31
- package/dist/chunk-5ADWTXNT.js.map +1 -0
- package/dist/{chunk-B7XDUFI3.js → chunk-ARZIQ7YZ.js} +3 -3
- package/dist/{chunk-6MEYSN2W.js → chunk-BXUNWV52.js} +480 -36
- package/dist/chunk-BXUNWV52.js.map +1 -0
- package/dist/{chunk-P3GYAMGZ.js → chunk-VQP4XR67.js} +15 -5
- package/dist/chunk-VQP4XR67.js.map +1 -0
- package/dist/{eas-TMHFTX43.js → eas-TI4XI5VU.js} +4 -4
- package/dist/index.js +1064 -210
- package/dist/index.js.map +1 -1
- package/dist/{research-SESA7KGU.js → research-GX32VMP7.js} +5 -5
- package/dist/{research-JMGCIJ4H.js → research-ZR7HXITG.js} +3 -3
- package/dist/{session-XUOMZWOT.js → session-QEIVURQO.js} +5 -5
- package/dist/{xmtp-4XTQQ7RE.js → xmtp-A6F63GZH.js} +6 -6
- package/dist/xmtp-A6F63GZH.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-5F5LFNIC.js.map +0 -1
- package/dist/chunk-6MEYSN2W.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-O34BTHII.js.map} +0 -0
- /package/dist/{chunk-B7XDUFI3.js.map → chunk-ARZIQ7YZ.js.map} +0 -0
- /package/dist/{eas-TMHFTX43.js.map → eas-TI4XI5VU.js.map} +0 -0
- /package/dist/{research-SESA7KGU.js.map → research-GX32VMP7.js.map} +0 -0
- /package/dist/{research-JMGCIJ4H.js.map → research-ZR7HXITG.js.map} +0 -0
- /package/dist/{session-XUOMZWOT.js.map → session-QEIVURQO.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-VQP4XR67.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-ARZIQ7YZ.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-BXUNWV52.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
|
|
@@ -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-A6F63GZH.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,7 +2654,7 @@ 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;
|
|
@@ -1825,9 +2666,9 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
1825
2666
|
// operator = creator EOA
|
|
1826
2667
|
);
|
|
1827
2668
|
} catch (regErr) {
|
|
1828
|
-
console.warn(
|
|
2669
|
+
console.warn(chalk7.yellow("\n \u26A0 Could not auto-register creator as agent \u2014 register manually with `syndicate add`"));
|
|
1829
2670
|
}
|
|
1830
|
-
spinner.text =
|
|
2671
|
+
spinner.text = W3("Setting up chat...");
|
|
1831
2672
|
try {
|
|
1832
2673
|
const xmtp = await loadXmtp();
|
|
1833
2674
|
const xmtpClient = await xmtp.getXmtpClient();
|
|
@@ -1835,30 +2676,30 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
1835
2676
|
await setTextRecord(subdomain, "xmtpGroupId", groupId, result.vault);
|
|
1836
2677
|
cacheGroupId(subdomain, groupId);
|
|
1837
2678
|
} catch {
|
|
1838
|
-
console.warn(
|
|
1839
|
-
console.warn(
|
|
2679
|
+
console.warn(chalk7.yellow("\n \u26A0 Could not create XMTP chat group"));
|
|
2680
|
+
console.warn(chalk7.dim(` Recover later with: sherwood chat ${subdomain} init`));
|
|
1840
2681
|
}
|
|
1841
2682
|
spinner.stop();
|
|
1842
2683
|
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(
|
|
2684
|
+
console.log(LABEL3(" \u25C6 Syndicate Created"));
|
|
2685
|
+
SEP3();
|
|
2686
|
+
console.log(W3(` ID: ${G3(`#${result.syndicateId}`)}`));
|
|
2687
|
+
console.log(W3(` Vault: ${G3(result.vault)}`));
|
|
2688
|
+
console.log(W3(` ENS: ${G3(`${subdomain}.sherwoodagent.eth`)}`));
|
|
2689
|
+
console.log(W3(` Metadata: ${DIM3(metadataURI.length > 50 ? metadataURI.slice(0, 50) + "..." : metadataURI)}`));
|
|
2690
|
+
console.log(W3(` Explorer: ${DIM3(getExplorerUrl(result.hash))}`));
|
|
2691
|
+
console.log(W3(` Chat: ${DIM3(`sherwood chat ${subdomain}`)}`));
|
|
2692
|
+
SEP3();
|
|
2693
|
+
console.log(G3(" \u2713 Vault saved to ~/.sherwood/config.json"));
|
|
1853
2694
|
console.log();
|
|
1854
2695
|
} catch (err) {
|
|
1855
|
-
console.error(
|
|
2696
|
+
console.error(chalk7.red(`
|
|
1856
2697
|
\u2716 ${err instanceof Error ? err.message : String(err)}`));
|
|
1857
2698
|
process.exit(1);
|
|
1858
2699
|
}
|
|
1859
2700
|
});
|
|
1860
2701
|
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 =
|
|
2702
|
+
const spinner = ora7("Loading syndicates...").start();
|
|
1862
2703
|
try {
|
|
1863
2704
|
let syndicates;
|
|
1864
2705
|
if (process.env.SUBGRAPH_URL) {
|
|
@@ -1877,65 +2718,65 @@ syndicate.command("list").description("List active syndicates (queries subgraph,
|
|
|
1877
2718
|
}
|
|
1878
2719
|
spinner.stop();
|
|
1879
2720
|
if (syndicates.length === 0) {
|
|
1880
|
-
console.log(
|
|
2721
|
+
console.log(chalk7.dim("No active syndicates found."));
|
|
1881
2722
|
return;
|
|
1882
2723
|
}
|
|
1883
2724
|
console.log();
|
|
1884
|
-
console.log(
|
|
2725
|
+
console.log(chalk7.bold(`Active Syndicates (${syndicates.length})`));
|
|
1885
2726
|
if (!process.env.SUBGRAPH_URL) {
|
|
1886
|
-
console.log(
|
|
2727
|
+
console.log(chalk7.dim(" (Set SUBGRAPH_URL for faster indexed queries)"));
|
|
1887
2728
|
}
|
|
1888
|
-
console.log(
|
|
2729
|
+
console.log(chalk7.dim("\u2500".repeat(70)));
|
|
1889
2730
|
for (const s of syndicates) {
|
|
1890
2731
|
const ts = typeof s.createdAt === "string" ? Number(s.createdAt) : Number(s.createdAt);
|
|
1891
2732
|
const date = new Date(ts * 1e3).toLocaleDateString();
|
|
1892
2733
|
const ensName = s.subdomain ? `${s.subdomain}.sherwoodagent.eth` : "";
|
|
1893
|
-
console.log(` #${s.id} ${
|
|
1894
|
-
if (ensName) console.log(` Vault: ${
|
|
2734
|
+
console.log(` #${s.id} ${chalk7.bold(ensName || String(s.vault))}`);
|
|
2735
|
+
if (ensName) console.log(` Vault: ${chalk7.cyan(String(s.vault))}`);
|
|
1895
2736
|
console.log(` Creator: ${s.creator}`);
|
|
1896
2737
|
console.log(` Created: ${date}`);
|
|
1897
2738
|
if (s.totalDeposits) {
|
|
1898
2739
|
console.log(` Deposits: ${s.totalDeposits} USDC`);
|
|
1899
2740
|
}
|
|
1900
2741
|
if (s.metadataURI) {
|
|
1901
|
-
console.log(` Metadata: ${
|
|
2742
|
+
console.log(` Metadata: ${chalk7.dim(s.metadataURI)}`);
|
|
1902
2743
|
}
|
|
1903
2744
|
console.log();
|
|
1904
2745
|
}
|
|
1905
2746
|
} catch (err) {
|
|
1906
2747
|
spinner.fail("Failed to load syndicates");
|
|
1907
|
-
console.error(
|
|
2748
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
1908
2749
|
process.exit(1);
|
|
1909
2750
|
}
|
|
1910
2751
|
});
|
|
1911
2752
|
syndicate.command("info").description("Display syndicate details by ID").argument("<id>", "Syndicate ID").action(async (idStr) => {
|
|
1912
|
-
const spinner =
|
|
2753
|
+
const spinner = ora7("Loading syndicate info...").start();
|
|
1913
2754
|
try {
|
|
1914
2755
|
const id = BigInt(idStr);
|
|
1915
2756
|
const info = await getSyndicate(id);
|
|
1916
2757
|
spinner.stop();
|
|
1917
2758
|
if (!info.vault || info.vault === "0x0000000000000000000000000000000000000000") {
|
|
1918
|
-
console.log(
|
|
2759
|
+
console.log(chalk7.red(`Syndicate #${id} not found.`));
|
|
1919
2760
|
process.exit(1);
|
|
1920
2761
|
}
|
|
1921
2762
|
const date = new Date(Number(info.createdAt) * 1e3).toLocaleDateString();
|
|
1922
2763
|
console.log();
|
|
1923
|
-
console.log(
|
|
1924
|
-
console.log(
|
|
2764
|
+
console.log(chalk7.bold(`Syndicate #${info.id}`));
|
|
2765
|
+
console.log(chalk7.dim("\u2500".repeat(40)));
|
|
1925
2766
|
if (info.subdomain) {
|
|
1926
|
-
console.log(` ENS: ${
|
|
2767
|
+
console.log(` ENS: ${chalk7.bold(`${info.subdomain}.sherwoodagent.eth`)}`);
|
|
1927
2768
|
}
|
|
1928
|
-
console.log(` Vault: ${
|
|
2769
|
+
console.log(` Vault: ${chalk7.cyan(info.vault)}`);
|
|
1929
2770
|
console.log(` Creator: ${info.creator}`);
|
|
1930
2771
|
console.log(` Created: ${date}`);
|
|
1931
|
-
console.log(` Active: ${info.active ?
|
|
2772
|
+
console.log(` Active: ${info.active ? chalk7.green("yes") : chalk7.red("no")}`);
|
|
1932
2773
|
if (info.metadataURI) {
|
|
1933
|
-
console.log(` Metadata: ${
|
|
2774
|
+
console.log(` Metadata: ${chalk7.dim(info.metadataURI)}`);
|
|
1934
2775
|
}
|
|
1935
2776
|
setVaultAddress(info.vault);
|
|
1936
2777
|
const vaultInfo = await getVaultInfo();
|
|
1937
2778
|
console.log();
|
|
1938
|
-
console.log(
|
|
2779
|
+
console.log(chalk7.bold(" Vault Stats"));
|
|
1939
2780
|
console.log(` Total Assets: ${vaultInfo.totalAssets}`);
|
|
1940
2781
|
console.log(` Agent Count: ${vaultInfo.agentCount}`);
|
|
1941
2782
|
console.log(` Redemptions Locked: ${vaultInfo.redemptionsLocked}`);
|
|
@@ -1943,12 +2784,12 @@ syndicate.command("info").description("Display syndicate details by ID").argumen
|
|
|
1943
2784
|
console.log();
|
|
1944
2785
|
} catch (err) {
|
|
1945
2786
|
spinner.fail("Failed to load syndicate info");
|
|
1946
|
-
console.error(
|
|
2787
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
1947
2788
|
process.exit(1);
|
|
1948
2789
|
}
|
|
1949
2790
|
});
|
|
1950
2791
|
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 =
|
|
2792
|
+
const spinner = ora7({ text: W3("Loading syndicate..."), color: "green" }).start();
|
|
1952
2793
|
try {
|
|
1953
2794
|
const syndicateId = BigInt(opts.id);
|
|
1954
2795
|
let metadataURI = opts.uri;
|
|
@@ -1960,7 +2801,7 @@ syndicate.command("update-metadata").description("Update syndicate metadata (cre
|
|
|
1960
2801
|
}
|
|
1961
2802
|
const name = opts.name || info.subdomain;
|
|
1962
2803
|
const description = opts.description || `${name} \u2014 a Sherwood syndicate on ${info.subdomain}.sherwoodagent.eth`;
|
|
1963
|
-
spinner.text =
|
|
2804
|
+
spinner.text = W3("Uploading metadata to IPFS...");
|
|
1964
2805
|
const metadata = {
|
|
1965
2806
|
schema: "sherwood/syndicate/v1",
|
|
1966
2807
|
name,
|
|
@@ -1971,46 +2812,46 @@ syndicate.command("update-metadata").description("Update syndicate metadata (cre
|
|
|
1971
2812
|
links: {}
|
|
1972
2813
|
};
|
|
1973
2814
|
metadataURI = await uploadMetadata(metadata);
|
|
1974
|
-
spinner.text =
|
|
2815
|
+
spinner.text = W3("Updating on-chain metadata...");
|
|
1975
2816
|
}
|
|
1976
2817
|
const hash = await updateMetadata(syndicateId, metadataURI);
|
|
1977
|
-
spinner.succeed(
|
|
1978
|
-
console.log(
|
|
1979
|
-
console.log(
|
|
2818
|
+
spinner.succeed(G3(`Metadata updated`));
|
|
2819
|
+
console.log(DIM3(` IPFS: ${metadataURI}`));
|
|
2820
|
+
console.log(DIM3(` ${getExplorerUrl(hash)}`));
|
|
1980
2821
|
} catch (err) {
|
|
1981
2822
|
spinner.fail("Metadata update failed");
|
|
1982
|
-
console.error(
|
|
2823
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
1983
2824
|
process.exit(1);
|
|
1984
2825
|
}
|
|
1985
2826
|
});
|
|
1986
2827
|
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
2828
|
resolveVault(opts);
|
|
1988
|
-
const spinner =
|
|
2829
|
+
const spinner = ora7("Approving depositor...").start();
|
|
1989
2830
|
try {
|
|
1990
2831
|
const hash = await approveDepositor(opts.depositor);
|
|
1991
2832
|
spinner.succeed(`Depositor approved: ${hash}`);
|
|
1992
|
-
console.log(
|
|
2833
|
+
console.log(chalk7.dim(` ${getExplorerUrl(hash)}`));
|
|
1993
2834
|
} catch (err) {
|
|
1994
2835
|
spinner.fail("Approval failed");
|
|
1995
|
-
console.error(
|
|
2836
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
1996
2837
|
process.exit(1);
|
|
1997
2838
|
}
|
|
1998
2839
|
});
|
|
1999
2840
|
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
2841
|
resolveVault(opts);
|
|
2001
|
-
const spinner =
|
|
2842
|
+
const spinner = ora7("Removing depositor...").start();
|
|
2002
2843
|
try {
|
|
2003
2844
|
const hash = await removeDepositor(opts.depositor);
|
|
2004
2845
|
spinner.succeed(`Depositor removed: ${hash}`);
|
|
2005
|
-
console.log(
|
|
2846
|
+
console.log(chalk7.dim(` ${getExplorerUrl(hash)}`));
|
|
2006
2847
|
} catch (err) {
|
|
2007
2848
|
spinner.fail("Removal failed");
|
|
2008
|
-
console.error(
|
|
2849
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
2009
2850
|
process.exit(1);
|
|
2010
2851
|
}
|
|
2011
2852
|
});
|
|
2012
2853
|
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("--pkp <address>", "Agent PKP address").requiredOption("--eoa <address>", "Operator EOA address").action(async (opts) => {
|
|
2013
|
-
const spinner =
|
|
2854
|
+
const spinner = ora7("Verifying creator...").start();
|
|
2014
2855
|
try {
|
|
2015
2856
|
resolveVault(opts);
|
|
2016
2857
|
const vaultAddress = getVaultAddress();
|
|
@@ -2027,7 +2868,7 @@ syndicate.command("add").description("Register an agent on a syndicate vault (cr
|
|
|
2027
2868
|
opts.eoa
|
|
2028
2869
|
);
|
|
2029
2870
|
spinner.succeed(`Agent registered: ${hash}`);
|
|
2030
|
-
console.log(
|
|
2871
|
+
console.log(chalk7.dim(` ${getExplorerUrl(hash)}`));
|
|
2031
2872
|
try {
|
|
2032
2873
|
const xmtp = await loadXmtp();
|
|
2033
2874
|
const xmtpClient = await xmtp.getXmtpClient();
|
|
@@ -2039,19 +2880,19 @@ syndicate.command("add").description("Register an agent on a syndicate vault (cr
|
|
|
2039
2880
|
syndicate: subdomain,
|
|
2040
2881
|
timestamp: Math.floor(Date.now() / 1e3)
|
|
2041
2882
|
});
|
|
2042
|
-
console.log(
|
|
2883
|
+
console.log(chalk7.dim(` Added to chat: ${subdomain}`));
|
|
2043
2884
|
} catch {
|
|
2044
|
-
console.warn(
|
|
2045
|
-
console.warn(
|
|
2885
|
+
console.warn(chalk7.yellow(" \u26A0 Could not add agent to chat group"));
|
|
2886
|
+
console.warn(chalk7.dim(` If no group exists, run: sherwood chat ${subdomain} init`));
|
|
2046
2887
|
}
|
|
2047
2888
|
} catch (err) {
|
|
2048
2889
|
spinner.fail("Registration failed");
|
|
2049
|
-
console.error(
|
|
2890
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
2050
2891
|
process.exit(1);
|
|
2051
2892
|
}
|
|
2052
2893
|
});
|
|
2053
2894
|
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 =
|
|
2895
|
+
const spinner = ora7("Resolving syndicate...").start();
|
|
2055
2896
|
try {
|
|
2056
2897
|
const agentId = getAgentId();
|
|
2057
2898
|
if (!agentId) {
|
|
@@ -2068,9 +2909,9 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
|
|
|
2068
2909
|
try {
|
|
2069
2910
|
const xmtp = await loadXmtp();
|
|
2070
2911
|
await xmtp.getXmtpClient();
|
|
2071
|
-
console.log(
|
|
2912
|
+
console.log(chalk7.dim(" XMTP identity ready"));
|
|
2072
2913
|
} catch {
|
|
2073
|
-
console.warn(
|
|
2914
|
+
console.warn(chalk7.yellow(" \u26A0 Could not initialize XMTP identity"));
|
|
2074
2915
|
}
|
|
2075
2916
|
return;
|
|
2076
2917
|
}
|
|
@@ -2081,14 +2922,14 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
|
|
|
2081
2922
|
);
|
|
2082
2923
|
if (existingRequest) {
|
|
2083
2924
|
spinner.succeed("You already have a pending join request for this syndicate");
|
|
2084
|
-
console.log(
|
|
2085
|
-
console.log(
|
|
2925
|
+
console.log(chalk7.dim(` Attestation: ${existingRequest.uid}`));
|
|
2926
|
+
console.log(chalk7.dim(` Submitted: ${new Date(existingRequest.time * 1e3).toLocaleString()}`));
|
|
2086
2927
|
try {
|
|
2087
2928
|
const xmtp = await loadXmtp();
|
|
2088
2929
|
await xmtp.getXmtpClient();
|
|
2089
|
-
console.log(
|
|
2930
|
+
console.log(chalk7.dim(" XMTP identity ready"));
|
|
2090
2931
|
} catch {
|
|
2091
|
-
console.warn(
|
|
2932
|
+
console.warn(chalk7.yellow(" \u26A0 Could not initialize XMTP identity"));
|
|
2092
2933
|
}
|
|
2093
2934
|
return;
|
|
2094
2935
|
}
|
|
@@ -2107,29 +2948,29 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
|
|
|
2107
2948
|
spinner.succeed("Join request created (XMTP identity ready)");
|
|
2108
2949
|
} catch {
|
|
2109
2950
|
spinner.succeed("Join request created");
|
|
2110
|
-
console.warn(
|
|
2951
|
+
console.warn(chalk7.yellow(" \u26A0 Could not initialize XMTP identity \u2014 creator may not be able to auto-add you to chat"));
|
|
2111
2952
|
}
|
|
2112
2953
|
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(
|
|
2954
|
+
console.log(LABEL3(" \u25C6 Join Request Submitted"));
|
|
2955
|
+
SEP3();
|
|
2956
|
+
console.log(W3(` Syndicate: ${G3(`${opts.subdomain}.sherwoodagent.eth`)}`));
|
|
2957
|
+
console.log(W3(` Agent ID: #${agentId}`));
|
|
2958
|
+
console.log(W3(` Creator: ${DIM3(syndicate2.creator)}`));
|
|
2959
|
+
console.log(W3(` Attestation: ${DIM3(uid)}`));
|
|
2960
|
+
console.log(W3(` EAS Scan: ${DIM3(getEasScanUrl(uid))}`));
|
|
2961
|
+
console.log(W3(` Explorer: ${DIM3(getExplorerUrl(hash))}`));
|
|
2962
|
+
SEP3();
|
|
2963
|
+
console.log(G3(" \u2713 The creator can review with:"));
|
|
2964
|
+
console.log(DIM3(` sherwood syndicate requests --subdomain ${opts.subdomain}`));
|
|
2124
2965
|
console.log();
|
|
2125
2966
|
} catch (err) {
|
|
2126
2967
|
spinner.fail("Join request failed");
|
|
2127
|
-
console.error(
|
|
2968
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
2128
2969
|
process.exit(1);
|
|
2129
2970
|
}
|
|
2130
2971
|
});
|
|
2131
2972
|
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 =
|
|
2973
|
+
const spinner = ora7("Loading join requests...").start();
|
|
2133
2974
|
try {
|
|
2134
2975
|
let creatorAddress;
|
|
2135
2976
|
let subdomain;
|
|
@@ -2160,34 +3001,34 @@ syndicate.command("requests").description("View pending join requests for a synd
|
|
|
2160
3001
|
);
|
|
2161
3002
|
spinner.stop();
|
|
2162
3003
|
if (requests.length === 0) {
|
|
2163
|
-
console.log(
|
|
3004
|
+
console.log(DIM3("\n No pending join requests.\n"));
|
|
2164
3005
|
return;
|
|
2165
3006
|
}
|
|
2166
3007
|
console.log();
|
|
2167
|
-
console.log(
|
|
2168
|
-
|
|
3008
|
+
console.log(LABEL3(` \u25C6 Pending Join Requests (${requests.length})`));
|
|
3009
|
+
SEP3();
|
|
2169
3010
|
for (let i = 0; i < requests.length; i++) {
|
|
2170
3011
|
const req = requests[i];
|
|
2171
3012
|
const date = new Date(req.time * 1e3).toLocaleString();
|
|
2172
|
-
console.log(
|
|
2173
|
-
console.log(
|
|
2174
|
-
console.log(
|
|
2175
|
-
console.log(
|
|
3013
|
+
console.log(W3(` ${i + 1}. Agent #${req.decoded.agentId} ${DIM3(`(${req.attester})`)}`));
|
|
3014
|
+
console.log(DIM3(` Message: "${req.decoded.message}"`));
|
|
3015
|
+
console.log(DIM3(` Requested: ${date}`));
|
|
3016
|
+
console.log(DIM3(` Attestation: ${req.uid}`));
|
|
2176
3017
|
console.log();
|
|
2177
3018
|
}
|
|
2178
|
-
console.log(
|
|
2179
|
-
console.log(
|
|
2180
|
-
console.log(
|
|
2181
|
-
console.log(
|
|
3019
|
+
console.log(G3(" To approve:"));
|
|
3020
|
+
console.log(DIM3(` sherwood syndicate approve --agent-id <id> --pkp <addr> --eoa <addr>`));
|
|
3021
|
+
console.log(G3(" To reject:"));
|
|
3022
|
+
console.log(DIM3(` sherwood syndicate reject --attestation <uid>`));
|
|
2182
3023
|
console.log();
|
|
2183
3024
|
} catch (err) {
|
|
2184
3025
|
spinner.fail("Failed to load requests");
|
|
2185
|
-
console.error(
|
|
3026
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
2186
3027
|
process.exit(1);
|
|
2187
3028
|
}
|
|
2188
3029
|
});
|
|
2189
3030
|
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("--pkp <address>", "Agent PKP address").requiredOption("--eoa <address>", "Operator EOA address").action(async (opts) => {
|
|
2190
|
-
const spinner =
|
|
3031
|
+
const spinner = ora7("Verifying creator...").start();
|
|
2191
3032
|
try {
|
|
2192
3033
|
if (opts.subdomain && !opts.vault) {
|
|
2193
3034
|
const syndicateInfo = await resolveSyndicate(opts.subdomain);
|
|
@@ -2209,11 +3050,11 @@ syndicate.command("approve").description("Approve an agent join request (registe
|
|
|
2209
3050
|
opts.pkp,
|
|
2210
3051
|
opts.eoa
|
|
2211
3052
|
);
|
|
2212
|
-
console.log(
|
|
3053
|
+
console.log(DIM3(` Agent registered: ${getExplorerUrl(regHash)}`));
|
|
2213
3054
|
} catch (regErr) {
|
|
2214
3055
|
const msg = regErr instanceof Error ? regErr.message : String(regErr);
|
|
2215
3056
|
if (msg.includes("0xe098d3ee") || msg.includes("AgentAlreadyRegistered")) {
|
|
2216
|
-
console.log(
|
|
3057
|
+
console.log(DIM3(" Agent already registered on vault \u2014 skipping"));
|
|
2217
3058
|
} else {
|
|
2218
3059
|
throw regErr;
|
|
2219
3060
|
}
|
|
@@ -2226,7 +3067,7 @@ syndicate.command("approve").description("Approve an agent join request (registe
|
|
|
2226
3067
|
let approvalUid;
|
|
2227
3068
|
if (alreadyApproved) {
|
|
2228
3069
|
approvalUid = alreadyApproved.uid;
|
|
2229
|
-
console.log(
|
|
3070
|
+
console.log(DIM3(` Approval attestation already exists \u2014 skipping`));
|
|
2230
3071
|
} else {
|
|
2231
3072
|
spinner.text = "Creating approval attestation...";
|
|
2232
3073
|
const result = await createApproval(
|
|
@@ -2249,39 +3090,39 @@ syndicate.command("approve").description("Approve an agent join request (registe
|
|
|
2249
3090
|
syndicate: subdomain,
|
|
2250
3091
|
timestamp: Math.floor(Date.now() / 1e3)
|
|
2251
3092
|
});
|
|
2252
|
-
console.log(
|
|
3093
|
+
console.log(DIM3(` Added to chat: ${subdomain}`));
|
|
2253
3094
|
} catch {
|
|
2254
|
-
console.warn(
|
|
2255
|
-
console.warn(
|
|
3095
|
+
console.warn(chalk7.yellow(" \u26A0 Could not add agent to chat group"));
|
|
3096
|
+
console.warn(chalk7.dim(` If no group exists, run: sherwood chat ${subdomain} init`));
|
|
2256
3097
|
}
|
|
2257
3098
|
spinner.succeed("Agent approved and registered");
|
|
2258
3099
|
console.log();
|
|
2259
|
-
console.log(
|
|
2260
|
-
|
|
2261
|
-
console.log(
|
|
2262
|
-
console.log(
|
|
2263
|
-
console.log(
|
|
2264
|
-
console.log(
|
|
2265
|
-
console.log(
|
|
2266
|
-
|
|
3100
|
+
console.log(LABEL3(" \u25C6 Agent Approved"));
|
|
3101
|
+
SEP3();
|
|
3102
|
+
console.log(W3(` Agent ID: #${opts.agentId}`));
|
|
3103
|
+
console.log(W3(` PKP: ${G3(opts.pkp)}`));
|
|
3104
|
+
console.log(W3(` EOA: ${G3(opts.eoa)}`));
|
|
3105
|
+
console.log(W3(` Approval: ${DIM3(approvalUid)}`));
|
|
3106
|
+
console.log(W3(` EAS Scan: ${DIM3(getEasScanUrl(approvalUid))}`));
|
|
3107
|
+
SEP3();
|
|
2267
3108
|
} catch (err) {
|
|
2268
3109
|
spinner.fail("Approval failed");
|
|
2269
|
-
console.error(
|
|
3110
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
2270
3111
|
process.exit(1);
|
|
2271
3112
|
}
|
|
2272
3113
|
});
|
|
2273
3114
|
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 =
|
|
3115
|
+
const spinner = ora7("Revoking attestation...").start();
|
|
2275
3116
|
try {
|
|
2276
3117
|
const hash = await revokeAttestation(
|
|
2277
3118
|
EAS_SCHEMAS().SYNDICATE_JOIN_REQUEST,
|
|
2278
3119
|
opts.attestation
|
|
2279
3120
|
);
|
|
2280
3121
|
spinner.succeed("Join request rejected");
|
|
2281
|
-
console.log(
|
|
3122
|
+
console.log(DIM3(` ${getExplorerUrl(hash)}`));
|
|
2282
3123
|
} catch (err) {
|
|
2283
3124
|
spinner.fail("Rejection failed");
|
|
2284
|
-
console.error(
|
|
3125
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
2285
3126
|
process.exit(1);
|
|
2286
3127
|
}
|
|
2287
3128
|
});
|
|
@@ -2290,39 +3131,39 @@ vaultCmd.command("deposit").description("Deposit into a vault").option("--vault
|
|
|
2290
3131
|
resolveVault(opts);
|
|
2291
3132
|
const decimals = await getAssetDecimals();
|
|
2292
3133
|
const amount = parseUnits8(opts.amount, decimals);
|
|
2293
|
-
const spinner =
|
|
3134
|
+
const spinner = ora7(`Depositing ${opts.amount}...`).start();
|
|
2294
3135
|
try {
|
|
2295
3136
|
const hash = await deposit(amount);
|
|
2296
3137
|
spinner.succeed(`Deposited: ${hash}`);
|
|
2297
|
-
console.log(
|
|
3138
|
+
console.log(chalk7.dim(` ${getExplorerUrl(hash)}`));
|
|
2298
3139
|
} catch (err) {
|
|
2299
3140
|
spinner.fail("Deposit failed");
|
|
2300
|
-
console.error(
|
|
3141
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
2301
3142
|
process.exit(1);
|
|
2302
3143
|
}
|
|
2303
3144
|
});
|
|
2304
3145
|
vaultCmd.command("ragequit").description("Withdraw all shares from a vault").option("--vault <address>", "Vault address (default: from config)").action(async (opts) => {
|
|
2305
3146
|
resolveVault(opts);
|
|
2306
|
-
const spinner =
|
|
3147
|
+
const spinner = ora7("Ragequitting...").start();
|
|
2307
3148
|
try {
|
|
2308
3149
|
const hash = await ragequit();
|
|
2309
3150
|
spinner.succeed(`Ragequit: ${hash}`);
|
|
2310
|
-
console.log(
|
|
3151
|
+
console.log(chalk7.dim(` ${getExplorerUrl(hash)}`));
|
|
2311
3152
|
} catch (err) {
|
|
2312
3153
|
spinner.fail("Ragequit failed");
|
|
2313
|
-
console.error(
|
|
3154
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
2314
3155
|
process.exit(1);
|
|
2315
3156
|
}
|
|
2316
3157
|
});
|
|
2317
3158
|
vaultCmd.command("info").description("Display vault state").option("--vault <address>", "Vault address (default: from config)").action(async (opts) => {
|
|
2318
3159
|
resolveVault(opts);
|
|
2319
|
-
const spinner =
|
|
3160
|
+
const spinner = ora7("Loading vault info...").start();
|
|
2320
3161
|
try {
|
|
2321
3162
|
const info = await getVaultInfo();
|
|
2322
3163
|
spinner.stop();
|
|
2323
3164
|
console.log();
|
|
2324
|
-
console.log(
|
|
2325
|
-
console.log(
|
|
3165
|
+
console.log(chalk7.bold("Vault Info"));
|
|
3166
|
+
console.log(chalk7.dim("\u2500".repeat(40)));
|
|
2326
3167
|
console.log(` Address: ${info.address}`);
|
|
2327
3168
|
console.log(` Total Assets: ${info.totalAssets}`);
|
|
2328
3169
|
console.log(` Agent Count: ${info.agentCount}`);
|
|
@@ -2331,85 +3172,85 @@ vaultCmd.command("info").description("Display vault state").option("--vault <add
|
|
|
2331
3172
|
console.log();
|
|
2332
3173
|
} catch (err) {
|
|
2333
3174
|
spinner.fail("Failed to load vault info");
|
|
2334
|
-
console.error(
|
|
3175
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
2335
3176
|
process.exit(1);
|
|
2336
3177
|
}
|
|
2337
3178
|
});
|
|
2338
3179
|
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
3180
|
resolveVault(opts);
|
|
2340
|
-
const spinner =
|
|
3181
|
+
const spinner = ora7("Loading balance...").start();
|
|
2341
3182
|
try {
|
|
2342
3183
|
const balance = await getBalance(opts.address);
|
|
2343
3184
|
spinner.stop();
|
|
2344
3185
|
console.log();
|
|
2345
|
-
console.log(
|
|
2346
|
-
console.log(
|
|
3186
|
+
console.log(chalk7.bold("LP Position"));
|
|
3187
|
+
console.log(chalk7.dim("\u2500".repeat(40)));
|
|
2347
3188
|
console.log(` Shares: ${balance.shares.toString()}`);
|
|
2348
3189
|
console.log(` Asset Value: ${balance.assetsValue}`);
|
|
2349
3190
|
console.log(` % of Vault: ${balance.percentOfVault}`);
|
|
2350
3191
|
console.log();
|
|
2351
3192
|
} catch (err) {
|
|
2352
3193
|
spinner.fail("Failed to load balance");
|
|
2353
|
-
console.error(
|
|
3194
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
2354
3195
|
process.exit(1);
|
|
2355
3196
|
}
|
|
2356
3197
|
});
|
|
2357
3198
|
var strategy = program.command("strategy");
|
|
2358
3199
|
strategy.command("list").description("List registered strategies").option("--type <id>", "Filter by strategy type").action(async (opts) => {
|
|
2359
|
-
const spinner =
|
|
3200
|
+
const spinner = ora7("Loading strategies...").start();
|
|
2360
3201
|
try {
|
|
2361
3202
|
const strategies = await listStrategies(
|
|
2362
3203
|
opts.type ? BigInt(opts.type) : void 0
|
|
2363
3204
|
);
|
|
2364
3205
|
spinner.stop();
|
|
2365
3206
|
if (strategies.length === 0) {
|
|
2366
|
-
console.log(
|
|
3207
|
+
console.log(chalk7.dim("No strategies registered."));
|
|
2367
3208
|
return;
|
|
2368
3209
|
}
|
|
2369
3210
|
console.log();
|
|
2370
|
-
console.log(
|
|
2371
|
-
console.log(
|
|
3211
|
+
console.log(chalk7.bold(`Strategies (${strategies.length})`));
|
|
3212
|
+
console.log(chalk7.dim("\u2500".repeat(70)));
|
|
2372
3213
|
for (const s of strategies) {
|
|
2373
|
-
const status = s.active ?
|
|
2374
|
-
console.log(` #${s.id} ${
|
|
3214
|
+
const status = s.active ? chalk7.green("active") : chalk7.red("inactive");
|
|
3215
|
+
console.log(` #${s.id} ${chalk7.bold(s.name)} [type: ${s.strategyTypeId}] ${status}`);
|
|
2375
3216
|
console.log(` Creator: ${s.creator}`);
|
|
2376
3217
|
console.log(` Implementation: ${s.implementation}`);
|
|
2377
3218
|
if (s.metadataURI) {
|
|
2378
|
-
console.log(` Metadata: ${
|
|
3219
|
+
console.log(` Metadata: ${chalk7.dim(s.metadataURI)}`);
|
|
2379
3220
|
}
|
|
2380
3221
|
console.log();
|
|
2381
3222
|
}
|
|
2382
3223
|
} catch (err) {
|
|
2383
3224
|
spinner.fail("Failed to load strategies");
|
|
2384
|
-
console.error(
|
|
3225
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
2385
3226
|
process.exit(1);
|
|
2386
3227
|
}
|
|
2387
3228
|
});
|
|
2388
3229
|
strategy.command("info").description("Show strategy details").argument("<id>", "Strategy ID").action(async (idStr) => {
|
|
2389
|
-
const spinner =
|
|
3230
|
+
const spinner = ora7("Loading strategy...").start();
|
|
2390
3231
|
try {
|
|
2391
3232
|
const s = await getStrategy(BigInt(idStr));
|
|
2392
3233
|
spinner.stop();
|
|
2393
3234
|
console.log();
|
|
2394
|
-
console.log(
|
|
2395
|
-
console.log(
|
|
3235
|
+
console.log(chalk7.bold(`Strategy #${s.id}`));
|
|
3236
|
+
console.log(chalk7.dim("\u2500".repeat(40)));
|
|
2396
3237
|
console.log(` Name: ${s.name}`);
|
|
2397
3238
|
console.log(` Type: ${s.strategyTypeId}`);
|
|
2398
|
-
console.log(` Active: ${s.active ?
|
|
3239
|
+
console.log(` Active: ${s.active ? chalk7.green("yes") : chalk7.red("no")}`);
|
|
2399
3240
|
console.log(` Creator: ${s.creator}`);
|
|
2400
3241
|
console.log(` Implementation: ${s.implementation}`);
|
|
2401
3242
|
if (s.metadataURI) {
|
|
2402
|
-
console.log(` Metadata: ${
|
|
3243
|
+
console.log(` Metadata: ${chalk7.dim(s.metadataURI)}`);
|
|
2403
3244
|
}
|
|
2404
3245
|
console.log();
|
|
2405
3246
|
} catch (err) {
|
|
2406
3247
|
spinner.fail("Failed to load strategy");
|
|
2407
|
-
console.error(
|
|
3248
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
2408
3249
|
process.exit(1);
|
|
2409
3250
|
}
|
|
2410
3251
|
});
|
|
2411
3252
|
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 =
|
|
3253
|
+
const spinner = ora7("Registering strategy...").start();
|
|
2413
3254
|
try {
|
|
2414
3255
|
const hash = await registerStrategy(
|
|
2415
3256
|
opts.implementation,
|
|
@@ -2418,10 +3259,10 @@ strategy.command("register").description("Register a new strategy on-chain").req
|
|
|
2418
3259
|
opts.metadata
|
|
2419
3260
|
);
|
|
2420
3261
|
spinner.succeed(`Strategy registered: ${hash}`);
|
|
2421
|
-
console.log(
|
|
3262
|
+
console.log(chalk7.dim(` ${getExplorerUrl(hash)}`));
|
|
2422
3263
|
} catch (err) {
|
|
2423
3264
|
spinner.fail("Registration failed");
|
|
2424
|
-
console.error(
|
|
3265
|
+
console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
|
|
2425
3266
|
process.exit(1);
|
|
2426
3267
|
}
|
|
2427
3268
|
});
|
|
@@ -2430,7 +3271,7 @@ strategy.command("run").description("Execute the levered swap strategy").option(
|
|
|
2430
3271
|
await runLeveredSwap(opts);
|
|
2431
3272
|
});
|
|
2432
3273
|
program.command("providers").description("List available DeFi providers").action(async () => {
|
|
2433
|
-
const { MessariProvider, NansenProvider } = await import("./research-
|
|
3274
|
+
const { MessariProvider, NansenProvider } = await import("./research-ZR7HXITG.js");
|
|
2434
3275
|
const providers = [new MoonwellProvider(), new UniswapProvider(), new MessariProvider(), new NansenProvider()];
|
|
2435
3276
|
for (const p of providers) {
|
|
2436
3277
|
const info = p.info();
|
|
@@ -2441,57 +3282,70 @@ ${info.name} (${info.type})`);
|
|
|
2441
3282
|
}
|
|
2442
3283
|
});
|
|
2443
3284
|
try {
|
|
2444
|
-
const { registerChatCommands } = await import("./chat-
|
|
3285
|
+
const { registerChatCommands } = await import("./chat-O34BTHII.js");
|
|
2445
3286
|
registerChatCommands(program);
|
|
2446
3287
|
} catch {
|
|
2447
3288
|
program.command("chat <name> [action] [actionArgs...]").description("Syndicate chat (XMTP) \u2014 requires @xmtp/cli").action(() => {
|
|
2448
|
-
console.error(
|
|
2449
|
-
console.error(
|
|
2450
|
-
console.error(
|
|
3289
|
+
console.error(chalk7.red("XMTP CLI not available."));
|
|
3290
|
+
console.error(chalk7.dim("Install with: npm install -g @xmtp/cli"));
|
|
3291
|
+
console.error(chalk7.dim("Or reinstall: npm i -g @sherwoodagent/cli"));
|
|
2451
3292
|
process.exit(1);
|
|
2452
3293
|
});
|
|
2453
3294
|
}
|
|
2454
|
-
var { registerSessionCommands } = await import("./session-
|
|
3295
|
+
var { registerSessionCommands } = await import("./session-QEIVURQO.js");
|
|
2455
3296
|
registerSessionCommands(program);
|
|
2456
3297
|
registerVeniceCommands(program);
|
|
2457
3298
|
registerAllowanceCommands(program);
|
|
2458
3299
|
registerIdentityCommands(program);
|
|
2459
|
-
|
|
3300
|
+
registerProposalCommands(program);
|
|
3301
|
+
registerGovernorCommands(program);
|
|
3302
|
+
var { registerResearchCommands } = await import("./research-GX32VMP7.js");
|
|
2460
3303
|
registerResearchCommands(program);
|
|
2461
3304
|
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) => {
|
|
3305
|
+
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
3306
|
let saved = false;
|
|
2464
3307
|
if (opts.privateKey) {
|
|
2465
3308
|
setPrivateKey(opts.privateKey);
|
|
2466
3309
|
const account = getAccount();
|
|
2467
|
-
console.log(
|
|
2468
|
-
console.log(
|
|
3310
|
+
console.log(chalk7.green("Private key saved to ~/.sherwood/config.json"));
|
|
3311
|
+
console.log(chalk7.dim(` Wallet: ${account.address}`));
|
|
2469
3312
|
saved = true;
|
|
2470
3313
|
}
|
|
2471
3314
|
if (opts.vault) {
|
|
2472
3315
|
const chainId = getChain().id;
|
|
2473
3316
|
setChainContract(chainId, "vault", opts.vault);
|
|
2474
|
-
console.log(
|
|
2475
|
-
console.log(
|
|
3317
|
+
console.log(chalk7.green(`Vault saved to ~/.sherwood/config.json (chain ${chainId})`));
|
|
3318
|
+
console.log(chalk7.dim(` Vault: ${opts.vault}`));
|
|
3319
|
+
saved = true;
|
|
3320
|
+
}
|
|
3321
|
+
if (opts.rpc) {
|
|
3322
|
+
const network = getNetwork();
|
|
3323
|
+
setConfigRpcUrl(network, opts.rpc);
|
|
3324
|
+
console.log(chalk7.green(`RPC URL saved for ${network}`));
|
|
3325
|
+
console.log(chalk7.dim(` RPC: ${opts.rpc}`));
|
|
2476
3326
|
saved = true;
|
|
2477
3327
|
}
|
|
2478
3328
|
if (!saved) {
|
|
2479
|
-
console.log(
|
|
3329
|
+
console.log(chalk7.red("Provide at least one of: --private-key, --vault, --rpc"));
|
|
2480
3330
|
process.exit(1);
|
|
2481
3331
|
}
|
|
2482
3332
|
});
|
|
2483
3333
|
configCmd.command("show").description("Display current config for the active network").action(() => {
|
|
3334
|
+
const network = getNetwork();
|
|
2484
3335
|
const chainId = getChain().id;
|
|
2485
3336
|
const contracts = getChainContracts(chainId);
|
|
2486
3337
|
const config = loadConfig();
|
|
3338
|
+
const customRpc = config.rpc?.[network];
|
|
2487
3339
|
console.log();
|
|
2488
|
-
console.log(
|
|
2489
|
-
console.log(
|
|
2490
|
-
console.log(`
|
|
2491
|
-
console.log(`
|
|
2492
|
-
console.log(`
|
|
3340
|
+
console.log(chalk7.bold(`Sherwood Config`));
|
|
3341
|
+
console.log(chalk7.dim("\u2500".repeat(50)));
|
|
3342
|
+
console.log(` Network: ${chalk7.cyan(network)} (chain ${chainId})`);
|
|
3343
|
+
console.log(` RPC: ${customRpc ? chalk7.green(customRpc) : chalk7.dim("default")}`);
|
|
3344
|
+
console.log(` Wallet: ${config.privateKey ? chalk7.green("configured") : chalk7.dim("not set")}`);
|
|
3345
|
+
console.log(` Agent ID: ${config.agentId ?? chalk7.dim("not set")}`);
|
|
3346
|
+
console.log(` Vault: ${contracts.vault ?? chalk7.dim("not set")}`);
|
|
2493
3347
|
console.log();
|
|
2494
|
-
console.log(
|
|
3348
|
+
console.log(chalk7.dim(" Config file: ~/.sherwood/config.json"));
|
|
2495
3349
|
console.log();
|
|
2496
3350
|
});
|
|
2497
3351
|
program.parse();
|