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}