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
| Check | Why |
|---|---|
| At least 0.02 AE in the wallet | Two transactions, and the deploy is the expensive one |
| Final wording for title and description | Neither is editable afterwards |
| A reference link | Required, and it must start with http:// or https:// |
| The full option list, in order | Option indices are fixed at deployment and are 0-based |
| A close height, or a decision to leave it open-ended | Also not editable |
Field limits
| Field | Constraint |
|---|---|
| Title | Required. Maximum 50 characters. |
| Description | Required. Maximum 300 characters. |
| Reference link | Required. Must start with http:// or https://. |
| Options | At least 2, each a non-empty label. |
| Close height | A future key-block height, or 0 for a poll that never closes. |
| Listing | Listed 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 length | Blocks to add |
|---|---|
| 1 day | 480 |
| 7 days | 3,360 |
| 14 days | 6,720 |
| 30 days | 14,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.
- Connect a wallet with at least 0.02 AE.
- Enter the title (50 characters) and the description (300 characters).
- 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. - Choose Publicly Listed or Not Listed.
- Add your options. The form starts with two empty rows and adds another each time you fill the last one.
- 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.
- 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]| Argument | Notes |
|---|---|
title | Maximum 50 characters, trimmed |
description | Maximum 300 characters, trimmed |
link | Must start with http:// or https:// |
options | One comma-separated string, minimum 2 after trimming and dropping empties |
close_height | Optional. A future height; 0 or omitted means open-ended. |
--unlisted | Optional. 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 \
--unlistedThe 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 idBoth 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.
| Action | Transactions |
|---|---|
| Create a poll | 2 |
| Vote | 1 |
| Revoke a vote | 1 |
Related
- Polls: the data model you are populating, and which result figure counts.
- Voting on Polls: what happens once people start voting.
- The Governance Registry:
add_poll, the registry addresses, and the governance API. - Governance with an agent: the guarded workflow for creating polls from an agent.
- Create a poll (opens in a new tab): the live form.