dexe-create-proposal
dexe_proposal_create builds a governance proposal in one call: it checks
token balance, approves the UserKeeper (not GovPool), deposits, uploads IPFS
metadata with the correct {proposalName, proposalDescription, category, isMeta, changes} shape, and calls createProposalAndVote. With DEXE_PRIVATE_KEY it
signs+broadcasts; otherwise it returns ordered TxPayloads.
If you don't already know the target govPool/chain, call dexe_context —
it returns the signer, active chain, and the DAOs/proposals from prior sessions.
When the user already told you the DAO and what to do, go straight to
dexe_proposal_create.
Do not hand-sequence approve/deposit/create, and do not hand-build the IPFS
metadata — the composite does both correctly. Do not guess ABIs/selectors;
the wired builders encode canonical calldata. For a truly custom call use the
custom_abi type; dexe_proposal_catalog (or the playbook — MCP resource
dexe://playbook, docs/PLAYBOOK.md) lists every type and its params.
Amounts: a digits-only string is raw wei; a string with a decimal point
("1000.0") is human units, scaled by the token's real on-chain decimals
(never assumed 18). Both forms work in every params amount and voteAmount.
Pick a proposalType
Pass proposalType + the type's inputs in params:
| proposalType |
params |
token_transfer |
{ token, recipient, amount, isNative? } |
withdraw_treasury |
{ receiver, token?, amount?, nftAddress?, nftIds? } (emits external token.transfer) |
change_voting_settings |
{ govSettings, settings:[…], settingsIds? } (empty ids ⇒ addSettings) |
add_expert |
{ expertNftContract, scope:"local"|"global", nominatedUser, uri? } |
remove_expert |
{ expertNftContract, scope, nominatedUser } |
token_distribution |
{ distributionProposal, proposalId, token, amount, isNative? } |
token_sale |
{ tokenSaleProposal, tiers:[…], latestTierId? } |
custom_abi |
{ target, signature, method, args?, value? } |
modify_dao_profile |
top-level newDaoName/newDaoDescription/newWebsiteUrl/newSocialLinks; avatar via newAvatarPath (local image path — the server uploads + validates it; do NOT read the file yourself) or newAvatarCID |
custom |
top-level actionsOnFor:[{executor,value,data}] (+ optional category) |
All 33 catalog types are wired — proposalType is a strict enum; an
unknown string is rejected at validation with the list of valid types. Beyond
the table above: treasury/tokens (apply_to_dao, token_sale_recover,
token_sale_whitelist), governance config (new_proposal_type,
enable_staking, change_math_model, manage_validators /
validators_allocation), experts/delegation (delegate_to_expert /
revoke_from_expert, plus catalog-style aliases like
delegate_tokens_to_expert and add_local_expert / add_global_expert),
token controls (blacklist, reward_multiplier), and staking
(create_staking_tier).
Internal validator types (change_validator_balances,
change_validator_settings, monthly_withdraw, offchain_internal_proposal)
auto-route to GovValidators.createInternalProposal — validators only, no
deposit or UserKeeper approve. Off-chain types are on the DeXe backend, not
on-chain: build with dexe_proposal_build_offchain_*, then dexe_auth_login
(one call — signs the nonce internally when a signer is set; never write code
that extracts the private key to sign), then POST with the Bearer token. Only
single-option and multi-option off-chain voting exist in the DeXe product —
offchain_for_against is NOT creatable (the app has no for/against creation
path); for a binary vote use offchain_single_option with ["For","Against"].
Discover every type + params with dexe_proposal_catalog.
Example: transfer treasury tokens
dexe_proposal_create({
govPool: "0x…",
chainId: 97,
proposalType: "token_transfer",
title: "Pay contributor grant",
description: "Q3 grant to @alice.",
params: { token: "0xGovToken", recipient: "0xAlice", amount: "1000.0" } // human units
})
Example: update the DAO avatar (ONE call — no upload step, no file reading)
dexe_proposal_create({
govPool: "0x…",
proposalType: "modify_dao_profile",
title: "New DAO avatar",
newAvatarPath: "C:/Users/me/Pictures/logo.png" // server reads, validates, pins
})
The server reads the image from disk, rejects non-raster bytes (SVG never
renders on app.dexe.io), pins it, rebuilds the metadata, and creates the
proposal. Do not call dexe_ipfs_upload_avatar first and do not read the
image file into the conversation.
Failure modes this guards against
- Sequence — the composite runs approve→deposit→create; never do it by hand.
- Metadata shape — auto-built + preflight-validated (
{proposalName, proposalDescription, category, isMeta:false, changes:{proposedChanges, currentChanges}}). Wrong shape breaks the frontend indexer/diff.
- ABI/selector guessing — wired builders use canonical signatures; tuple
field order is easy to get wrong by hand.
- votingPower vs tokenBalance — deposited power is
tokenBalance(user,0).balance − ownedBalance, not votingPower() (which is 0
without a deposit). The composite computes it.
- Approve target — the composite approves the UserKeeper (which does
transferFrom), never GovPool.
- withdraw_treasury emits an external
token.transfer, never
GovPool.withdraw ("Gov: invalid internal data").
- Blacklisted recipient — token transfers to a blacklisted address are
refused up front (they'd stick the proposal in
SucceededFor forever).
- Quorum-danger gate (
confirmRisky) — a change_voting_settings /
new_proposal_type build that lowers quorum below the safe floor (into
treasury-drain territory) is refused before any transaction with
mode: "blocked-risky" + governanceAdvisories. If the lowering is
intentional, re-run the SAME call with confirmRisky: true. CAUTION-level
advisories (no-timelock, unreachable validator quorum) attach to the result
without blocking.
Cross-DAO delegation (partner DAO votes with delegated power)
A holder can delegate deposited power to another DAO's GovPool, and that DAO
then votes in the first DAO with the delegated (micropool) power:
dexe_vote_build_delegate({ govPool: A, delegatee: <DAO_B govPool>, amount })
→ broadcast. (First make every proposal you voted on in A terminal, or
this reverts GovUK: overdelegation — see [[dexe-vote-execute]].)
- In DAO B, create a
custom proposal whose action calls A's vote:
actionsOnFor: [{ executor: A, data: <A.multicall([vote(pid, true, 0, [])])> }]
(build the inner calldata with dexe_vote_build_vote({ govPool: A, proposalId, amount: "0" })).
- Pass + execute the DAO-B proposal → on execute, B calls
A.vote() and its
micropool power is counted (verify with
dexe_vote_get_votes(A, pid, voter: B, voteType: "MicropoolVote")).
Works when A has delegatedVotingAllowed=false (micropool voting).
No signer? Preview first
Pass dryRun: true to get the ordered TxPayloads without broadcasting (also
the default behavior when no signer is configured). Then broadcast via
dexe_tx_send or a connected wallet.
Next step after it passes: [[dexe-vote-execute]].
Canonical recipe (generated from src/knowledge/ — edit there, then npm run gen:knowledge)
Create a governance proposal (any type) (create_proposal)
Create ANY of the 33 catalog proposal types with one dexe_proposal_create call — it handles approve → deposit → create + IPFS metadata.
Ask the user:
govPool — Which DAO (govPool address)? If we just created one this session, confirm reusing it.
proposalType — What should the proposal DO? (map the user's intent to a proposalType via dexe_proposal_catalog — e.g. token_transfer, change_voting_settings, add_expert)
title — Proposal title (public)?
description (optional) — Short proposal description for voters? (optional but recommended)
Steps:
dexe_proposal_catalog — Only when unsure which proposalType matches the intent: list all 33 types with their target + effect. The per-type params shapes are in dexe_proposal_create's description and docs/PLAYBOOK.md. (skip when: the proposalType is already obvious from the user's request)
dexe_proposal_create — Approve → deposit → createProposalAndVote in one call, with correct IPFS metadata.
Pitfalls (danger first):
- 🔴 When depositing gov tokens, ERC20.approve must target the DAO's UserKeeper, NEVER the GovPool. Approving the GovPool burns gas and the deposit reverts. The composites (dexe_proposal_create, dexe_proposal_vote_and_execute) sequence this correctly — do not hand-build the approve.
- 🔴 CHAIN-ASYMMETRIC (verified 2026-07-23): on fresh TESTNET (97) pools, EXECUTING a proposal whose action is GovSettings.addSettings reverts 'disallowed tx pattern' — deterministically, re-running never helps. This hits change_voting_settings WITHOUT settingsIds, new_proposal_type, and enable_staking. On current MAINNET (56) fresh pools addSettings EXECUTES fine (proven 2026-07-22) — testnet runs an older protocol deployment. On 97: EDIT existing settings instead (pass settingsIds) — editSettings is always allowed — and do NOT use testnet to validate addSettings-based flows.
- ⚠ Tokens you voted with stay LOCKED per-proposal even after the proposal executes, and votingPower() reads 0 while locked (it shows available, not deposited, power). Between proposals run dexe_vote_build_withdraw to unlock, or the next create/vote fails with 'No voting power available'.
- ⚠ Fresh (SphereX-guarded, deployed ≥ 2026-07) pools reject multicall([deposit, createProposalAndVote]) with 'SphereX error: disallowed tx pattern'. Deposit and create must be SEPARATE transactions — the composites already send them separately; never re-bundle them.
- ⚠ settingsIds semantics: 0 = DEFAULT settings, 1 = INTERNAL settings (2 validators, 3 distribution, 4 tokenSale on fresh deploys). When editing, leave executorDescription blank to preserve the on-chain value — it holds the settings-JSON IPFS ref the frontend reads; overwriting it blanks the DAO's settings UI.
- ⚠ A token transfer to a blacklisted recipient passes the vote and then REVERTS at execute — the proposal is stuck in SucceededFor forever (there is no cancel). Before proposing transfers of an ERC20Gov token, verify the recipient isn't blacklisted.
- ℹ Creating a proposal requires approve(UserKeeper) → deposit(GovPool) → createProposal, in that order. dexe_proposal_create runs the whole sequence; on partial failure it returns the landed-steps ledger — fix the cause and re-run the SAME call. approve, deposit, createProposalAndVote and the vote are re-derived from chain state and skipped; GovPool.execute and the validator round are NOT, and a receipt-wait TIMEOUT means the transaction was already broadcast — check dexe_tx_status before re-running.
- ℹ The FIRST proposal on a freshly deployed DAO can revert 'low creating power' — the just-landed deposit isn't credited at snapshot time. This is transient: re-run the SAME dexe_proposal_create call; the ledger resume skips the landed deposit and the create succeeds.
- ℹ Amount strings: digits-only = RAW smallest units (wei); a decimal point ("12.5") = human units scaled by the token's REAL on-chain decimals (never assumed 18). Durations and delays are SECONDS (86400 = 1 day). Composite quorum/percent params are plain percent numbers (51).
For the machine-readable plan (interview questions with risk notes, step templates with flowContext chaining), call the dexe_guide tool with flow:"create_proposal".
1---2name: dexe-create-proposal3description: Create any DeXe governance proposal with the one-call `dexe_proposal_create` composite — it runs approve→deposit→createProposalAndVote and uploads correct IPFS metadata for you. Covers every wired proposalType + params recipe and the metadata/ABI/blacklist failure modes. Use when the user says "create a proposal", "transfer treasury", "add an expert", "change voting settings", "start a token sale".4---56# dexe-create-proposal78`dexe_proposal_create` builds a governance proposal in **one call**: it checks9token balance, approves the **UserKeeper** (not GovPool), deposits, uploads IPFS10metadata with the correct `{proposalName, proposalDescription, category, isMeta,11changes}` shape, and calls `createProposalAndVote`. With `DEXE_PRIVATE_KEY` it12signs+broadcasts; otherwise it returns ordered `TxPayload`s.1314If you don't already know the target `govPool`/chain, call **`dexe_context`** —15it returns the signer, active chain, and the DAOs/proposals from prior sessions.16When the user already told you the DAO and what to do, go straight to17`dexe_proposal_create`.1819**Do not hand-sequence** approve/deposit/create, and do not hand-build the IPFS20metadata — the composite does both correctly. **Do not guess ABIs/selectors**;21the wired builders encode canonical calldata. For a truly custom call use the22`custom_abi` type; `dexe_proposal_catalog` (or the playbook — MCP resource23`dexe://playbook`, `docs/PLAYBOOK.md`) lists every type and its params.2425**Amounts:** a digits-only string is raw wei; a string with a decimal point26(`"1000.0"`) is human units, scaled by the token's **real on-chain decimals**27(never assumed 18). Both forms work in every `params` amount and `voteAmount`.2829## Pick a proposalType3031Pass `proposalType` + the type's inputs in `params`:3233| proposalType | params |34|---|---|35| `token_transfer` | `{ token, recipient, amount, isNative? }` |36| `withdraw_treasury` | `{ receiver, token?, amount?, nftAddress?, nftIds? }` (emits external `token.transfer`) |37| `change_voting_settings` | `{ govSettings, settings:[…], settingsIds? }` (empty ids ⇒ addSettings) |38| `add_expert` | `{ expertNftContract, scope:"local"\|"global", nominatedUser, uri? }` |39| `remove_expert` | `{ expertNftContract, scope, nominatedUser }` |40| `token_distribution` | `{ distributionProposal, proposalId, token, amount, isNative? }` |41| `token_sale` | `{ tokenSaleProposal, tiers:[…], latestTierId? }` |42| `custom_abi` | `{ target, signature, method, args?, value? }` |43| `modify_dao_profile` | top-level `newDaoName/newDaoDescription/newWebsiteUrl/newSocialLinks`; avatar via `newAvatarPath` (local image path — the server uploads + validates it; do NOT read the file yourself) or `newAvatarCID` |44| `custom` | top-level `actionsOnFor:[{executor,value,data}]` (+ optional `category`) |4546**All 33 catalog types are wired** — `proposalType` is a strict enum; an47unknown string is rejected at validation with the list of valid types. Beyond48the table above: treasury/tokens (`apply_to_dao`, `token_sale_recover`,49`token_sale_whitelist`), governance config (`new_proposal_type`,50`enable_staking`, `change_math_model`, `manage_validators` /51`validators_allocation`), experts/delegation (`delegate_to_expert` /52`revoke_from_expert`, plus catalog-style aliases like53`delegate_tokens_to_expert` and `add_local_expert` / `add_global_expert`),54token controls (`blacklist`, `reward_multiplier`), and staking55(`create_staking_tier`).5657Internal validator types (`change_validator_balances`,58`change_validator_settings`, `monthly_withdraw`, `offchain_internal_proposal`)59**auto-route to `GovValidators.createInternalProposal`** — validators only, no60deposit or UserKeeper approve. Off-chain types are on the DeXe backend, not61on-chain: build with `dexe_proposal_build_offchain_*`, then **`dexe_auth_login`**62(one call — signs the nonce internally when a signer is set; never write code63that extracts the private key to sign), then POST with the Bearer token. Only64**single-option and multi-option** off-chain voting exist in the DeXe product —65**`offchain_for_against` is NOT creatable** (the app has no for/against creation66path); for a binary vote use `offchain_single_option` with `["For","Against"]`.67Discover every type + params with `dexe_proposal_catalog`.6869## Example: transfer treasury tokens7071```jsonc72dexe_proposal_create({73 govPool: "0x…",74 chainId: 97,75 proposalType: "token_transfer",76 title: "Pay contributor grant",77 description: "Q3 grant to @alice.",78 params: { token: "0xGovToken", recipient: "0xAlice", amount: "1000.0" } // human units79})80```8182## Example: update the DAO avatar (ONE call — no upload step, no file reading)8384```jsonc85dexe_proposal_create({86 govPool: "0x…",87 proposalType: "modify_dao_profile",88 title: "New DAO avatar",89 newAvatarPath: "C:/Users/me/Pictures/logo.png" // server reads, validates, pins90})91```9293The server reads the image from disk, rejects non-raster bytes (SVG never94renders on app.dexe.io), pins it, rebuilds the metadata, and creates the95proposal. Do not call `dexe_ipfs_upload_avatar` first and do not read the96image file into the conversation.9798## Failure modes this guards against991001. **Sequence** — the composite runs approve→deposit→create; never do it by hand.1012. **Metadata shape** — auto-built + preflight-validated (`{proposalName,102 proposalDescription, category, isMeta:false, changes:{proposedChanges,103 currentChanges}}`). Wrong shape breaks the frontend indexer/diff.1043. **ABI/selector guessing** — wired builders use canonical signatures; tuple105 field order is easy to get wrong by hand.1064. **votingPower vs tokenBalance** — deposited power is107 `tokenBalance(user,0).balance − ownedBalance`, not `votingPower()` (which is 0108 without a deposit). The composite computes it.1096. **Approve target** — the composite approves the **UserKeeper** (which does110 `transferFrom`), never GovPool.1118. **withdraw_treasury** emits an external `token.transfer`, never112 `GovPool.withdraw` ("Gov: invalid internal data").11310. **Blacklisted recipient** — token transfers to a blacklisted address are114 refused up front (they'd stick the proposal in `SucceededFor` forever).11511. **Quorum-danger gate (`confirmRisky`)** — a `change_voting_settings` /116 `new_proposal_type` build that lowers quorum below the safe floor (into117 treasury-drain territory) is refused **before any transaction** with118 `mode: "blocked-risky"` + `governanceAdvisories`. If the lowering is119 intentional, re-run the SAME call with `confirmRisky: true`. CAUTION-level120 advisories (no-timelock, unreachable validator quorum) attach to the result121 without blocking.122123## Cross-DAO delegation (partner DAO votes with delegated power)124125A holder can delegate deposited power to another DAO's GovPool, and that DAO126then votes in the first DAO with the delegated (micropool) power:1271281. `dexe_vote_build_delegate({ govPool: A, delegatee: <DAO_B govPool>, amount })`129 → broadcast. (First make every proposal you voted on in A **terminal**, or130 this reverts `GovUK: overdelegation` — see [[dexe-vote-execute]].)1312. In DAO B, create a `custom` proposal whose action calls A's vote:132 `actionsOnFor: [{ executor: A, data: <A.multicall([vote(pid, true, 0, [])])> }]`133 (build the inner calldata with `dexe_vote_build_vote({ govPool: A, proposalId, amount: "0" })`).1343. Pass + execute the DAO-B proposal → on execute, B calls `A.vote()` and its135 micropool power is counted (verify with136 `dexe_vote_get_votes(A, pid, voter: B, voteType: "MicropoolVote")`).137 Works when A has `delegatedVotingAllowed=false` (micropool voting).138139## No signer? Preview first140141Pass `dryRun: true` to get the ordered `TxPayload`s without broadcasting (also142the default behavior when no signer is configured). Then broadcast via143`dexe_tx_send` or a connected wallet.144145Next step after it passes: [[dexe-vote-execute]].146147## Canonical recipe (generated from src/knowledge/ — edit there, then `npm run gen:knowledge`)148149<!-- BEGIN GENERATED: flow-recipe -->150### Create a governance proposal (any type) (`create_proposal`)151152Create ANY of the 33 catalog proposal types with one dexe_proposal_create call — it handles approve → deposit → create + IPFS metadata.153154**Ask the user:**155- `govPool` — Which DAO (govPool address)? If we just created one this session, confirm reusing it.156- `proposalType` — What should the proposal DO? (map the user's intent to a proposalType via dexe_proposal_catalog — e.g. token_transfer, change_voting_settings, add_expert)157- `title` — Proposal title (public)?158- `description` (optional) — Short proposal description for voters? (optional but recommended)159160**Steps:**1611. `dexe_proposal_catalog` — Only when unsure which proposalType matches the intent: list all 33 types with their target + effect. The per-type params shapes are in dexe_proposal_create's description and docs/PLAYBOOK.md. _(skip when: the proposalType is already obvious from the user's request)_1622. `dexe_proposal_create` — Approve → deposit → createProposalAndVote in one call, with correct IPFS metadata.163164**Pitfalls (danger first):**165- 🔴 When depositing gov tokens, ERC20.approve must target the DAO's UserKeeper, NEVER the GovPool. Approving the GovPool burns gas and the deposit reverts. The composites (dexe_proposal_create, dexe_proposal_vote_and_execute) sequence this correctly — do not hand-build the approve.166- 🔴 CHAIN-ASYMMETRIC (verified 2026-07-23): on fresh TESTNET (97) pools, EXECUTING a proposal whose action is GovSettings.addSettings reverts 'disallowed tx pattern' — deterministically, re-running never helps. This hits change_voting_settings WITHOUT settingsIds, new_proposal_type, and enable_staking. On current MAINNET (56) fresh pools addSettings EXECUTES fine (proven 2026-07-22) — testnet runs an older protocol deployment. On 97: EDIT existing settings instead (pass settingsIds) — editSettings is always allowed — and do NOT use testnet to validate addSettings-based flows.167- ⚠ Tokens you voted with stay LOCKED per-proposal even after the proposal executes, and votingPower() reads 0 while locked (it shows available, not deposited, power). Between proposals run dexe_vote_build_withdraw to unlock, or the next create/vote fails with 'No voting power available'.168- ⚠ Fresh (SphereX-guarded, deployed ≥ 2026-07) pools reject multicall([deposit, createProposalAndVote]) with 'SphereX error: disallowed tx pattern'. Deposit and create must be SEPARATE transactions — the composites already send them separately; never re-bundle them.169- ⚠ settingsIds semantics: 0 = DEFAULT settings, 1 = INTERNAL settings (2 validators, 3 distribution, 4 tokenSale on fresh deploys). When editing, leave executorDescription blank to preserve the on-chain value — it holds the settings-JSON IPFS ref the frontend reads; overwriting it blanks the DAO's settings UI.170- ⚠ A token transfer to a blacklisted recipient passes the vote and then REVERTS at execute — the proposal is stuck in SucceededFor forever (there is no cancel). Before proposing transfers of an ERC20Gov token, verify the recipient isn't blacklisted.171- ℹ Creating a proposal requires approve(UserKeeper) → deposit(GovPool) → createProposal, in that order. dexe_proposal_create runs the whole sequence; on partial failure it returns the landed-steps ledger — fix the cause and re-run the SAME call. approve, deposit, createProposalAndVote and the vote are re-derived from chain state and skipped; GovPool.execute and the validator round are NOT, and a receipt-wait TIMEOUT means the transaction was already broadcast — check dexe_tx_status before re-running.172- ℹ The FIRST proposal on a freshly deployed DAO can revert 'low creating power' — the just-landed deposit isn't credited at snapshot time. This is transient: re-run the SAME dexe_proposal_create call; the ledger resume skips the landed deposit and the create succeeds.173- ℹ Amount strings: digits-only = RAW smallest units (wei); a decimal point ("12.5") = human units scaled by the token's REAL on-chain decimals (never assumed 18). Durations and delays are SECONDS (86400 = 1 day). Composite quorum/percent params are plain percent numbers (51).174175_For the machine-readable plan (interview questions with risk notes, step templates with `flowContext` chaining), call the `dexe_guide` tool with `flow:"create_proposal"`._176<!-- END GENERATED: flow-recipe -->