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.

bond.gno

6.52 Kb · 249 lines
  1// Package bond is the create-bond policy for gnomemepad — separate from pad
  2// so promo schedules can change without redeploying the pad realm.
  3//
  4// Pad calls CurrentBondUgnot() at Create time. This package owns:
  5//   - normal bond (default 2 GNOT)
  6//   - promo bond (default ~$20 in ugnot at a fixed GNOT rate, admin-adjustable)
  7//   - 10-day window OR admin EndPromo → back to normal
  8//
  9// USD is not on-chain: promo amount is ugnot. Admin sets PromoBondUgnot to
 10// approximate $20 (e.g. at $1/GNOT → 20e6 ugnot; at $0.5/GNOT → 40e6).
 11package bond
 12
 13import (
 14	"chain"
 15	"strconv"
 16	"time"
 17)
 18
 19const (
 20	DenomUgnot = "ugnot"
 21
 22	// Defaults (ugnot, 1 GNOT = 1e6 ugnot)
 23	// Pearl / current policy: 100 GNOT create bond (admin can SetNormalBond).
 24	DefaultNormalBondUgnot int64 = 100_000_000 // 100 GNOT
 25	DefaultPromoBondUgnot  int64 = 100_000_000 // same unless StartPromo changes it
 26	DefaultPromoDuration   int64 = 10 * 24 * 60 * 60 // 10 days (seconds)
 27
 28	// Status for UI
 29	StatusNormal = 0
 30	StatusPromo  = 1
 31)
 32
 33var (
 34	inited bool
 35	admin  address
 36
 37	normalBondUgnot int64
 38	promoBondUgnot  int64
 39	// promo: active while time.Now().Unix() < promoEndsAt and !promoEnded
 40	promoEndsAt int64
 41	promoEnded  bool // admin force-end (or auto after window)
 42	promoStartedAt int64
 43)
 44
 45// Init sets admin (deploy EOA) and default bond amounts. Call once.
 46func Init(cur realm) {
 47	if inited {
 48		panic("bond: already initialized")
 49	}
 50	if !cur.Previous().IsUserCall() {
 51		panic("bond: EOA only")
 52	}
 53	admin = cur.Previous().Address()
 54	normalBondUgnot = DefaultNormalBondUgnot
 55	promoBondUgnot = DefaultPromoBondUgnot
 56	promoEndsAt = 0
 57	promoEnded = true
 58	promoStartedAt = 0
 59	inited = true
 60	chain.Emit("Init", "admin", admin.String())
 61}
 62
 63func requireInit() {
 64	if !inited {
 65		panic("bond: call Init first")
 66	}
 67}
 68
 69func requireAdmin(cur realm) {
 70	requireInit()
 71	if !cur.Previous().IsUserCall() {
 72		panic("bond: EOA only")
 73	}
 74	if cur.Previous().Address() != admin {
 75		panic("bond: not admin")
 76	}
 77}
 78
 79// Admin returns the admin (deploy) address.
 80func Admin() string {
 81	requireInit()
 82	return admin.String()
 83}
 84
 85// TransferAdmin rotates admin (current admin only).
 86func TransferAdmin(cur realm, newAdmin address) {
 87	requireAdmin(cur)
 88	if !newAdmin.IsValid() {
 89		panic("bond: invalid address")
 90	}
 91	old := admin
 92	admin = newAdmin
 93	chain.Emit("TransferAdmin", "from", old.String(), "to", newAdmin.String())
 94}
 95
 96// IsPromoActive reports whether the promo window is currently in force.
 97func IsPromoActive() bool {
 98	if !inited || promoEnded || promoEndsAt <= 0 {
 99		return false
100	}
101	now := time.Now().Unix()
102	if now >= promoEndsAt {
103		return false
104	}
105	return true
106}
107
108// CurrentBondUgnot is the create bond pad must require (cross-safe read).
109// Pad: if sent < CurrentBondUgnot() → panic underpaid.
110func CurrentBondUgnot() int64 {
111	requireInit()
112	if IsPromoActive() {
113		return promoBondUgnot
114	}
115	return normalBondUgnot
116}
117
118// NormalBondUgnot returns the post-promo / default bond.
119func NormalBondUgnot() int64 {
120	requireInit()
121	return normalBondUgnot
122}
123
124// PromoBondUgnot returns the promo-period bond amount (may be inactive).
125func PromoBondUgnot() int64 {
126	requireInit()
127	return promoBondUgnot
128}
129
130// PromoEndsAt returns unix end of promo (0 if never started).
131func PromoEndsAt() int64 {
132	requireInit()
133	return promoEndsAt
134}
135
136// PromoSecondsLeft returns remaining promo seconds (0 if inactive).
137func PromoSecondsLeft() int64 {
138	if !IsPromoActive() {
139		return 0
140	}
141	left := promoEndsAt - time.Now().Unix()
142	if left < 0 {
143		return 0
144	}
145	return left
146}
147
148// Status: 0=normal 1=promo active.
149func Status() int {
150	if IsPromoActive() {
151		return StatusPromo
152	}
153	return StatusNormal
154}
155
156// BondInfo: status|currentUgnot|normalUgnot|promoUgnot|endsAt|secondsLeft|admin
157func BondInfo() string {
158	requireInit()
159	st := Status()
160	return strconv.Itoa(st) + "|" +
161		strconv.FormatInt(CurrentBondUgnot(), 10) + "|" +
162		strconv.FormatInt(normalBondUgnot, 10) + "|" +
163		strconv.FormatInt(promoBondUgnot, 10) + "|" +
164		strconv.FormatInt(promoEndsAt, 10) + "|" +
165		strconv.FormatInt(PromoSecondsLeft(), 10) + "|" +
166		admin.String()
167}
168
169// StartPromo begins a promo window from now.
170// promoUgnot: bond during promo (e.g. 20e6 ≈ $20 at $1/GNOT). 0 → keep current promoBondUgnot.
171// durationSec: window length. 0 → DefaultPromoDuration (10 days).
172func StartPromo(cur realm, promoUgnot, durationSec int64) {
173	requireAdmin(cur)
174	if promoUgnot < 0 {
175		panic("bond: promoUgnot must be non-negative")
176	}
177	if promoUgnot > 0 {
178		promoBondUgnot = promoUgnot
179	}
180	if promoBondUgnot <= 0 {
181		panic("bond: set promo bond amount first")
182	}
183	if durationSec <= 0 {
184		durationSec = DefaultPromoDuration
185	}
186	now := time.Now().Unix()
187	promoStartedAt = now
188	promoEndsAt = now + durationSec
189	promoEnded = false
190	chain.Emit("StartPromo",
191		"promoUgnot", strconv.FormatInt(promoBondUgnot, 10),
192		"endsAt", strconv.FormatInt(promoEndsAt, 10),
193		"durationSec", strconv.FormatInt(durationSec, 10),
194	)
195}
196
197// EndPromo forces normal bond immediately (deploy admin).
198func EndPromo(cur realm) {
199	requireAdmin(cur)
200	promoEnded = true
201	// keep promoEndsAt for history
202	chain.Emit("EndPromo",
203		"at", strconv.FormatInt(time.Now().Unix(), 10),
204		"normalUgnot", strconv.FormatInt(normalBondUgnot, 10),
205	)
206}
207
208// SetNormalBond updates the post-promo bond (default 2 GNOT).
209func SetNormalBond(cur realm, ugnot int64) {
210	requireAdmin(cur)
211	if ugnot <= 0 {
212		panic("bond: normal bond must be positive")
213	}
214	normalBondUgnot = ugnot
215	chain.Emit("SetNormalBond", "ugnot", strconv.FormatInt(ugnot, 10))
216}
217
218// SetPromoBond updates promo amount without restarting the clock.
219// Use to re-target ~$20 when GNOT price moves during the window.
220func SetPromoBond(cur realm, ugnot int64) {
221	requireAdmin(cur)
222	if ugnot <= 0 {
223		panic("bond: promo bond must be positive")
224	}
225	promoBondUgnot = ugnot
226	chain.Emit("SetPromoBond", "ugnot", strconv.FormatInt(ugnot, 10))
227}
228
229// ExtendPromo adds seconds to the current end (or starts from now if inactive).
230func ExtendPromo(cur realm, extraSec int64) {
231	requireAdmin(cur)
232	if extraSec <= 0 {
233		panic("bond: extraSec must be positive")
234	}
235	now := time.Now().Unix()
236	if !IsPromoActive() {
237		// restart promo with current promoBondUgnot
238		promoStartedAt = now
239		promoEndsAt = now + extraSec
240		promoEnded = false
241	} else {
242		promoEndsAt += extraSec
243		promoEnded = false
244	}
245	chain.Emit("ExtendPromo",
246		"endsAt", strconv.FormatInt(promoEndsAt, 10),
247		"extraSec", strconv.FormatInt(extraSec, 10),
248	)
249}