Governance & Voting
Creating a Poll

Creating a Poll

Creating a poll takes two on-chain transactions (deploy the poll contract, then register it in the Registry) and costs roughly 0.02 AE. Allow 2–5 minutes for both to mine.

A poll cannot be edited after it is deployed. There is no entrypoint to change the title, fix a typo in the description, add an option, or move the close height. The only correction is a new poll. Get the text and the options right before you sign.

Before you start

CheckWhy
At least 0.02 AE in the walletTwo transactions, and the deploy is the expensive one
Final wording for title and descriptionNeither is editable afterwards
A reference linkRequired, and it must start with http:// or https://
The full option list, in orderOption indices are fixed at deployment and are 0-based
A close height, or a decision to leave it open-endedAlso not editable

Field limits

FieldConstraint
TitleRequired. Maximum 50 characters.
DescriptionRequired. Maximum 300 characters.
Reference linkRequired. Must start with http:// or https://.
OptionsAt least 2, each a non-empty label.
Close heightA future key-block height, or 0 for a poll that never closes.
ListingListed by default; unlisted keeps it out of the public listing.

The title and description limits are enforced on chain:

require(String.length(metadata.title) =< 50, "TITLE_STRING_TO_LONG")
require(String.length(metadata.description) =< 300, "DESCRIPTION_STRING_TO_LONG")

The lengths are counted in characters, not bytes, so 300 characters is 300 characters whatever alphabet they are in.

The link, spec_ref and the option map are not inspected by the contract; the http/https prefix rule and the two-option minimum are enforced by the clients. A poll with fewer than two options deploys but cannot be voted on usefully, because vote requires the option to exist in the map.

The link is stored on-chain as a string. Clients that render it must treat it as untrusted input; the Superhero app runs it through a URL-safety check before putting it in an href.

Choose a close height

close_height is a key-block height. æternity produces one key-block roughly every three minutes:

key-blocks per day = (24 * 60) / 3 = 480

close_height = current_height + days * 480
Poll lengthBlocks to add
1 day480
7 days3,360
14 days6,720
30 days14,400

The web form defaults to 30 days, computed as current_height + 14400.

Micro-blocks arrive about every three seconds, but close_height is a key-block height, so always convert at three minutes per block.

Setting 0, or omitting it in the CLI, creates a poll that never closes. Stake weighting is computed against the state at the close height, so an open-ended poll has no snapshot and produces a continuously moving figure rather than a settled outcome. Use it for standing sentiment, not for a decision.

Create from the web app

Go to superhero.com/voting/create (opens in a new tab), or open superhero.com/voting (opens in a new tab) and pick the Create Poll tab.

  1. Connect a wallet with at least 0.02 AE.
  2. Enter the title (50 characters) and the description (300 characters).
  3. Enter the link. If it does not point at forum.aeternity.com, the form suggests opening a forum thread and linking that instead. This is a hint, not a requirement.
  4. Choose Publicly Listed or Not Listed.
  5. Add your options. The form starts with two empty rows and adds another each time you fill the last one.
  6. Set the close height. The form pre-fills 30 days ahead and shows an estimated close date and time; editing either the date or the time recomputes the height at three minutes per block, and editing the height recomputes the date.
  7. Submit, and approve two transactions in your wallet: the poll deployment, then the registry entry.

On success the app takes you to /voting/p/:pollAddress, the poll's own page.

If the second transaction fails after the first succeeds, you have a deployed but unregistered poll. It exists on-chain and can be voted on by address, but it will not appear in any listing until somebody calls add_poll for it.

Create from the CLI

node scripts/superhero-governance.mjs create \
  "<title>" "<description>" "<link>" "<opt0,opt1,...>" [close_height] [--unlisted]
ArgumentNotes
titleMaximum 50 characters, trimmed
descriptionMaximum 300 characters, trimmed
linkMust start with http:// or https://
optionsOne comma-separated string, minimum 2 after trimming and dropping empties
close_heightOptional. A future height; 0 or omitted means open-ended.
--unlistedOptional. Equivalent to --listed false. Listed by default.
node scripts/superhero-governance.mjs create \
  "Should we upgrade to protocol v3?" \
  "Proposal to activate the protocol upgrade described at the link." \
  "https://forum.aeternity.com/t/upgrade-v3/123" \
  "Yes,No,Abstain"

A 14-day, unlisted poll, assuming a current height of 1,000,000:

node scripts/superhero-governance.mjs create \
  "Which feature should ship next?" \
  "Vote for the next milestone. Discussion at the link." \
  "https://forum.aeternity.com/t/next-milestone/456" \
  "Multisig wallets,State channels,NFT standard" \
  1006720 \
  --unlisted

The script validates every field before touching the chain, and checks a non-zero close height against the node's current height so it cannot be set in the past. On success it prints:

{
  "success": true,
  "action": "create_poll",
  "poll_id": "43",
  "poll_address": "ct_...",
  "is_listed": true,
  "vote_options": { "0": "Yes", "1": "No", "2": "Abstain" },
  "close_height": 1006720,
  "deploy_tx_hash": "th_...",
  "register_tx_hash": "th_..."
}

Keep poll_address; it is what every other command takes. poll_id is the registry's sequential id, usable with info-by-id.

Deploy without the script

If you are building your own client, the two calls are:

1.  Poll.init(metadata, vote_options, close_height)
      metadata     = { title, description, link, spec_ref }
      vote_options = map(int, string)   -- 0-based indices to labels
      close_height = option(int)        -- None for open-ended

2.  Registry.add_poll(poll: Poll, is_listed: bool) : int
      returns the poll's sequential registry id

Both Superhero paths leave spec_ref unset. The registry to call is the one for your network; see The Governance Registry.

Deploy the poll from the account that should be its author: Poll.state.author is set at deployment and is_author checks against it. The author has no special power over the poll afterwards (they cannot close it, edit it or delete it), but the attribution is permanent.

Registry.add_poll can be called by any account for any deployed poll, and the caller sets is_listed. The registry has no remove_poll, no set_listed and no update entrypoint, so a registration is permanent: the id, the cached title and the is_listed flag are fixed. If you register with the wrong is_listed value, register the poll again with the other value (both entries remain), or deploy a fresh poll.

Deploy the standard poll bytecode as shipped with the app or the agent skill. The governance backend computes stake_weighted_results only for polls built from the unmodified Poll_Iris.aes source; a poll compiled from modified source can be voted on but has no official result.

Transactions

Each transaction pays gas at the network's current gas price.

ActionTransactions
Create a poll2
Vote1
Revoke a vote1

Related