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

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

emission.gno

4.49 Kb · 185 lines
  1package kourtv3
  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("kourtv3: 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("kourtv3: 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("kourtv3: 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("kourtv3: 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}