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

claims.gno

3.03 Kb · 113 lines
  1package arena
  2
  3import (
  4	"chain"
  5	"strings"
  6)
  7
  8// Winnings are not sent when a game settles. They are credited to each
  9// winner, stay in the escrow, and the winner takes them with Claim, every
 10// game at once. A settlement then moves no coin to a player, and a player
 11// who never comes back costs nobody anything.
 12//
 13// Refunds (Leave, CancelRound, RefundRound, ExpireLobby) still send the
 14// stakes back straight away: a refund is not a win.
 15
 16// claimCredit is what one game owes one player.
 17type claimCredit struct {
 18	id     string
 19	amount int64
 20}
 21
 22// The credits of a player are stored as one string, oldest first:
 23//
 24//	<arena id>:<amount>,<arena id>:<amount>
 25
 26func creditsOf(addr address) []claimCredit {
 27	v := claims.Get(addr.String())
 28	if v == nil {
 29		return nil
 30	}
 31	var out []claimCredit
 32	for _, entry := range strings.Split(v.(string), ",") {
 33		colon := indexFrom(entry, 0, ':')
 34		if colon == 0 || colon == len(entry) {
 35			panic(errCorruptClaims)
 36		}
 37		amount, ok := digits(entry[colon+1:])
 38		if !ok || amount <= 0 {
 39			panic(errCorruptClaims)
 40		}
 41		out = append(out, claimCredit{id: entry[:colon], amount: amount})
 42	}
 43	return out
 44}
 45
 46// credit owes amount to a player for the game of an arena. The coins are
 47// already in the realm: they stay in the escrow until claimed.
 48func credit(addr address, id string, amount int64) {
 49	if amount <= 0 {
 50		return
 51	}
 52	entry := id + ":" + itoa(amount)
 53	if v := claims.Get(addr.String()); v != nil {
 54		entry = v.(string) + "," + entry
 55	}
 56	claims.Set(addr.String(), entry)
 57	hold(amount)
 58}
 59
 60// Claim sends the caller everything the realm owes them from settled
 61// games, and returns the amount in ugnot. It panics when there is nothing
 62// to claim.
 63func Claim(cur realm) int64 {
 64	player := callerOf(0, cur)
 65	list := creditsOf(player)
 66	if len(list) == 0 {
 67		panic(errNothingToClaim)
 68	}
 69	total := int64(0)
 70	for _, c := range list {
 71		total += c.amount
 72	}
 73	// State first: nothing is owed any more before the coins leave.
 74	claims.Remove(player.String())
 75	release(total)
 76	pay(0, cur, player, total)
 77	assertSolvent(0, cur)
 78	chain.Emit("Claimed", "player", player.String(), "amount", itoa(total), "games", itoa(int64(len(list))))
 79	return total
 80}
 81
 82// Claimable returns what a player can claim, in ugnot.
 83func Claimable(addr address) int64 {
 84	total := int64(0)
 85	for _, c := range creditsOf(addr) {
 86		total += c.amount
 87	}
 88	return total
 89}
 90
 91// ClaimsJSON returns what a player can claim, game by game, oldest first:
 92//
 93//	{ "address": "g1...", "total": 19000000, "games": [ { "id": "...", "amount": 19000000 } ] }
 94//
 95// The output has no whitespace. An address with nothing to claim returns
 96// the same shape with a zero total and no game.
 97func ClaimsJSON(addr address) string {
 98	list := creditsOf(addr)
 99	var b strings.Builder
100	total := int64(0)
101	for _, c := range list {
102		total += c.amount
103	}
104	b.WriteString(`{"address":` + quote(addr.String()) + `,"total":` + itoa(total) + `,"games":[`)
105	for i, c := range list {
106		if i > 0 {
107			b.WriteByte(',')
108		}
109		b.WriteString(`{"id":"` + c.id + `","amount":` + itoa(c.amount) + `}`)
110	}
111	b.WriteString("]}")
112	return b.String()
113}