lock.gno
4.18 Kb · 167 lines
1package kourtv3
2import (
3 "strconv"
4 bptree "gno.land/p/nt/bptree/v0"
5)
6func lockedOf(c *Court, who address) int64 {
7 if c.locked == nil {
8 return 0
9 }
10 if v := c.locked.Get(string(who)); v != nil {
11 return v.(int64)
12 }
13 return 0
14}
15func spendable(c *Court, who address) int64 {
16 s := c.coin.BalanceOf(who) - lockedOf(c, who)
17 if s < 0 {
18 return 0
19 }
20 return s
21}
22func disposable(c *Court, who address) int64 {
23 claim := lockedOf(c, who)
24 if v := voteLockedOf(c, who); v > claim {
25 claim = v
26 }
27 d := c.coin.BalanceOf(who) - claim
28 if d < 0 {
29 return 0
30 }
31 return d
32}
33func DisposableOf(courtSlug string, who address) int64 {
34 return disposable(mustCourt(courtSlug), who)
35}
36func ccAmt(n int64) string {
37 if n < 0 {
38 return "-" + ccAmt(-n)
39 }
40 whole := strconv.FormatInt(n/1_000_000, 10)
41 frac := n % 1_000_000
42 if frac == 0 {
43 return whole
44 }
45 f := strconv.FormatInt(frac, 10)
46 for len(f) < 6 {
47 f = "0" + f
48 }
49 for len(f) > 1 && f[len(f)-1] == '0' {
50 f = f[:len(f)-1]
51 }
52 return whole + "." + f
53}
54func mustSpendable(c *Court, who address, amount int64) {
55 if disposable(c, who) >= amount {
56 return
57 }
58 vl, sl := voteLockedOf(c, who), lockedOf(c, who)
59 short := "need " + ccAmt(amount) + " CC, you have " + ccAmt(disposable(c, who))
60 switch {
61 case vl <= 0 && sl <= 0:
62 panic("kourtv3: not enough CC — " + short)
63 case vl > sl:
64 panic("kourtv3: not enough uncommitted CC — " + short +
65 " free; " + ccAmt(vl) + " is committed by voting")
66 case sl > vl:
67 panic("kourtv3: not enough unstaked CC — " + short +
68 " free; " + ccAmt(sl) + " is staked")
69 default:
70 panic("kourtv3: not enough free CC — " + short +
71 " free; " + ccAmt(sl) + " is both staked and committed by voting")
72 }
73}
74func mustStakable(c *Court, who address, amount int64) {
75 if s := spendable(c, who); s < amount {
76 tail := ""
77 if l := lockedOf(c, who); l > 0 {
78 tail = "; " + ccAmt(l) + " is staked"
79 }
80 panic("kourtv3: not enough unstaked CC — need " + ccAmt(amount) +
81 " CC, you have " + ccAmt(s) + tail)
82 }
83}
84func lockStake(c *Court, who address, amount int64) {
85 if amount <= 0 {
86 return
87 }
88 if c.locked == nil {
89 c.locked = bptree.NewBPTree32()
90 }
91 c.locked.Set(string(who), mustAdd(lockedOf(c, who), amount))
92}
93func releaseStake(c *Court, who address, amount int64) {
94 if amount <= 0 {
95 return
96 }
97 l := lockedOf(c, who)
98 if amount > l {
99 panic("kourtv3: releasing more stake than is locked")
100 }
101 if amount == l {
102 c.locked.Remove(string(who))
103 return
104 }
105 c.locked.Set(string(who), l-amount)
106}
107func LockedOf(courtSlug string, who address) int64 {
108 return lockedOf(mustCourt(courtSlug), who)
109}
110func SpendableOf(courtSlug string, who address) int64 {
111 return spendable(mustCourt(courtSlug), who)
112}
113func TransferCC(cur realm, courtSlug string, to address, amount int64) {
114 if !cur.IsCurrent() {
115 panic(errStaleRealm)
116 }
117 if amount <= 0 {
118 panic("kourtv3: transfer amount must be positive")
119 }
120 from := cur.Previous().Address()
121 if from == to {
122 panic("kourtv3: cannot transfer to yourself")
123 }
124 c := mustCourt(courtSlug)
125 if to == c.escrow || from == c.escrow {
126 panic("kourtv3: the court escrow is not a transfer endpoint")
127 }
128 touch(c)
129 mustSpendable(c, from, amount)
130 c.coin.Transfer(from, to, amount)
131}
132func ApproveCC(cur realm, courtSlug string, spender address, amount int64) {
133 if !cur.IsCurrent() {
134 panic(errStaleRealm)
135 }
136 if amount < 0 {
137 panic("kourtv3: negative allowance")
138 }
139 c := mustCourt(courtSlug)
140 owner := cur.Previous().Address()
141 if spender == c.escrow || owner == c.escrow {
142 panic("kourtv3: the court escrow is not a transfer endpoint")
143 }
144 touch(c)
145 c.coin.Approve(owner, spender, amount)
146}
147func TransferFromCC(cur realm, courtSlug string, from, to address, amount int64) {
148 if !cur.IsCurrent() {
149 panic(errStaleRealm)
150 }
151 if amount <= 0 {
152 panic("kourtv3: transfer amount must be positive")
153 }
154 if from == to {
155 panic("kourtv3: cannot transfer to yourself")
156 }
157 c := mustCourt(courtSlug)
158 if to == c.escrow || from == c.escrow {
159 panic("kourtv3: the court escrow is not a transfer endpoint")
160 }
161 touch(c)
162 mustSpendable(c, from, amount)
163 c.coin.TransferFrom(cur.Previous().Address(), from, to, amount)
164}
165func AllowanceCC(courtSlug string, owner, spender address) int64 {
166 return mustCourt(courtSlug).coin.Allowance(owner, spender)
167}