Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

create.gno

4.42 Kb · 128 lines
  1package bolo
  2
  3import (
  4	"chain"
  5	"chain/banker"
  6	"chain/runtime"
  7
  8	"gno.land/p/nt/avl/v0"
  9	"gno.land/p/nt/seqid/v0"
 10)
 11
 12// maxDeadlineDays bounds how far ahead a deadline may be, so no timer computed from
 13// it can overflow (API §3.1).
 14const maxDeadlineDays = 3650
 15
 16// CreateCampaign opens a campaign, which you organize, and returns its ID. Send the
 17// creation deposit with it — Bolo's "Start a campaign" link attaches it — and it comes
 18// back when the campaign completes. title and description are public, and the
 19// description may hold links, never an image. category is one of Bolo's categories.
 20// imageCID and imageAlt are a photo on IPFS and what it shows, or both empty.
 21// beneficiary is the address the money goes to, which can never change. goal is in
 22// ugnot, 0 for none. mode is "escrow" or "direct". deadline is a Unix time, 0 for
 23// none.
 24//
 25// It opens a campaign for its caller, the organizer (API §3.1). The envelope is
 26// exactly the creation deposit, which goes to the campaign's bond address: the realm
 27// keeps nothing (INV-3).
 28func CreateCampaign(cur realm, title, description, category, imageCID, imageAlt string, beneficiary address, goal int64, mode string, deadline int64) string {
 29	assertCurrent(cur)
 30	if !cur.Previous().IsUserCall() {
 31		panic(ErrNotUserCall)
 32	}
 33	runtime.AssertOriginCall()
 34	if envelopeUgnot() != params.CreationDeposit {
 35		panic(ErrDepositMismatch)
 36	}
 37	if paused {
 38		panic(ErrPaused)
 39	}
 40	if sunset {
 41		panic(ErrSunset)
 42	}
 43	text := checkCampaignText(title, description, imageCID, imageAlt)
 44	checkCampaignTerms(category, mode, goal, deadline)
 45	checkBeneficiary(beneficiary, nextID+1)
 46
 47	id := nextID.Next()
 48	c := &campaign{
 49		id:          id,
 50		category:    category,
 51		organizer:   cur.Previous().Address(),
 52		beneficiary: beneficiary,
 53		mode:        mode,
 54		goal:        goal,
 55		deadline:    deadline,
 56		createdAt:   now(),
 57		params:      params,
 58
 59		text:  text,
 60		state: &campaignState{status: statusOpen, listed: true},
 61
 62		contributions: avl.NewTree(),
 63		receipts:      avl.NewTree(),
 64		withdrawals:   avl.NewTree(),
 65		refunds:       avl.NewTree(),
 66		updates:       avl.NewTree(),
 67	}
 68	key := id.Binary()
 69	campaigns.Set(key, c)
 70	stats.open++
 71	derived.Set(escrowAddr(id).String(), true)
 72	derived.Set(bondAddr(id).String(), true)
 73	addActive(c)
 74	byOrganizer.Set(c.organizer.String()+"/"+key, true)
 75	byBeneficiary.Set(beneficiary.String()+"/"+key, true)
 76
 77	banker.NewBanker(banker.BankerTypeRealmSend, cur).SendCoins(realmAddr(), bondAddr(id), chain.NewCoins(chain.NewCoin("ugnot", c.params.CreationDeposit)))
 78	chain.Emit(evCampaignCreated,
 79		"id", idString(id),
 80		"organizer", c.organizer.String(),
 81		"beneficiary", beneficiary.String(),
 82		"mode", mode,
 83		"category", category,
 84		"goal", itoa(goal),
 85		"deadline", itoa(deadline),
 86	)
 87	return idString(id)
 88}
 89
 90// checkCampaignText checks a campaign's text (API §2.3) and returns it normalized:
 91// the title, the story, and the photo with its description.
 92func checkCampaignText(title, description, imageCID, imageAlt string) *campaignText {
 93	title = checkText(title, titleMin, titleMax, false, ErrTitle)
 94	description = checkStory(description, 0, descriptionMax, ErrDescription)
 95	imageCID, imageAlt = checkImage(imageCID, imageAlt)
 96	return &campaignText{title: title, description: description, imageCID: imageCID, imageAlt: imageAlt}
 97}
 98
 99// checkCampaignTerms refuses a category not listed, a mode that isn't one, a
100// negative goal, and a deadline neither 0 nor in the future, at most maxDeadlineDays
101// ahead (API §3.1).
102func checkCampaignTerms(category, mode string, goal, deadline int64) {
103	if !categories.Has(category) {
104		panic(ErrCategory)
105	}
106	if mode != modeDirect && mode != modeEscrow {
107		panic(ErrMode)
108	}
109	if goal < 0 {
110		panic(ErrGoal)
111	}
112	if deadline != 0 && (deadline <= now() || deadline > now()+maxDeadlineDays*secondsPerDay) {
113		panic(ErrDeadline)
114	}
115}
116
117// checkBeneficiary refuses a malformed address (C12) and every address a campaign
118// must never pay (API §1): the realm's, slashed, the storage-deposit address, the
119// registry and its storage-deposit address, every escrow and bond address handed
120// out — and those of id, the campaign about to be created, which paying itself
121// would trap every gift.
122func checkBeneficiary(b address, id seqid.ID) {
123	checkAddress(b, ErrBeneficiary)
124	if b == realmAddr() || b == slashedAddr() || b == depositAddr() || b == registryAddr() || b == registryDepositAddr() ||
125		derived.Has(b.String()) || b == escrowAddr(id) || b == bondAddr(id) {
126		panic(ErrBeneficiary)
127	}
128}