Proposals & Vote Subjects
A proposal's action is a value of the Sophia variant type VoteSubjects.subject, which has a
fixed list of seven constructors compiled into the contract. The set is closed. If an action
is not one of the constructors below, no vote can express it. Changing the set means deploying
a different DAO implementation and migrating to it with a ChangeDAO vote.
The subjects
In ACI order, which is also the variant tag order used on the wire:
| Tag | Subject | Parameters | Effect |
|---|---|---|---|
| 0 | VotePayout | address | Disburse the entire DAO balance to that account |
| 1 | VotePayoutAmount | address, int | Disburse a specified amount to that account |
| 2 | ChangeDAO | address | Transfer the treasury, the token sale's ownership and beneficiary rights, and ownership of community management to another DAO |
| 3 | ChangeMetaInfo | map(string, string) | Replace the community's metadata map in community management |
| 4 | ChangeMinimumTokenThreshold | int | Change the minimum token threshold for write access and for opening proposals |
| 5 | AddModerator | address | Appoint a moderator |
| 6 | DeleteModerator | address | Remove a moderator |
The tag is the index: a decoded subject arrives as an object keyed by the tag number, so tag
5 with one address payload is an AddModerator proposal. The Superhero wallet's transaction
summary decodes the subject in this order before asking you to sign.
Parameter shapes
| Subject | What the address must be |
|---|---|
VotePayout | An account (ak_) that receives the whole treasury |
VotePayoutAmount | An account (ak_) that receives amount |
ChangeDAO | A contract (ct_) implementing the ReplaceableDAO interface |
AddModerator / DeleteModerator | An account (ak_) to appoint or remove as moderator |
VotePayoutAmount's int is in aettos, with 10^18 aettos to one AE.
ChangeMinimumTokenThreshold's int is in the community token's smallest unit, so divide by
10^decimals to compare it against a displayed balance.
Payout amounts and the treasury balance
add_vote validates only that Call.value is 555 AE and that the caller holds the minimum
token threshold; it does not compare a payout amount to the DAO's balance. apply_vote_subject
executes the subject with Chain.spend(receiver, amount), which aborts if the balance is
insufficient and leaves the vote unapplied. If no application succeeds inside the execution
window, the proposal expires.
- Check the treasury before you propose an amount, and leave headroom. The balance can fall between proposal and execution: another payout vote may apply first, and treasury income only arrives on buys.
VotePayoutspendsContract.balance: whatever is there at execution, including zero, so it cannot fail this way.
Any number of proposals can be open at once, including conflicting ones. If two payout votes
both pass, both can be applied, in whatever order people call apply_vote_subject, until the
balance runs out.
ChangeMetaInfo replaces the whole metadata map. CommunityManagement.change_meta_info
takes a complete map(string, string); there is no per-key update, so a proposal that changes
one field must carry every other field unchanged.
The ChangeDAO safeguard
ChangeDAO hands over everything. The branch in apply_vote_subject:
VoteSubjects.ChangeDAO(new_dao_address) =>
let new_dao = Address.to_contract(new_dao_address) : ReplaceableDAO
require(new_dao.is_dao(), "NEW_DAO_INVALID")
Chain.spend(new_dao_address, Contract.balance)
get_token_sale().set_beneficiary(new_dao_address)
get_token_sale().set_owner(new_dao_address)
require(get_community_management() == new_dao.get_community_management(),
"NEW_DAO_HAS_TO_REFERENCE_SAME_COMMUNITY_MANAGEMENT")
call_community_management((community_management) => community_management.set_owner(new_dao_address))Five effects in one transaction: the treasury moves, the sale's beneficiary changes, the sale's
owner changes, the target is checked to point at the same CommunityManagement, and that
contract's ownership is transferred. Moderator appointments and threshold changes keep
working after a migration, because the new DAO becomes the owner that CommunityManagement checks.
Two guards protect the migration:
is_dao()must returntrue. An account address, a token address or an unrelated contract does not answer that call, so the migration cannot be applied to one.- The target must already reference the same
CommunityManagement. Deploy the replacement DAO pointing at the existing community management contract before the vote is applied; otherwise the whole transaction reverts and nothing moves.
set_beneficiary on the sale also runs its own verify_dao probe against the new address, so
a target whose record_sale_transaction is not payable fails there.
is_dao() is a shape check, not a trust check. Any contract can return true from it. Read
Replaceable DAOs before voting on a ChangeDAO proposal.
The metadata record
DAOVote.metadata = { subject : VoteSubjects.subject
, description : string
, link : string }| Field | Purpose |
|---|---|
subject | The machine-readable action. This is what executes. |
description | Free text explaining the proposal to voters. At most 300 characters. |
link | A reference URL, such as a forum thread, a spec or a budget. |
Only subject and the description length are enforced by the contract: DAOVote.init
requires String.length(metadata.description) =< 300. There is no title field. The link is
free text supplied by whoever opened the vote; the app runs it through a URL-safety check
before rendering it as a hyperlink, and any client that surfaces it should do the same.
Who may open a proposal
The same rule covers all seven subjects: a holder who owns at least the community's
minimum_token_threshold at the moment of the call can open a vote by calling
DAO.add_vote(metadata) with exactly 555 AE attached. The balance is read live from the AEX-9
contract for Call.caller; there is no snapshot and no holding period. There is no
moderator-only subject and no proposal whitelist. Moderators are appointed by vote but hold
no proposal rights of their own.
Open a proposal
stateful payable entrypoint add_vote(metadata: DAOVote.metadata) : (int, DAOVote)The call deploys a new DAOVote contract, registers it in the DAO's votes map under the
next sequential id, and returns both the id and the vote contract address. Keep both: the vote
address is where ballots are read and cast, and the id is what
apply_vote_subject(vote_seq_id) takes.
A worked VotePayoutAmount proposal
Paying 100 AE to a contributor, with the discussion thread as the reference link:
subject = VotePayoutAmount(ak_contributor..., 100000000000000000000)
description = "Pay 100 AE to the contributor who built the community bot, \
per the scope agreed in the linked thread."
link = "https://forum.aeternity.com/t/..."100000000000000000000 is 100 AE in aettos (100 * 10^18). A vote is not editable after it
is opened, so check the digit count before you sign; the Superhero wallet's transaction summary
shows the decoded subject and its payload.
From the app
Open the community's DAO at /trends/dao/:saleAddress, fill in the create-vote form, and
confirm the 555 AE payment in your wallet. VotePayoutAmount and ChangeMetaInfo proposals
are opened through the SDK or a direct contract call. The vote's detail page decodes and
displays the subject that was signed.
Related
- Voting: how holders vote on a proposal once it is open.
- Quorum and Thresholds: the numbers a proposal must clear.
- Replaceable DAOs: what a
ChangeDAOtarget has to implement. - Moderators and Write Thresholds: what the moderator and threshold subjects change.
- DAO contract reference:
add_voteandapply_vote_subjectin the full entrypoint table. - Open a DAO and its proposals (opens in a new tab): the live list of communities.