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

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}