PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/r/moul/x/daily/polls/v0

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
v0
Namespace
moul / x / daily / polls
Files
3 (README)(gnomod.toml)
Exported functions
4
Module
gno.land/r/moul/x/daily/polls/v0
gno
0.9

Files (3)

  • README.mdmarkdown
  • gnomod.tomltoml
  • polls.gnogno
polls.gnogno
1// Package polls is an on-chain poll / voting realm for gno.land.2//3// Anyone can create a poll with a question and a comma-separated list of4// options. Each caller address may cast exactly one vote per poll. Render5// draws every poll with its options, tallies as ▓░ bar charts, percentages6// and the total vote count.7package polls89import (10	"chain"11	"chain/runtime/unsafe"12	"strconv"13	"strings"1415	"gno.land/p/nt/avl/v0"16)1718// Poll is a single question with its options and per-address vote tracking.19type Poll struct {20	ID       int21	Question string22	Options  []string23	Tallies  []int24	Creator  string    // bech32 address of the poll creator25	voted    *avl.Tree // address string -> struct{}{}, one vote per address26}2728var (29	polls  []*Poll // index i holds the poll with ID i30	nextID int31)3233// CreatePoll registers a new poll. options is a comma-separated list; blank34// entries are dropped. Returns the new poll's id. Crossing function: an EOA35// invokes it via MsgCall; another realm via polls.CreatePoll(cross, q, opts).36func CreatePoll(cur realm, question string, options string) int {37	creator := unsafe.PreviousRealm().Address().String()3839	q := strings.TrimSpace(question)40	if q == "" {41		panic("question must not be empty")42	}4344	opts := parseOptions(options)45	if len(opts) < 2 {46		panic("a poll needs at least 2 options")47	}4849	id := nextID50	nextID++51	p := &Poll{52		ID:       id,53		Question: q,54		Options:  opts,55		Tallies:  make([]int, len(opts)),56		Creator:  creator,57		voted:    avl.NewTree(),58	}59	polls = append(polls, p)6061	chain.Emit("PollCreated",62		"id", strconv.Itoa(id),63		"creator", creator,64		"options", strconv.Itoa(len(opts)),65	)66	return id67}6869// Vote casts one vote for optionIndex on poll pollID. Each caller address may70// vote at most once per poll. Crossing function.71func Vote(cur realm, pollID int, optionIndex int) {72	voter := unsafe.PreviousRealm().Address().String()7374	p := getPoll(pollID)75	if optionIndex < 0 || optionIndex >= len(p.Options) {76		panic("option index out of range")77	}78	if p.voted.Has(voter) {79		panic("address already voted on this poll")80	}8182	p.voted.Set(voter, struct{}{})83	p.Tallies[optionIndex]++8485	chain.Emit("Voted",86		"id", strconv.Itoa(pollID),87		"voter", voter,88		"option", strconv.Itoa(optionIndex),89	)90}9192// parseOptions splits a comma-separated option string, trimming whitespace and93// dropping empties. Pure logic — no realm state.94func parseOptions(options string) []string {95	var out []string96	for _, raw := range strings.Split(options, ",") {97		o := strings.TrimSpace(raw)98		if o != "" {99			out = append(out, o)100		}101	}102	return out103}104105// getPoll returns the poll with the given id or panics if it does not exist.106func getPoll(id int) *Poll {107	if id < 0 || id >= len(polls) {108		panic("poll not found")109	}110	return polls[id]111}112113// PollCount returns the number of polls created so far (read-only helper).114func PollCount() int {115	return len(polls)116}117118const barWidth = 20119120// bar renders a proportional ▓░ bar of fixed width for count out of total.121func bar(count, total int) string {122	filled := 0123	if total > 0 {124		filled = count * barWidth / total125	}126	if filled > barWidth {127		filled = barWidth128	}129	return strings.Repeat("▓", filled) + strings.Repeat("░", barWidth-filled)130}131132// percent returns the integer percentage of count out of total.133func percent(count, total int) int {134	if total == 0 {135		return 0136	}137	return count * 100 / total138}139140// total sums a tally slice.141func total(tallies []int) int {142	sum := 0143	for _, t := range tallies {144		sum += t145	}146	return sum147}148149// renderPoll writes one poll's Markdown block into b.150func renderPoll(b *strings.Builder, p *Poll) {151	sum := total(p.Tallies)152153	b.WriteString("## Poll #")154	b.WriteString(strconv.Itoa(p.ID))155	b.WriteString(": ")156	b.WriteString(p.Question)157	b.WriteString("\n\n")158159	for i, opt := range p.Options {160		c := p.Tallies[i]161		b.WriteString("- **")162		b.WriteString(opt)163		b.WriteString("** `")164		b.WriteString(bar(c, sum))165		b.WriteString("` ")166		b.WriteString(strconv.Itoa(percent(c, sum)))167		b.WriteString("% (")168		b.WriteString(strconv.Itoa(c))169		if c == 1 {170			b.WriteString(" vote)")171		} else {172			b.WriteString(" votes)")173		}174		b.WriteString("\n")175	}176177	b.WriteString("\n_Total votes: ")178	b.WriteString(strconv.Itoa(sum))179	b.WriteString("_\n\n")180}181182// Render produces the Markdown page for gnoweb. It is NOT a crossing function.183//184//   - ""        -> index of all polls185//   - "<id>"    -> a single poll by id186func Render(path string) string {187	var b strings.Builder188189	path = strings.TrimSpace(path)190	if path != "" {191		id, err := strconv.Atoi(path)192		if err != nil || id < 0 || id >= len(polls) {193			return "# Polls\n\nPoll not found.\n"194		}195		b.WriteString("# Polls\n\n")196		renderPoll(&b, polls[id])197		b.WriteString("[← all polls](/r/REPLACE_ADDR/polls)\n")198		return b.String()199	}200201	b.WriteString("# Polls & Voting\n\n")202	if len(polls) == 0 {203		b.WriteString("_No polls yet. Create one with `CreatePoll(question, \"a,b,c\")`._\n")204		return b.String()205	}206207	b.WriteString("Total polls: ")208	b.WriteString(strconv.Itoa(len(polls)))209	b.WriteString("\n\n")210	for _, p := range polls {211		renderPoll(&b, p)212	}213	return b.String()214}215

Functions

  • CreatePoll(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, question string, options string) int

  • PollCount() int

  • Render(path string) string

  • Vote(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, pollID int, optionIndex int)

Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.

Rendered

RenderedRawgnoweb ↗

vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.