PathrockNetwork Gno Explorer
HomeBlocksTransactionsRealmsPackagesValidatorsAnalytics

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/crowdfund/v0

Realm
Open in gnoweb ↗

Overview

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

Files (3)

  • README.mdmarkdown
  • gnomod.tomltoml
  • crowdfund.gnogno
crowdfund.gnogno
1// Package crowdfund is an accounting-only port of the classic Solidity2// Kickstarter-style crowdfunding contract to a gno.land realm.3//4// A creator Launches a campaign with a funding goal and a duration (in blocks).5// Backers Pledge (and may Unpledge before the deadline). After the deadline the6// creator can Claim if the goal was met, otherwise backers can Refund.7//8// No real coin moves: pledges are tracked as plain uint64 accounting, exactly9// like the mapping(address => uint) balances of the Solidity original.10package crowdfund1112import (13	"strconv"1415	"chain"16	"chain/runtime"17	"chain/runtime/unsafe"1819	"gno.land/p/nt/avl/v0"20)2122// Campaign holds the state of a single crowdfunding campaign.23type Campaign struct {24	ID       int25	Creator  address26	Goal     uint6427	Deadline int64     // block height after which the campaign is closed28	Pledged  uint64    // running total pledged29	Claimed  bool      // creator already claimed the funds30	pledges  *avl.Tree // backer address (string) -> pledged uint6431}3233var (34	campaigns = avl.NewTree() // padded id -> *Campaign (ordered by id)35	nextID    = 136)3738// key zero-pads an id so avl iteration is in numeric order.39func key(id int) string {40	s := strconv.Itoa(id)41	for len(s) < 12 {42		s = "0" + s43	}44	return s45}4647func mustGet(id int) *Campaign {48	v := campaigns.Get(key(id))49	if v == nil {50		panic("campaign not found")51	}52	return v.(*Campaign)53}5455// Launch creates a new campaign owned by the caller and returns its id.56func Launch(cur realm, goal uint64, durationBlocks int) int {57	if goal == 0 {58		panic("goal must be > 0")59	}60	if durationBlocks <= 0 {61		panic("duration must be > 0")62	}63	caller := unsafe.PreviousRealm().Address()64	id := nextID65	nextID++66	c := &Campaign{67		ID:       id,68		Creator:  caller,69		Goal:     goal,70		Deadline: runtime.ChainHeight() + int64(durationBlocks),71		pledges:  avl.NewTree(),72	}73	campaigns.Set(key(id), c)74	chain.Emit("Launch",75		"id", strconv.Itoa(id),76		"creator", caller.String(),77		"goal", strconv.FormatUint(goal, 10))78	return id79}8081// Pledge backs campaign id with amount (before the deadline).82func Pledge(cur realm, id int, amount uint64) {83	if amount == 0 {84		panic("amount must be > 0")85	}86	c := mustGet(id)87	if runtime.ChainHeight() > c.Deadline {88		panic("campaign ended")89	}90	caller := unsafe.PreviousRealm().Address()91	prev := uint64(0)92	if v := c.pledges.Get(caller.String()); v != nil {93		prev = v.(uint64)94	}95	c.pledges.Set(caller.String(), prev+amount)96	c.Pledged += amount97	chain.Emit("Pledge",98		"id", strconv.Itoa(id),99		"from", caller.String(),100		"amount", strconv.FormatUint(amount, 10))101}102103// Unpledge withdraws amount from the caller's pledge (before the deadline).104func Unpledge(cur realm, id int, amount uint64) {105	if amount == 0 {106		panic("amount must be > 0")107	}108	c := mustGet(id)109	if runtime.ChainHeight() > c.Deadline {110		panic("campaign ended")111	}112	caller := unsafe.PreviousRealm().Address()113	v := c.pledges.Get(caller.String())114	if v == nil {115		panic("nothing pledged")116	}117	have := v.(uint64)118	if amount > have {119		panic("amount exceeds pledge")120	}121	if remaining := have - amount; remaining == 0 {122		c.pledges.Remove(caller.String())123	} else {124		c.pledges.Set(caller.String(), remaining)125	}126	c.Pledged -= amount127	chain.Emit("Unpledge",128		"id", strconv.Itoa(id),129		"from", caller.String(),130		"amount", strconv.FormatUint(amount, 10))131}132133// Claim lets the creator take the funds if the goal was met after the deadline.134func Claim(cur realm, id int) {135	c := mustGet(id)136	if runtime.ChainHeight() <= c.Deadline {137		panic("campaign not ended")138	}139	caller := unsafe.PreviousRealm().Address()140	if caller != c.Creator {141		panic("only creator can claim")142	}143	if c.Pledged < c.Goal {144		panic("goal not reached")145	}146	if c.Claimed {147		panic("already claimed")148	}149	c.Claimed = true150	chain.Emit("Claim",151		"id", strconv.Itoa(id),152		"amount", strconv.FormatUint(c.Pledged, 10))153}154155// Refund returns the caller's pledge if the campaign failed after the deadline.156func Refund(cur realm, id int) {157	c := mustGet(id)158	if runtime.ChainHeight() <= c.Deadline {159		panic("campaign not ended")160	}161	if c.Pledged >= c.Goal {162		panic("campaign succeeded, no refund")163	}164	caller := unsafe.PreviousRealm().Address()165	v := c.pledges.Get(caller.String())166	if v == nil {167		panic("nothing to refund")168	}169	amount := v.(uint64)170	c.pledges.Remove(caller.String())171	c.Pledged -= amount172	chain.Emit("Refund",173		"id", strconv.Itoa(id),174		"to", caller.String(),175		"amount", strconv.FormatUint(amount, 10))176}177178// --- pure/read-only helpers (also used by Render) ---179180// pct returns the funded percentage (0..100).181func pct(pledged, goal uint64) int {182	if goal == 0 {183		return 0184	}185	p := int(pledged * 100 / goal)186	if p > 100 {187		p = 100188	}189	return p190}191192// progressBar renders a fixed-width ▓░ bar for pledged/goal.193func progressBar(pledged, goal uint64) string {194	const width = 20195	filled := 0196	if goal > 0 {197		filled = int(pledged * width / goal)198		if filled > width {199			filled = width200		}201	}202	bar := ""203	for i := 0; i < width; i++ {204		if i < filled {205			bar += "▓"206		} else {207			bar += "░"208		}209	}210	return bar211}212213// state describes a campaign relative to a given block height.214func state(c *Campaign, height int64) string {215	if height <= c.Deadline {216		return "Active"217	}218	if c.Pledged >= c.Goal {219		if c.Claimed {220			return "Funded (claimed)"221		}222		return "Funded"223	}224	return "Failed"225}226227// Render shows all campaigns with a progress bar, goal, pledged and state.228func Render(path string) string {229	if campaigns.Size() == 0 {230		return "# Crowdfund\n\nNo campaigns yet. Call `Launch(goal, durationBlocks)` to start one.\n"231	}232	height := runtime.ChainHeight()233	out := "# Crowdfund\n\n"234	out += "Current block height: **" + strconv.FormatInt(height, 10) + "**\n\n"235	campaigns.Iterate("", "", func(k string, v any) bool {236		c := v.(*Campaign)237		out += "## Campaign #" + strconv.Itoa(c.ID) + "\n\n"238		out += "`" + progressBar(c.Pledged, c.Goal) + "` " + strconv.Itoa(pct(c.Pledged, c.Goal)) + "%\n\n"239		out += "- State: **" + state(c, height) + "**\n"240		out += "- Goal: " + strconv.FormatUint(c.Goal, 10) + "\n"241		out += "- Pledged: " + strconv.FormatUint(c.Pledged, 10) + "\n"242		out += "- Deadline: block " + strconv.FormatInt(c.Deadline, 10) + "\n"243		out += "- Creator: `" + c.Creator.String() + "`\n\n"244		return false245	})246	return out247}248

Functions

  • Claim(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}, id int)

  • Launch(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}, goal uint64, durationBlocks int) int

  • Pledge(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}, id int, amount uint64)

  • Refund(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}, id int)

  • Render(path string) string

  • Unpledge(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}, id int, amount uint64)

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.