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.

finalize.gno

4.49 Kb · 123 lines
  1package bolo
  2
  3import (
  4	"chain"
  5	"chain/banker"
  6)
  7
  8// Finalize completes closed campaign id: anyone can, once the campaign's page offers
  9// it. It returns the organizer's deposit, and sends the beneficiary the coins someone
 10// sent to the escrow outside Bolo. What donors can still reclaim stays theirs.
 11//
 12// It settles a closed campaign, and forwards what no donor is owed (API §3.4): anyone
 13// may call, with no identity. At its first call it settles the campaign;
 14// every call forwards the coins sent to the escrow outside Bolo to the beneficiary,
 15// and everything at the bond address but a bond still on hold to the organizer — the
 16// deposit, and the bond once its hold has ended. It never moves the tracked balance:
 17// that stays the donors' (INV-16).
 18func Finalize(cur realm, id string) {
 19	assertCurrent(cur)
 20	c := campaignOf(id)
 21	recordDeadline(cur, c)
 22	f := bookFinalize(c)
 23	if f.toBeneficiary > 0 {
 24		banker.NewBanker(banker.BankerTypeRealmSend, escrowSub(cur, c)).SendCoins(escrowAddr(c.id), c.beneficiary, chain.NewCoins(chain.NewCoin("ugnot", f.toBeneficiary)))
 25	}
 26	if f.toOrganizer > 0 {
 27		banker.NewBanker(banker.BankerTypeRealmSend, bondSub(cur, c)).SendCoins(bondAddr(c.id), c.organizer, chain.NewCoins(chain.NewCoin("ugnot", f.toOrganizer)))
 28	}
 29	if f.settled {
 30		chain.Emit(evSettled, "id", idString(c.id), "toOrganizer", itoa(f.toOrganizer), "toBeneficiary", itoa(f.toBeneficiary))
 31	}
 32	if f.released > 0 {
 33		chain.Emit(evBondReleased, "id", idString(c.id), "amount", itoa(f.released), "to", c.organizer.String())
 34	}
 35}
 36
 37// settlement is what one Finalize call moves, and announces.
 38type settlement struct {
 39	toBeneficiary int64 // the untracked coins at the escrow
 40	toOrganizer   int64 // everything at the bond address but a bond still on hold
 41	released      int64 // the bond this call releases, or 0
 42	settled       bool  // this call settles the campaign
 43}
 44
 45// bookFinalize checks a Finalize call on c and enters it in the books, before any coin
 46// moves (API §3.4): the first settles c, if checkFinalizable allows; every one works
 47// out what to forward, and releases a bond whose hold has ended.
 48func bookFinalize(c *campaign) settlement {
 49	s := c.state
 50	settled := false
 51	if s.status != statusSettled {
 52		checkFinalizable(c)
 53		s.status = statusSettled
 54		stats.settled++
 55		settled = true
 56	}
 57	f := pending(c)
 58	f.settled = settled
 59	if f.released > 0 {
 60		s.bondReleased = true
 61	}
 62	return f
 63}
 64
 65// checkFinalizable refuses c's first settlement (API §3.4), with ErrNotFinalizable,
 66// unless it is closed and its FinalizableAt has come: at once in direct mode or with
 67// no tracked balance, else once its exit window opens. The view, CanFinalize and this
 68// share one rule, finalizableAt.
 69func checkFinalizable(c *campaign) {
 70	s := c.state
 71	if s.status != statusClosed {
 72		panic(ErrNotFinalizable)
 73	}
 74	if now() < finalizableAt(c, s.closedAt, trackedBalance(c)) {
 75		panic(ErrNotFinalizable)
 76	}
 77}
 78
 79// pending works out what a Finalize call would move from c now, without writing: the
 80// escrow's coins above its books, to the beneficiary; the bond address's coins but a
 81// bond still on hold — not slashed, not released, its hold not ended — to the
 82// organizer, the bond itself included once its hold has ended. Books above the bank,
 83// at either address, are ErrBooksMismatch (INV-1, INV-7).
 84func pending(c *campaign) settlement {
 85	s := c.state
 86	var f settlement
 87	f.toBeneficiary = banker.NewReadonlyBanker().GetCoin(escrowAddr(c.id), "ugnot") - trackedBalance(c)
 88	held := int64(0)
 89	if s.bond > 0 && !s.bondSlashed && !s.bondReleased {
 90		if now() >= s.closedAt+c.params.BondHold {
 91			f.released = s.bond
 92		} else {
 93			held = s.bond
 94		}
 95	}
 96	f.toOrganizer = banker.NewReadonlyBanker().GetCoin(bondAddr(c.id), "ugnot") - held
 97	if f.toBeneficiary < 0 || f.toOrganizer < 0 {
 98		panic(ErrBooksMismatch)
 99	}
100	return f
101}
102
103// CanFinalize says what Finalize(id) would do now (API §4), a passed deadline counted
104// as recorded: true with "settle" for the first settlement, or "forward" on a settled
105// campaign with something to move; false with "open", "not_yet" while a tracked balance
106// waits for its exit window, or "settled" with nothing to move.
107func CanFinalize(id string) (bool, string) {
108	c := campaignOf(id)
109	status, _, closedAt := effective(c)
110	switch status {
111	case statusOpen:
112		return false, statusOpen
113	case statusClosed:
114		if now() < finalizableAt(c, closedAt, trackedBalance(c)) {
115			return false, "not_yet"
116		}
117		return true, "settle"
118	}
119	if f := pending(c); f.toBeneficiary > 0 || f.toOrganizer > 0 {
120		return true, "forward"
121	}
122	return false, statusSettled
123}