dexe-mcp 0.1.5 → 0.3.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.
Files changed (111) hide show
  1. package/.mcp.example.json +12 -2
  2. package/CHANGELOG.md +84 -0
  3. package/FUTURE.md +26 -5
  4. package/README.md +105 -197
  5. package/dist/config.d.ts +12 -0
  6. package/dist/config.d.ts.map +1 -1
  7. package/dist/config.js +34 -1
  8. package/dist/config.js.map +1 -1
  9. package/dist/index.js +16 -0
  10. package/dist/index.js.map +1 -1
  11. package/dist/lib/addresses.d.ts +57 -0
  12. package/dist/lib/addresses.d.ts.map +1 -0
  13. package/dist/lib/addresses.js +83 -0
  14. package/dist/lib/addresses.js.map +1 -0
  15. package/dist/lib/calldata.d.ts +32 -0
  16. package/dist/lib/calldata.d.ts.map +1 -0
  17. package/dist/lib/calldata.js +28 -0
  18. package/dist/lib/calldata.js.map +1 -0
  19. package/dist/lib/govEnums.d.ts +12 -0
  20. package/dist/lib/govEnums.d.ts.map +1 -0
  21. package/dist/lib/govEnums.js +35 -0
  22. package/dist/lib/govEnums.js.map +1 -0
  23. package/dist/lib/ipfs.d.ts +60 -0
  24. package/dist/lib/ipfs.d.ts.map +1 -0
  25. package/dist/lib/ipfs.js +168 -0
  26. package/dist/lib/ipfs.js.map +1 -0
  27. package/dist/lib/markdownToSlate.d.ts +39 -0
  28. package/dist/lib/markdownToSlate.d.ts.map +1 -0
  29. package/dist/lib/markdownToSlate.js +203 -0
  30. package/dist/lib/markdownToSlate.js.map +1 -0
  31. package/dist/lib/multicall.d.ts +33 -0
  32. package/dist/lib/multicall.d.ts.map +1 -0
  33. package/dist/lib/multicall.js +46 -0
  34. package/dist/lib/multicall.js.map +1 -0
  35. package/dist/lib/proposalCatalog.d.ts +45 -0
  36. package/dist/lib/proposalCatalog.d.ts.map +1 -0
  37. package/dist/lib/proposalCatalog.js +416 -0
  38. package/dist/lib/proposalCatalog.js.map +1 -0
  39. package/dist/lib/signer.d.ts +12 -0
  40. package/dist/lib/signer.d.ts.map +1 -0
  41. package/dist/lib/signer.js +31 -0
  42. package/dist/lib/signer.js.map +1 -0
  43. package/dist/lib/subgraph.d.ts +15 -0
  44. package/dist/lib/subgraph.d.ts.map +1 -0
  45. package/dist/lib/subgraph.js +42 -0
  46. package/dist/lib/subgraph.js.map +1 -0
  47. package/dist/tools/dao.d.ts +4 -0
  48. package/dist/tools/dao.d.ts.map +1 -0
  49. package/dist/tools/dao.js +242 -0
  50. package/dist/tools/dao.js.map +1 -0
  51. package/dist/tools/daoDeploy.d.ts +4 -0
  52. package/dist/tools/daoDeploy.d.ts.map +1 -0
  53. package/dist/tools/daoDeploy.js +573 -0
  54. package/dist/tools/daoDeploy.js.map +1 -0
  55. package/dist/tools/flow.d.ts +5 -0
  56. package/dist/tools/flow.d.ts.map +1 -0
  57. package/dist/tools/flow.js +451 -0
  58. package/dist/tools/flow.js.map +1 -0
  59. package/dist/tools/index.d.ts.map +1 -1
  60. package/dist/tools/index.js +32 -0
  61. package/dist/tools/index.js.map +1 -1
  62. package/dist/tools/ipfs.d.ts +6 -0
  63. package/dist/tools/ipfs.d.ts.map +1 -0
  64. package/dist/tools/ipfs.js +376 -0
  65. package/dist/tools/ipfs.js.map +1 -0
  66. package/dist/tools/proposal.d.ts +4 -0
  67. package/dist/tools/proposal.d.ts.map +1 -0
  68. package/dist/tools/proposal.js +217 -0
  69. package/dist/tools/proposal.js.map +1 -0
  70. package/dist/tools/proposalBuild.d.ts +4 -0
  71. package/dist/tools/proposalBuild.d.ts.map +1 -0
  72. package/dist/tools/proposalBuild.js +372 -0
  73. package/dist/tools/proposalBuild.js.map +1 -0
  74. package/dist/tools/proposalBuildComplex.d.ts +4 -0
  75. package/dist/tools/proposalBuildComplex.d.ts.map +1 -0
  76. package/dist/tools/proposalBuildComplex.js +766 -0
  77. package/dist/tools/proposalBuildComplex.js.map +1 -0
  78. package/dist/tools/proposalBuildInternal.d.ts +4 -0
  79. package/dist/tools/proposalBuildInternal.d.ts.map +1 -0
  80. package/dist/tools/proposalBuildInternal.js +224 -0
  81. package/dist/tools/proposalBuildInternal.js.map +1 -0
  82. package/dist/tools/proposalBuildMore.d.ts +4 -0
  83. package/dist/tools/proposalBuildMore.d.ts.map +1 -0
  84. package/dist/tools/proposalBuildMore.js +457 -0
  85. package/dist/tools/proposalBuildMore.js.map +1 -0
  86. package/dist/tools/proposalBuildOffchain.d.ts +4 -0
  87. package/dist/tools/proposalBuildOffchain.d.ts.map +1 -0
  88. package/dist/tools/proposalBuildOffchain.js +389 -0
  89. package/dist/tools/proposalBuildOffchain.js.map +1 -0
  90. package/dist/tools/read.d.ts +4 -0
  91. package/dist/tools/read.d.ts.map +1 -0
  92. package/dist/tools/read.js +580 -0
  93. package/dist/tools/read.js.map +1 -0
  94. package/dist/tools/subgraph.d.ts +4 -0
  95. package/dist/tools/subgraph.d.ts.map +1 -0
  96. package/dist/tools/subgraph.js +404 -0
  97. package/dist/tools/subgraph.js.map +1 -0
  98. package/dist/tools/txSend.d.ts +5 -0
  99. package/dist/tools/txSend.d.ts.map +1 -0
  100. package/dist/tools/txSend.js +87 -0
  101. package/dist/tools/txSend.js.map +1 -0
  102. package/dist/tools/vote.d.ts +4 -0
  103. package/dist/tools/vote.d.ts.map +1 -0
  104. package/dist/tools/vote.js +196 -0
  105. package/dist/tools/vote.js.map +1 -0
  106. package/dist/tools/voteBuild.d.ts +4 -0
  107. package/dist/tools/voteBuild.d.ts.map +1 -0
  108. package/dist/tools/voteBuild.js +983 -0
  109. package/dist/tools/voteBuild.js.map +1 -0
  110. package/package.json +18 -5
  111. package/PLAN.md +0 -132
@@ -0,0 +1,766 @@
1
+ import { z } from "zod";
2
+ import { Interface, isAddress, ZeroAddress } from "ethers";
3
+ /**
4
+ * Phase 3c — 10 complex named wrappers. Same contract as 3a/3b:
5
+ * every wrapper returns `{ metadata, actions: Action[] }`. Actions are fed
6
+ * to `dexe_proposal_build_external`. Signatures verified against DeXe
7
+ * frontend hooks at `C:/dev/investing-dashboard/src/hooks/dao/proposals/**`
8
+ * on 2026-04-15.
9
+ *
10
+ * Note: `enable_staking` is NOT a distinct wrapper — the frontend reuses
11
+ * `useGovPoolCreateProposalType`, so the catalog routes it to
12
+ * `dexe_proposal_build_new_proposal_type`.
13
+ */
14
+ // ---------- ABIs ----------
15
+ const DISTRIBUTION_PROPOSAL_ABI = [
16
+ "function execute(uint256 proposalId, address token, uint256 amount)",
17
+ ];
18
+ const TOKEN_SALE_PROPOSAL_ABI = [
19
+ "function createTiers(tuple(tuple(string name, string description) metadata, uint256 totalTokenProvided, uint256 saleStartTime, uint256 saleEndTime, address saleTokenAddress, uint256 claimLockDuration, address[] purchaseTokenAddresses, uint256[] exchangeRates, uint256 minAllocationPerUser, uint256 maxAllocationPerUser, tuple(uint256 cliffPeriod, uint256 unlockStep, uint256 vestingDuration, uint256 vestingPercentage) vestingSettings, tuple(uint8 participationType, bytes data)[] participationDetails)[] tiers)",
20
+ "function recover(uint256[] tierIds)",
21
+ ];
22
+ const STAKING_PROPOSAL_ABI = [
23
+ "function createStaking(address rewardToken, uint256 rewardAmount, uint256 startedAt, uint256 deadline, string metadata)",
24
+ ];
25
+ const GOV_POOL_EXT_ABI = [
26
+ "function changeVotePower(address newVotePower)",
27
+ "function editDescriptionURL(string descriptionURL)",
28
+ "function setNftMultiplierAddress(address nftMultiplier)",
29
+ ];
30
+ const ERC20_GOV_ABI = [
31
+ "function blacklist(address[] users, bool isBlacklisted)",
32
+ "function transfer(address to, uint256 amount) returns (bool)",
33
+ "function mint(address to, uint256 amount)",
34
+ "function approve(address spender, uint256 amount) returns (bool)",
35
+ ];
36
+ const ERC721_MULTIPLIER_ABI = [
37
+ "function setTokenURI(uint256 tokenId, string uri)",
38
+ "function mint(address to, uint256 multiplier, uint256 rewardPeriod, string metadataUrl)",
39
+ "function changeToken(uint256 tokenId, uint256 multiplier, uint256 rewardPeriod)",
40
+ ];
41
+ const GOV_SETTINGS_FULL_ABI = [
42
+ "function addSettings(tuple(bool earlyCompletion, bool delegatedVotingAllowed, bool validatorsVote, uint64 duration, uint64 durationValidators, uint64 executionDelay, uint128 quorum, uint128 quorumValidators, uint256 minVotesForVoting, uint256 minVotesForCreating, tuple(address rewardToken, uint256 creationReward, uint256 executionReward, uint256 voteRewardsCoefficient) rewardsInfo, string executorDescription)[] settings)",
43
+ "function changeExecutors(address[] executors, uint256[] settingsIds)",
44
+ ];
45
+ function errorResult(message) {
46
+ return { content: [{ type: "text", text: message }], isError: true };
47
+ }
48
+ function payloadOutputSchema() {
49
+ return {
50
+ metadata: z.unknown(),
51
+ actions: z.array(z.object({ executor: z.string(), value: z.string(), data: z.string() })),
52
+ };
53
+ }
54
+ function wrapperResult(params) {
55
+ return {
56
+ content: [
57
+ {
58
+ type: "text",
59
+ text: `${params.title}\n${params.detail}\n\nNext:\n` +
60
+ `1) dexe_ipfs_upload_proposal_metadata with the metadata object → get CID\n` +
61
+ `2) dexe_proposal_build_external with descriptionURL=<CID>, actionsOnFor=actions (${params.actions.length} action${params.actions.length === 1 ? "" : "s"})`,
62
+ },
63
+ ],
64
+ structuredContent: { metadata: params.metadata, actions: params.actions },
65
+ };
66
+ }
67
+ // ---------- register ----------
68
+ export function registerProposalBuildComplexTools(server, _ctx) {
69
+ registerTokenDistribution(server);
70
+ registerTokenSale(server);
71
+ registerTokenSaleRecover(server);
72
+ registerCreateStakingTier(server);
73
+ registerChangeMathModel(server);
74
+ registerModifyDaoProfile(server);
75
+ registerBlacklistManagement(server);
76
+ registerRewardMultiplier(server);
77
+ registerApplyToDao(server);
78
+ registerNewProposalType(server);
79
+ }
80
+ // ---------- 1. token_distribution ----------
81
+ function registerTokenDistribution(server) {
82
+ server.registerTool("dexe_proposal_build_token_distribution", {
83
+ title: "Wrapper: batch token distribution via DistributionProposal",
84
+ description: "Builds a 'Token Distribution' external proposal. Encodes `DistributionProposal.execute(proposalId, token, amount)`. For ERC20 tokens, automatically prepends an `ERC20.approve` action. For native tokens (isNative=true), sets the action value instead. `proposalId` is the DAO's latest proposalId + 1.",
85
+ inputSchema: {
86
+ distributionProposal: z
87
+ .string()
88
+ .describe("DistributionProposal address (from catalog / registry lookup)"),
89
+ proposalId: z
90
+ .string()
91
+ .describe("Expected proposalId for this distribution (usually latestProposalId + 1)"),
92
+ token: z.string(),
93
+ amount: z.string(),
94
+ isNative: z.boolean().default(false).describe("True for native token (BNB/ETH) — sends value instead of approve"),
95
+ proposalName: z.string().default("Token Distribution"),
96
+ proposalDescription: z.string().default(""),
97
+ },
98
+ outputSchema: payloadOutputSchema(),
99
+ }, async ({ distributionProposal, proposalId, token, amount, isNative = false, proposalName = "Token Distribution", proposalDescription = "", }) => {
100
+ if (!isAddress(distributionProposal))
101
+ return errorResult(`Invalid distributionProposal: ${distributionProposal}`);
102
+ if (!isAddress(token))
103
+ return errorResult(`Invalid token: ${token}`);
104
+ try {
105
+ const distIface = new Interface(DISTRIBUTION_PROPOSAL_ABI);
106
+ const executeData = distIface.encodeFunctionData("execute", [
107
+ BigInt(proposalId),
108
+ token,
109
+ BigInt(amount),
110
+ ]);
111
+ const actions = [];
112
+ if (isNative) {
113
+ actions.push({ executor: distributionProposal, value: amount, data: executeData });
114
+ }
115
+ else {
116
+ const erc20Iface = new Interface(ERC20_GOV_ABI);
117
+ const approveData = erc20Iface.encodeFunctionData("approve", [distributionProposal, BigInt(amount)]);
118
+ actions.push({ executor: token, value: "0", data: approveData });
119
+ actions.push({ executor: distributionProposal, value: "0", data: executeData });
120
+ }
121
+ const metadata = {
122
+ proposalName,
123
+ proposalDescription: JSON.stringify(proposalDescription),
124
+ category: "tokenDistribution",
125
+ isMeta: false,
126
+ changes: {
127
+ proposedChanges: { tokenAddress: token, tokenAmount: amount, proposalId },
128
+ currentChanges: {},
129
+ },
130
+ };
131
+ return wrapperResult({
132
+ metadata,
133
+ actions,
134
+ title: `Token Distribution → ${amount} of ${token} via proposal #${proposalId}`,
135
+ detail: `Target: DistributionProposal(${distributionProposal}).execute (${actions.length} actions)`,
136
+ });
137
+ }
138
+ catch (err) {
139
+ return errorResult(err instanceof Error ? err.message : String(err));
140
+ }
141
+ });
142
+ }
143
+ // ---------- 2. token_sale ----------
144
+ function registerTokenSale(server) {
145
+ server.registerTool("dexe_proposal_build_token_sale", {
146
+ title: "Wrapper: launch a token-sale tier via TokenSaleProposal.createTiers",
147
+ description: "Builds a Token Sale proposal with a single tier. Covers the common case (basic tier, optional plain whitelist, no merkle participation). For advanced merkle-whitelist tiers, encode `participationDetails` externally and use `dexe_proposal_build_custom_abi` instead.",
148
+ inputSchema: {
149
+ tokenSaleProposal: z.string().describe("TokenSaleProposal contract address"),
150
+ tier: z.object({
151
+ name: z.string(),
152
+ description: z.string().default(""),
153
+ totalTokenProvided: z.string(),
154
+ saleStartTime: z.string().describe("Unix seconds"),
155
+ saleEndTime: z.string().describe("Unix seconds"),
156
+ saleTokenAddress: z.string(),
157
+ claimLockDuration: z.string().default("0"),
158
+ purchaseTokenAddresses: z.array(z.string()).min(1),
159
+ exchangeRates: z.array(z.string()).min(1),
160
+ minAllocationPerUser: z.string().default("0"),
161
+ maxAllocationPerUser: z.string().default("0"),
162
+ vestingSettings: z
163
+ .object({
164
+ cliffPeriod: z.string().default("0"),
165
+ unlockStep: z.string().default("0"),
166
+ vestingDuration: z.string().default("0"),
167
+ vestingPercentage: z.string().default("0"),
168
+ })
169
+ .default({
170
+ cliffPeriod: "0",
171
+ unlockStep: "0",
172
+ vestingDuration: "0",
173
+ vestingPercentage: "0",
174
+ }),
175
+ }),
176
+ proposalName: z.string().default("Token Sale"),
177
+ proposalDescription: z.string().default(""),
178
+ },
179
+ outputSchema: payloadOutputSchema(),
180
+ }, async ({ tokenSaleProposal, tier, proposalName = "Token Sale", proposalDescription = "" }) => {
181
+ if (!isAddress(tokenSaleProposal))
182
+ return errorResult(`Invalid tokenSaleProposal: ${tokenSaleProposal}`);
183
+ if (!isAddress(tier.saleTokenAddress))
184
+ return errorResult(`Invalid saleTokenAddress`);
185
+ if (tier.purchaseTokenAddresses.length !== tier.exchangeRates.length) {
186
+ return errorResult("purchaseTokenAddresses and exchangeRates must be parallel arrays");
187
+ }
188
+ try {
189
+ const iface = new Interface(TOKEN_SALE_PROPOSAL_ABI);
190
+ const tierTuple = [
191
+ [tier.name, tier.description],
192
+ BigInt(tier.totalTokenProvided),
193
+ BigInt(tier.saleStartTime),
194
+ BigInt(tier.saleEndTime),
195
+ tier.saleTokenAddress,
196
+ BigInt(tier.claimLockDuration),
197
+ tier.purchaseTokenAddresses,
198
+ tier.exchangeRates.map((r) => BigInt(r)),
199
+ BigInt(tier.minAllocationPerUser),
200
+ BigInt(tier.maxAllocationPerUser),
201
+ [
202
+ BigInt(tier.vestingSettings.cliffPeriod),
203
+ BigInt(tier.vestingSettings.unlockStep),
204
+ BigInt(tier.vestingSettings.vestingDuration),
205
+ BigInt(tier.vestingSettings.vestingPercentage),
206
+ ],
207
+ [], // participationDetails — empty for the common case
208
+ ];
209
+ const data = iface.encodeFunctionData("createTiers", [[tierTuple]]);
210
+ const actions = [];
211
+ // Prepend ERC20.approve for the sale token (required for on-chain execution)
212
+ if (isAddress(tier.saleTokenAddress)) {
213
+ const erc20Iface = new Interface(ERC20_GOV_ABI);
214
+ const approveData = erc20Iface.encodeFunctionData("approve", [
215
+ tokenSaleProposal,
216
+ BigInt(tier.totalTokenProvided),
217
+ ]);
218
+ actions.push({ executor: tier.saleTokenAddress, value: "0", data: approveData });
219
+ }
220
+ actions.push({ executor: tokenSaleProposal, value: "0", data });
221
+ const metadata = {
222
+ proposalName,
223
+ proposalDescription: JSON.stringify(proposalDescription),
224
+ category: "tokenSale",
225
+ isMeta: false,
226
+ changes: {
227
+ proposedChanges: { tier },
228
+ currentChanges: {},
229
+ },
230
+ };
231
+ return wrapperResult({
232
+ metadata,
233
+ actions,
234
+ title: `Token Sale tier → ${tier.name}`,
235
+ detail: `Target: TokenSaleProposal(${tokenSaleProposal}).createTiers (1 tier)`,
236
+ });
237
+ }
238
+ catch (err) {
239
+ return errorResult(err instanceof Error ? err.message : String(err));
240
+ }
241
+ });
242
+ }
243
+ // ---------- 3. token_sale_recover ----------
244
+ function registerTokenSaleRecover(server) {
245
+ server.registerTool("dexe_proposal_build_token_sale_recover", {
246
+ title: "Wrapper: recover unsold tokens from token-sale tiers",
247
+ description: "Builds a 'Recover Token Sale' external proposal calling TokenSaleProposal.recover(tierIds).",
248
+ inputSchema: {
249
+ tokenSaleProposal: z.string(),
250
+ tierIds: z.array(z.string()).min(1),
251
+ proposalName: z.string().default("Recover Token Sale"),
252
+ proposalDescription: z.string().default(""),
253
+ },
254
+ outputSchema: payloadOutputSchema(),
255
+ }, async ({ tokenSaleProposal, tierIds, proposalName = "Recover Token Sale", proposalDescription = "", }) => {
256
+ if (!isAddress(tokenSaleProposal))
257
+ return errorResult(`Invalid tokenSaleProposal: ${tokenSaleProposal}`);
258
+ try {
259
+ const iface = new Interface(TOKEN_SALE_PROPOSAL_ABI);
260
+ const data = iface.encodeFunctionData("recover", [tierIds.map((n) => BigInt(n))]);
261
+ const actions = [{ executor: tokenSaleProposal, value: "0", data }];
262
+ const metadata = {
263
+ proposalName,
264
+ proposalDescription: JSON.stringify(proposalDescription),
265
+ category: "recoverTokenSale",
266
+ isMeta: false,
267
+ changes: {
268
+ proposedChanges: { tierIds },
269
+ currentChanges: {},
270
+ },
271
+ };
272
+ return wrapperResult({
273
+ metadata,
274
+ actions,
275
+ title: `Recover tiers [${tierIds.join(", ")}]`,
276
+ detail: `Target: TokenSaleProposal(${tokenSaleProposal}).recover`,
277
+ });
278
+ }
279
+ catch (err) {
280
+ return errorResult(err instanceof Error ? err.message : String(err));
281
+ }
282
+ });
283
+ }
284
+ // ---------- 4. create_staking_tier ----------
285
+ function registerCreateStakingTier(server) {
286
+ server.registerTool("dexe_proposal_build_create_staking_tier", {
287
+ title: "Wrapper: create a staking pool/tier via StakingProposal.createStaking",
288
+ description: "Builds a 'Create Staking Tier' external proposal calling StakingProposal.createStaking(rewardToken, rewardAmount, startedAt, deadline, metadata). For ERC20 reward tokens, automatically prepends an ERC20.approve action. For native tokens (isNative=true), sets the action value instead.",
289
+ inputSchema: {
290
+ stakingProposal: z.string().describe("StakingProposal contract address"),
291
+ rewardToken: z.string(),
292
+ rewardAmount: z.string(),
293
+ startedAt: z.string().describe("Unix seconds"),
294
+ deadline: z.string().describe("Unix seconds"),
295
+ stakingMetadataUrl: z.string().describe("ipfs://<cid> of staking-specific metadata"),
296
+ isNative: z.boolean().default(false).describe("True when reward token is native (BNB/ETH)"),
297
+ proposalName: z.string().default("Create Staking"),
298
+ proposalDescription: z.string().default(""),
299
+ },
300
+ outputSchema: payloadOutputSchema(),
301
+ }, async ({ stakingProposal, rewardToken, rewardAmount, startedAt, deadline, stakingMetadataUrl, isNative = false, proposalName = "Create Staking", proposalDescription = "", }) => {
302
+ if (!isAddress(stakingProposal))
303
+ return errorResult(`Invalid stakingProposal: ${stakingProposal}`);
304
+ if (!isAddress(rewardToken))
305
+ return errorResult(`Invalid rewardToken: ${rewardToken}`);
306
+ try {
307
+ const iface = new Interface(STAKING_PROPOSAL_ABI);
308
+ const createData = iface.encodeFunctionData("createStaking", [
309
+ rewardToken,
310
+ BigInt(rewardAmount),
311
+ BigInt(startedAt),
312
+ BigInt(deadline),
313
+ stakingMetadataUrl,
314
+ ]);
315
+ const actions = [];
316
+ if (isNative) {
317
+ actions.push({ executor: stakingProposal, value: rewardAmount, data: createData });
318
+ }
319
+ else {
320
+ const erc20Iface = new Interface(ERC20_GOV_ABI);
321
+ const approveData = erc20Iface.encodeFunctionData("approve", [stakingProposal, BigInt(rewardAmount)]);
322
+ actions.push({ executor: rewardToken, value: "0", data: approveData });
323
+ actions.push({ executor: stakingProposal, value: "0", data: createData });
324
+ }
325
+ const metadata = {
326
+ proposalName,
327
+ proposalDescription: JSON.stringify(proposalDescription),
328
+ category: "createStakingTier",
329
+ isMeta: false,
330
+ changes: {
331
+ proposedChanges: {
332
+ rewardToken,
333
+ rewardAmount,
334
+ startedAt,
335
+ deadline,
336
+ metadata: stakingMetadataUrl,
337
+ },
338
+ currentChanges: {},
339
+ },
340
+ };
341
+ return wrapperResult({
342
+ metadata,
343
+ actions,
344
+ title: `Create Staking → ${rewardAmount} of ${rewardToken}`,
345
+ detail: `Target: StakingProposal(${stakingProposal}).createStaking`,
346
+ });
347
+ }
348
+ catch (err) {
349
+ return errorResult(err instanceof Error ? err.message : String(err));
350
+ }
351
+ });
352
+ }
353
+ // ---------- 5. change_math_model ----------
354
+ function registerChangeMathModel(server) {
355
+ server.registerTool("dexe_proposal_build_change_math_model", {
356
+ title: "Wrapper: swap the DAO's vote-power math contract",
357
+ description: "Builds a 'Change Math Model' external proposal calling GovPool.changeVotePower(newVotePower). `newVotePower` is the address of a deployed power contract (LINEAR_POWER, POLYNOMIAL_POWER, or a custom one registered in PoolRegistry).",
358
+ inputSchema: {
359
+ govPool: z.string(),
360
+ newVotePower: z.string(),
361
+ proposalName: z.string().default("Change Vote Power"),
362
+ proposalDescription: z.string().default(""),
363
+ },
364
+ outputSchema: payloadOutputSchema(),
365
+ }, async ({ govPool, newVotePower, proposalName = "Change Vote Power", proposalDescription = "", }) => {
366
+ if (!isAddress(govPool))
367
+ return errorResult(`Invalid govPool: ${govPool}`);
368
+ if (!isAddress(newVotePower))
369
+ return errorResult(`Invalid newVotePower: ${newVotePower}`);
370
+ try {
371
+ const iface = new Interface(GOV_POOL_EXT_ABI);
372
+ const data = iface.encodeFunctionData("changeVotePower", [newVotePower]);
373
+ const actions = [{ executor: govPool, value: "0", data }];
374
+ const metadata = {
375
+ proposalName,
376
+ proposalDescription: JSON.stringify(proposalDescription),
377
+ category: "mathModel",
378
+ isMeta: false,
379
+ changes: {
380
+ proposedChanges: { newVotePower },
381
+ currentChanges: {},
382
+ },
383
+ };
384
+ return wrapperResult({
385
+ metadata,
386
+ actions,
387
+ title: `Change Vote Power → ${newVotePower}`,
388
+ detail: `Target: GovPool(${govPool}).changeVotePower`,
389
+ });
390
+ }
391
+ catch (err) {
392
+ return errorResult(err instanceof Error ? err.message : String(err));
393
+ }
394
+ });
395
+ }
396
+ // ---------- 6. modify_dao_profile ----------
397
+ function registerModifyDaoProfile(server) {
398
+ server.registerTool("dexe_proposal_build_modify_dao_profile", {
399
+ title: "Wrapper: update the DAO descriptionURL (name, avatar, links)",
400
+ description: "Builds a 'Modify DAO Profile' external proposal calling GovPool.editDescriptionURL(url). You upload the new DAO metadata JSON to IPFS first (via dexe_ipfs_upload_dao_metadata), then pass the resulting descriptionURL (ipfs://<cid>) here.",
401
+ inputSchema: {
402
+ govPool: z.string(),
403
+ newDescriptionURL: z.string().describe("ipfs://<cid> of new DAO metadata JSON"),
404
+ proposalName: z.string().default("Modify DAO Profile"),
405
+ proposalDescription: z.string().default(""),
406
+ previousDescriptionURL: z.string().optional(),
407
+ },
408
+ outputSchema: payloadOutputSchema(),
409
+ }, async ({ govPool, newDescriptionURL, proposalName = "Modify DAO Profile", proposalDescription = "", previousDescriptionURL, }) => {
410
+ if (!isAddress(govPool))
411
+ return errorResult(`Invalid govPool: ${govPool}`);
412
+ try {
413
+ const iface = new Interface(GOV_POOL_EXT_ABI);
414
+ const data = iface.encodeFunctionData("editDescriptionURL", [newDescriptionURL]);
415
+ const actions = [{ executor: govPool, value: "0", data }];
416
+ const metadata = {
417
+ proposalName,
418
+ proposalDescription: JSON.stringify(proposalDescription),
419
+ category: "daoProfileModification",
420
+ isMeta: true,
421
+ changes: {
422
+ proposedChanges: { descriptionUrl: newDescriptionURL },
423
+ currentChanges: { descriptionUrl: previousDescriptionURL ?? null },
424
+ },
425
+ };
426
+ return wrapperResult({
427
+ metadata,
428
+ actions,
429
+ title: `Modify DAO Profile → ${newDescriptionURL}`,
430
+ detail: `Target: GovPool(${govPool}).editDescriptionURL`,
431
+ });
432
+ }
433
+ catch (err) {
434
+ return errorResult(err instanceof Error ? err.message : String(err));
435
+ }
436
+ });
437
+ }
438
+ // ---------- 7. blacklist_management ----------
439
+ function registerBlacklistManagement(server) {
440
+ server.registerTool("dexe_proposal_build_blacklist", {
441
+ title: "Wrapper: add/remove addresses from the DAO token blacklist",
442
+ description: "Builds a 'Blacklist Management' external proposal. Emits up to 2 actions: one ERC20Gov.blacklist(add, true) and one ERC20Gov.blacklist(remove, false). Pass empty arrays to skip either.",
443
+ inputSchema: {
444
+ erc20Gov: z.string().describe("DAO ERC20Gov token contract"),
445
+ addAddresses: z.array(z.string()).default([]),
446
+ removeAddresses: z.array(z.string()).default([]),
447
+ proposalName: z.string().default("Blacklist Management"),
448
+ proposalDescription: z.string().default(""),
449
+ },
450
+ outputSchema: payloadOutputSchema(),
451
+ }, async ({ erc20Gov, addAddresses = [], removeAddresses = [], proposalName = "Blacklist Management", proposalDescription = "", }) => {
452
+ if (!isAddress(erc20Gov))
453
+ return errorResult(`Invalid erc20Gov: ${erc20Gov}`);
454
+ for (const a of [...addAddresses, ...removeAddresses]) {
455
+ if (!isAddress(a))
456
+ return errorResult(`Invalid blacklist address: ${a}`);
457
+ }
458
+ if (addAddresses.length === 0 && removeAddresses.length === 0) {
459
+ return errorResult("Must supply at least one address to add or remove");
460
+ }
461
+ try {
462
+ const iface = new Interface(ERC20_GOV_ABI);
463
+ const actions = [];
464
+ if (addAddresses.length) {
465
+ actions.push({
466
+ executor: erc20Gov,
467
+ value: "0",
468
+ data: iface.encodeFunctionData("blacklist", [addAddresses, true]),
469
+ });
470
+ }
471
+ if (removeAddresses.length) {
472
+ actions.push({
473
+ executor: erc20Gov,
474
+ value: "0",
475
+ data: iface.encodeFunctionData("blacklist", [removeAddresses, false]),
476
+ });
477
+ }
478
+ const metadata = {
479
+ proposalName,
480
+ proposalDescription: JSON.stringify(proposalDescription),
481
+ category: "blacklistManagement",
482
+ isMeta: false,
483
+ changes: {
484
+ proposedChanges: { addBlacklist: addAddresses, removeBlacklist: removeAddresses },
485
+ currentChanges: {},
486
+ },
487
+ };
488
+ return wrapperResult({
489
+ metadata,
490
+ actions,
491
+ title: `Blacklist: +${addAddresses.length} / -${removeAddresses.length}`,
492
+ detail: `Target: ERC20Gov(${erc20Gov}).blacklist (${actions.length} action${actions.length === 1 ? "" : "s"})`,
493
+ });
494
+ }
495
+ catch (err) {
496
+ return errorResult(err instanceof Error ? err.message : String(err));
497
+ }
498
+ });
499
+ }
500
+ // ---------- 8. reward_multiplier ----------
501
+ function registerRewardMultiplier(server) {
502
+ server.registerTool("dexe_proposal_build_reward_multiplier", {
503
+ title: "Wrapper: manage the DAO's reward-multiplier NFT contract",
504
+ description: "Four modes: 'set_address' (GovPool.setNftMultiplierAddress — ZERO to disable), 'set_token_uri' (ERC721Multiplier.setTokenURI on a tokenId), 'mint' (ERC721Multiplier.mint(to, multiplier, rewardPeriod, metadataUrl)), 'change_token' (ERC721Multiplier.changeToken(tokenId, multiplier, rewardPeriod) — modify existing NFT).",
505
+ inputSchema: {
506
+ mode: z.enum(["set_address", "set_token_uri", "mint", "change_token"]),
507
+ govPool: z.string().optional(),
508
+ nftMultiplierContract: z.string().optional(),
509
+ newMultiplierAddress: z.string().optional().describe("For mode=set_address"),
510
+ tokenId: z.string().optional().describe("For mode=set_token_uri or change_token"),
511
+ uri: z.string().optional().describe("For mode=set_token_uri"),
512
+ to: z.string().optional().describe("For mode=mint"),
513
+ multiplier: z.string().optional().describe("For mode=mint or change_token"),
514
+ rewardPeriod: z.string().default("0").describe("For mode=mint or change_token"),
515
+ metadataUrl: z.string().default("").describe("For mode=mint — metadata URI string"),
516
+ proposalName: z.string().default("Reward Multiplier"),
517
+ proposalDescription: z.string().default(""),
518
+ },
519
+ outputSchema: payloadOutputSchema(),
520
+ }, async (input) => {
521
+ const { mode, proposalName = "Reward Multiplier", proposalDescription = "" } = input;
522
+ try {
523
+ const actions = [];
524
+ if (mode === "set_address") {
525
+ if (!input.govPool || !isAddress(input.govPool))
526
+ return errorResult(`set_address requires valid govPool`);
527
+ const addr = input.newMultiplierAddress ?? ZeroAddress;
528
+ if (!isAddress(addr))
529
+ return errorResult(`Invalid newMultiplierAddress: ${addr}`);
530
+ const iface = new Interface(GOV_POOL_EXT_ABI);
531
+ actions.push({
532
+ executor: input.govPool,
533
+ value: "0",
534
+ data: iface.encodeFunctionData("setNftMultiplierAddress", [addr]),
535
+ });
536
+ }
537
+ else if (mode === "set_token_uri") {
538
+ if (!input.nftMultiplierContract || !isAddress(input.nftMultiplierContract))
539
+ return errorResult(`set_token_uri requires valid nftMultiplierContract`);
540
+ if (!input.tokenId)
541
+ return errorResult(`set_token_uri requires tokenId`);
542
+ if (input.uri === undefined)
543
+ return errorResult(`set_token_uri requires uri`);
544
+ const iface = new Interface(ERC721_MULTIPLIER_ABI);
545
+ actions.push({
546
+ executor: input.nftMultiplierContract,
547
+ value: "0",
548
+ data: iface.encodeFunctionData("setTokenURI", [BigInt(input.tokenId), input.uri]),
549
+ });
550
+ }
551
+ else if (mode === "change_token") {
552
+ if (!input.nftMultiplierContract || !isAddress(input.nftMultiplierContract))
553
+ return errorResult(`change_token requires valid nftMultiplierContract`);
554
+ if (!input.tokenId)
555
+ return errorResult(`change_token requires tokenId`);
556
+ if (!input.multiplier)
557
+ return errorResult(`change_token requires multiplier`);
558
+ const iface = new Interface(ERC721_MULTIPLIER_ABI);
559
+ actions.push({
560
+ executor: input.nftMultiplierContract,
561
+ value: "0",
562
+ data: iface.encodeFunctionData("changeToken", [
563
+ BigInt(input.tokenId),
564
+ BigInt(input.multiplier),
565
+ BigInt(input.rewardPeriod ?? "0"),
566
+ ]),
567
+ });
568
+ }
569
+ else {
570
+ // mint — 4-arg: mint(address, uint256, uint256, string)
571
+ if (!input.nftMultiplierContract || !isAddress(input.nftMultiplierContract))
572
+ return errorResult(`mint requires valid nftMultiplierContract`);
573
+ if (!input.to || !isAddress(input.to))
574
+ return errorResult(`mint requires valid to`);
575
+ if (!input.multiplier)
576
+ return errorResult(`mint requires multiplier`);
577
+ const iface = new Interface(ERC721_MULTIPLIER_ABI);
578
+ actions.push({
579
+ executor: input.nftMultiplierContract,
580
+ value: "0",
581
+ data: iface.encodeFunctionData("mint", [
582
+ input.to,
583
+ BigInt(input.multiplier),
584
+ BigInt(input.rewardPeriod ?? "0"),
585
+ input.metadataUrl ?? "",
586
+ ]),
587
+ });
588
+ }
589
+ const metadata = {
590
+ proposalName,
591
+ proposalDescription: JSON.stringify(proposalDescription),
592
+ category: "rewardMultiplier",
593
+ isMeta: false,
594
+ changes: {
595
+ proposedChanges: { ...input, mode },
596
+ currentChanges: {},
597
+ },
598
+ };
599
+ return wrapperResult({
600
+ metadata,
601
+ actions,
602
+ title: `Reward Multiplier (${mode})`,
603
+ detail: `${actions.length} action${actions.length === 1 ? "" : "s"} encoded`,
604
+ });
605
+ }
606
+ catch (err) {
607
+ return errorResult(err instanceof Error ? err.message : String(err));
608
+ }
609
+ });
610
+ }
611
+ // ---------- 9. apply_to_dao ----------
612
+ function registerApplyToDao(server) {
613
+ server.registerTool("dexe_proposal_build_apply_to_dao", {
614
+ title: "Wrapper: apply for/disburse DAO tokens to a receiver (transfer + optional mint)",
615
+ description: "Builds an 'Apply to DAO' external proposal. If the DAO treasury has enough tokens, emits one ERC20.transfer action. If not, emits ERC20Gov.transfer + ERC20Gov.mint for the shortfall. Pass `treasuryBalance` (in wei) so we decide correctly.",
616
+ inputSchema: {
617
+ token: z.string().describe("The token contract (ERC20 or ERC20Gov)"),
618
+ receiver: z.string(),
619
+ amount: z.string().describe("Total amount to grant, in wei"),
620
+ treasuryBalance: z
621
+ .string()
622
+ .default("0")
623
+ .describe("Current treasury balance of `token`. If >= amount, a single transfer is used."),
624
+ proposalName: z.string().default("Apply to DAO"),
625
+ proposalDescription: z.string().default(""),
626
+ },
627
+ outputSchema: payloadOutputSchema(),
628
+ }, async ({ token, receiver, amount, treasuryBalance = "0", proposalName = "Apply to DAO", proposalDescription = "", }) => {
629
+ if (!isAddress(token))
630
+ return errorResult(`Invalid token: ${token}`);
631
+ if (!isAddress(receiver))
632
+ return errorResult(`Invalid receiver: ${receiver}`);
633
+ try {
634
+ const iface = new Interface(ERC20_GOV_ABI);
635
+ const actions = [];
636
+ const total = BigInt(amount);
637
+ const have = BigInt(treasuryBalance);
638
+ actions.push({
639
+ executor: token,
640
+ value: "0",
641
+ data: iface.encodeFunctionData("transfer", [receiver, total]),
642
+ });
643
+ if (have < total) {
644
+ const shortfall = total - have;
645
+ actions.push({
646
+ executor: token,
647
+ value: "0",
648
+ data: iface.encodeFunctionData("mint", [receiver, shortfall]),
649
+ });
650
+ }
651
+ const metadata = {
652
+ proposalName,
653
+ proposalDescription: JSON.stringify(proposalDescription),
654
+ category: "applyToDao",
655
+ isMeta: false,
656
+ changes: {
657
+ proposedChanges: { receiver, tokenAmount: amount, tokenAddress: token },
658
+ currentChanges: { treasuryBalance },
659
+ },
660
+ };
661
+ return wrapperResult({
662
+ metadata,
663
+ actions,
664
+ title: `Apply to DAO: ${amount} of ${token} → ${receiver}`,
665
+ detail: `${actions.length} action${actions.length === 1 ? "" : "s"} (transfer${actions.length > 1 ? " + mint" : ""})`,
666
+ });
667
+ }
668
+ catch (err) {
669
+ return errorResult(err instanceof Error ? err.message : String(err));
670
+ }
671
+ });
672
+ }
673
+ // ---------- 10. new_proposal_type (also: enable_staking) ----------
674
+ function registerNewProposalType(server) {
675
+ server.registerTool("dexe_proposal_build_new_proposal_type", {
676
+ title: "Wrapper: register a new proposal settings template + bind executors",
677
+ description: "Builds a 'New Proposal Type' external proposal with 2 actions: GovSettings.addSettings([newSettings]) + GovSettings.changeExecutors(executors, [newSettingId, …]). This is also the path for enabling staking (executors include StakingProposal).",
678
+ inputSchema: {
679
+ govSettings: z.string(),
680
+ settings: z.object({
681
+ earlyCompletion: z.boolean(),
682
+ delegatedVotingAllowed: z.boolean(),
683
+ validatorsVote: z.boolean(),
684
+ duration: z.string(),
685
+ durationValidators: z.string(),
686
+ executionDelay: z.string().default("0"),
687
+ quorum: z.string(),
688
+ quorumValidators: z.string(),
689
+ minVotesForVoting: z.string(),
690
+ minVotesForCreating: z.string(),
691
+ rewardsInfo: z.object({
692
+ rewardToken: z.string(),
693
+ creationReward: z.string().default("0"),
694
+ executionReward: z.string().default("0"),
695
+ voteRewardsCoefficient: z.string().default("0"),
696
+ }),
697
+ executorDescription: z.string().default(""),
698
+ }),
699
+ executors: z.array(z.string()).min(1),
700
+ newSettingId: z
701
+ .string()
702
+ .describe("Id the new setting will receive on GovSettings (= current getSettingsLength()). The agent reads this before building."),
703
+ proposalName: z.string().default("New Proposal Type"),
704
+ proposalDescription: z.string().default(""),
705
+ },
706
+ outputSchema: payloadOutputSchema(),
707
+ }, async ({ govSettings, settings, executors, newSettingId, proposalName = "New Proposal Type", proposalDescription = "", }) => {
708
+ if (!isAddress(govSettings))
709
+ return errorResult(`Invalid govSettings: ${govSettings}`);
710
+ for (const e of executors) {
711
+ if (!isAddress(e))
712
+ return errorResult(`Invalid executor: ${e}`);
713
+ }
714
+ try {
715
+ const iface = new Interface(GOV_SETTINGS_FULL_ABI);
716
+ const tuple = [
717
+ settings.earlyCompletion,
718
+ settings.delegatedVotingAllowed,
719
+ settings.validatorsVote,
720
+ BigInt(settings.duration),
721
+ BigInt(settings.durationValidators),
722
+ BigInt(settings.executionDelay),
723
+ BigInt(settings.quorum),
724
+ BigInt(settings.quorumValidators),
725
+ BigInt(settings.minVotesForVoting),
726
+ BigInt(settings.minVotesForCreating),
727
+ [
728
+ settings.rewardsInfo.rewardToken,
729
+ BigInt(settings.rewardsInfo.creationReward),
730
+ BigInt(settings.rewardsInfo.executionReward),
731
+ BigInt(settings.rewardsInfo.voteRewardsCoefficient),
732
+ ],
733
+ settings.executorDescription,
734
+ ];
735
+ const addData = iface.encodeFunctionData("addSettings", [[tuple]]);
736
+ const changeData = iface.encodeFunctionData("changeExecutors", [
737
+ executors,
738
+ executors.map(() => BigInt(newSettingId)),
739
+ ]);
740
+ const actions = [
741
+ { executor: govSettings, value: "0", data: addData },
742
+ { executor: govSettings, value: "0", data: changeData },
743
+ ];
744
+ const metadata = {
745
+ proposalName,
746
+ proposalDescription: JSON.stringify(proposalDescription),
747
+ category: "createProposalType",
748
+ isMeta: false,
749
+ changes: {
750
+ proposedChanges: { settings, executors, newSettingId },
751
+ currentChanges: {},
752
+ },
753
+ };
754
+ return wrapperResult({
755
+ metadata,
756
+ actions,
757
+ title: `New Proposal Type (settingsId=${newSettingId}, ${executors.length} executors)`,
758
+ detail: `Target: GovSettings(${govSettings}).addSettings + changeExecutors (2 actions)`,
759
+ });
760
+ }
761
+ catch (err) {
762
+ return errorResult(err instanceof Error ? err.message : String(err));
763
+ }
764
+ });
765
+ }
766
+ //# sourceMappingURL=proposalBuildComplex.js.map