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

emission.gno

4.48 Kb · 185 lines
  1package kourt
  2import (
  3	"chain"
  4	"strconv"
  5)
  6const (
  7	seniorEnqueuedEvent = "SeniorEnqueued"
  8	seniorPaidEvent     = "SeniorPaid"
  9)
 10type entitlement struct {
 11	to      address
 12	amount  int64
 13	start   int64
 14	paid    int64
 15	purpose string
 16}
 17func touch(c *Court) {
 18	now := heightNow()
 19	for c.lastAccrual < now {
 20		boundary := c.createdAt + (c.periodIndex+1)*periodBlocks
 21		segEnd := now
 22		if boundary < segEnd {
 23			segEnd = boundary
 24		}
 25		accrueSegment(c, segEnd-c.lastAccrual)
 26		advanceRateAcc(c, segEnd-c.lastAccrual)
 27		c.lastAccrual = segEnd
 28		if segEnd == boundary {
 29			rollPeriod(c)
 30		}
 31	}
 32}
 33func accrueSegment(c *Court, blocks int64) {
 34	if blocks <= 0 || c.curPeriodBudget <= 0 {
 35		return
 36	}
 37	if c.reservoirR() >= c.rMax() && c.cumAccrual >= c.reservedTail {
 38		return
 39	}
 40	num := mustAdd(mustMul(c.curPeriodBudget, blocks), c.accrualRem)
 41	c.cumAccrual = mustAdd(c.cumAccrual, num/periodBlocks)
 42	c.accrualRem = num % periodBlocks
 43}
 44func advanceRateAcc(c *Court, blocks int64) {
 45	if blocks <= 0 {
 46		return
 47	}
 48	c.rateAccFP = mustAdd(c.rateAccFP, mustMul(rateBpsFP(c), blocks))
 49}
 50func rollPeriod(c *Court) {
 51	c.periodIndex++
 52	c.curBudgetBpsFP = c.curBudgetBpsFP * stepDownNum / stepDownDen
 53	minted := c.emittedTotal - c.emittedAtRoll
 54	c.emittedAtRoll = c.emittedTotal
 55	c.emaMinted = (3*c.emaMinted + minted) / 4
 56	s := c.coin.TotalSupply()
 57	if s > 0 {
 58		if c.emaMinted < 0 {
 59			panic("kourtv2: negative realized mint")
 60		}
 61		dReal := mulDiv128(c.emaMinted, 10_000*1_000_000, s)
 62		if dReal < c.curBudgetBpsFP {
 63			c.dEffBpsFP = dReal
 64		} else {
 65			c.dEffBpsFP = c.curBudgetBpsFP
 66		}
 67		if c.absBudget > 0 {
 68			c.absBudget = c.absBudget * stepDownNum / stepDownDen
 69			c.curPeriodBudget = c.absBudget
 70		} else {
 71			c.curPeriodBudget = mulDiv128(s, c.curBudgetBpsFP, 10_000*1_000_000)
 72		}
 73	} else {
 74		c.dEffBpsFP = 0
 75		c.curPeriodBudget = 0
 76	}
 77}
 78func (c *Court) reservoirR() int64 {
 79	past := c.cumAccrual - c.reservedTail
 80	if past < 0 {
 81		past = 0
 82	}
 83	r := past - c.juniorReserved
 84	if r < 0 {
 85		r = 0
 86	}
 87	return r
 88}
 89func (c *Court) rMax() int64 { return rMaxPeriods * c.curPeriodBudget }
 90func enqueueSenior(c *Court, to address, amount int64, purpose string) uint64 {
 91	if amount <= 0 {
 92		panic("kourtv2: a senior entitlement must be positive")
 93	}
 94	start := mustAdd(c.reservedTail, c.juniorReserved)
 95	e := &entitlement{to: to, amount: amount, start: start, purpose: purpose}
 96	c.reservedTail = mustAdd(c.reservedTail, amount)
 97	c.seniorOwed = mustAdd(c.seniorOwed, amount)
 98	c.queueSeq++
 99	c.queue.Set(beClaimKey(c.queueSeq), e)
100	chain.Emit(seniorEnqueuedEvent,
101		"court", c.id,
102		"seq", strconv.FormatUint(c.queueSeq, 10),
103		"to", to.String(),
104		"amount", strconv.FormatInt(amount, 10),
105		"purpose", purpose,
106	)
107	return c.queueSeq
108}
109func reserveJunior(c *Court, want int64) int64 {
110	touch(c)
111	r := c.reservoirR()
112	if want > r {
113		want = r
114	}
115	if want < 0 {
116		want = 0
117	}
118	c.juniorReserved = mustAdd(c.juniorReserved, want)
119	return want
120}
121func mintEmission(c *Court, to address, amount int64) {
122	if amount <= 0 {
123		return
124	}
125	c.coin.Mint(to, amount)
126	c.emittedTotal = mustAdd(c.emittedTotal, amount)
127}
128func PullSenior(cur realm, courtSlug string, seq uint64) int64 {
129	if !cur.IsCurrent() {
130		panic(errStaleRealm)
131	}
132	c := mustCourt(courtSlug)
133	touch(c)
134	v := c.queue.Get(beClaimKey(seq))
135	if v == nil {
136		panic("kourtv2: no such entitlement")
137	}
138	e := v.(*entitlement)
139	covered := c.cumAccrual - e.start
140	if covered > e.amount {
141		covered = e.amount
142	}
143	pay := covered - e.paid
144	if pay <= 0 {
145		return 0
146	}
147	e.paid += pay
148	c.seniorOwed -= pay
149	c.seniorPaid = mustAdd(c.seniorPaid, pay)
150	mintEmission(c, e.to, pay)
151	chain.Emit(seniorPaidEvent,
152		"court", c.id,
153		"seq", strconv.FormatUint(seq, 10),
154		"to", e.to.String(),
155		"paid", strconv.FormatInt(pay, 10),
156		"remaining", strconv.FormatInt(e.amount-e.paid, 10),
157	)
158	return pay
159}
160func Reservoir(courtSlug string) int64  { return mustCourt(courtSlug).reservoirR() }
161func SeniorOwed(courtSlug string) int64 { return mustCourt(courtSlug).seniorOwed }
162func SeniorEntitlement(courtSlug string, seq uint64) (to address, amount, paid, payable int64) {
163	c := mustCourt(courtSlug)
164	v := c.queue.Get(beClaimKey(seq))
165	if v == nil {
166		panic("kourtv2: no such entitlement")
167	}
168	e := v.(*entitlement)
169	covered := c.cumAccrual - e.start
170	if covered > e.amount {
171		covered = e.amount
172	}
173	p := covered - e.paid
174	if p < 0 {
175		p = 0
176	}
177	return e.to, e.amount, e.paid, p
178}
179func QueueLen(courtSlug string) uint64 { return mustCourt(courtSlug).queueSeq }
180func Poke(cur realm, courtSlug string) {
181	if !cur.IsCurrent() {
182		panic(errStaleRealm)
183	}
184	touch(mustCourt(courtSlug))
185}