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