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

gns.gno

9.10 Kb · 319 lines
  1package gns
  2
  3import (
  4	"chain"
  5	"strings"
  6	"time"
  7
  8	"gno.land/p/nt/grc20/v0"
  9	"gno.land/p/nt/ufmt/v0"
 10
 11	"gno.land/r/gnoswap/access/v1"
 12	"gno.land/r/nt/grc20reg/v0"
 13
 14	"gno.land/p/gnoswap/gnsmath/v1"
 15	"gno.land/p/gnoswap/utils/v1"
 16	_ "gno.land/r/gnoswap/rbac/v1"
 17)
 18
 19const (
 20	tokenID             = 0
 21	MAXIMUM_SUPPLY      = int64(1_000_000_000_000_000)
 22	INITIAL_MINT_AMOUNT = int64(100_000_000_000_000)
 23	MAX_EMISSION_AMOUNT = int64(900_000_000_000_000) // MAXIMUM_SUPPLY - INITIAL_MINT_AMOUNT
 24)
 25
 26var (
 27	token         *grc20.Token
 28	privateLedger *grc20.PrivateLedger
 29	userTeller    grc20.Teller
 30
 31	leftEmissionAmount   int64 // amount of GNS can be minted for emission
 32	mintedEmissionAmount int64 // amount of GNS that has been minted for emission
 33	lastMintedTimestamp  int64 // last block time that gns was minted for emission
 34)
 35
 36func init(cur realm) {
 37	token, privateLedger = grc20.NewToken("Gnoswap", "GNS", 6, tokenID, cur)
 38	userTeller = privateLedger.CallerTeller()
 39
 40	grc20reg.Register(cross(cur), token, "")
 41
 42	// Initial amount set to 900_000_000_000_000 (MAXIMUM_SUPPLY - INITIAL_MINT_AMOUNT).
 43	// leftEmissionAmount will decrease as tokens are minted.
 44	setLeftEmissionAmount(MAX_EMISSION_AMOUNT)
 45	setMintedEmissionAmount(0)
 46	setLastMintedTimestamp(0)
 47
 48	// Pre-mint the initial GNS supply.
 49	setupPreMint(cur)
 50}
 51
 52// Name returns the name of the GNS token.
 53//
 54// Returns:
 55//   - name: token name
 56func Name() string { return token.GetName() }
 57
 58// Symbol returns the symbol of the GNS token.
 59//
 60// Returns:
 61//   - symbol: token symbol
 62func Symbol() string { return token.GetSymbol() }
 63
 64// Decimals returns the number of decimal places for GNS token.
 65//
 66// Returns:
 67//   - decimals: number of decimal places
 68func Decimals() int { return token.GetDecimals() }
 69
 70// TotalSupply returns the total supply of GNS tokens in circulation.
 71//
 72// Returns:
 73//   - supply: total token supply
 74func TotalSupply() int64 { return token.TotalSupply() }
 75
 76// KnownAccounts returns the number of addresses that have held GNS.
 77//
 78// Returns:
 79//   - count: number of known accounts
 80func KnownAccounts() int { return token.KnownAccounts() }
 81
 82// BalanceOf returns the GNS balance of a specific address.
 83//
 84// Parameters:
 85//   - owner: address to check balance for
 86//
 87// Returns:
 88//   - balance: token balance
 89func BalanceOf(owner address) int64 { return token.BalanceOf(owner) }
 90
 91// Allowance returns the amount of GNS that a spender is allowed to transfer from an owner.
 92//
 93// Parameters:
 94//   - owner: token owner address
 95//   - spender: spender address
 96//
 97// Returns:
 98//   - allowance: approved amount
 99func Allowance(owner, spender address) int64 { return token.Allowance(owner, spender) }
100
101// MintGns mints new GNS tokens according to the emission schedule.
102//
103// Parameters:
104//   - cur: Current realm context; callers use cross(cur) when crossing into this realm.
105//   - address: recipient address for minted tokens
106//
107// Returns:
108//   - amount: GNS minted for the current timestamp; zero when emission is
109//     already processed for that timestamp or the schedule has ended
110//
111// Only callable by emission contract.
112//
113// Note: Halt check is performed by the caller (emission.MintAndDistributeGns)
114// to allow graceful handling. This function assumes caller has already verified
115// halt status before invoking.
116func MintGns(cur realm, address address) int64 {
117	previousRealm := cur.Previous()
118	caller := previousRealm.Address()
119	access.AssertIsEmission(caller)
120
121	lastGNSMintedTimestamp := LastMintedTimestamp()
122	currentTime := time.Now().Unix()
123
124	// Skip if already minted this timestamp or emission ended.
125	if lastGNSMintedTimestamp == currentTime || lastGNSMintedTimestamp >= GetEmissionEndTimestamp() {
126		return 0
127	}
128
129	amountToMint, err := calculateAmountToMint(getEmissionState(), lastGNSMintedTimestamp+1, currentTime)
130	if err != nil {
131		panic(err)
132	}
133
134	err = validEmissionAmount(amountToMint)
135	if err != nil {
136		panic(err)
137	}
138
139	setLastMintedTimestamp(currentTime)
140	setMintedEmissionAmount(gnsmath.SafeAddInt64(MintedEmissionAmount(), amountToMint))
141	setLeftEmissionAmount(gnsmath.SafeSubInt64(LeftEmissionAmount(), amountToMint))
142
143	err = privateLedger.Mint(address, amountToMint)
144	if err != nil {
145		panic(err.Error())
146	}
147
148	chain.Emit(
149		"MintGNS",
150		"prevAddr", caller.String(),
151		"prevRealm", previousRealm.PkgPath(),
152		"mintedBlockTime", utils.FormatInt(currentTime),
153		"mintedGNSAmount", utils.FormatInt(amountToMint),
154		"accumMintedGNSAmount", utils.FormatInt(MintedEmissionAmount()),
155		"accumLeftMintGNSAmount", utils.FormatInt(LeftEmissionAmount()),
156	)
157
158	return amountToMint
159}
160
161// Transfer transfers GNS tokens from caller to recipient.
162//
163// Parameters:
164//   - cur: Current realm context; callers use cross(cur) when crossing into this realm.
165//   - to: recipient address
166//   - amount: number of GNS base units to transfer
167func Transfer(cur realm, to address, amount int64) {
168	checkErr(userTeller.Transfer(0, cur, to, amount))
169}
170
171// Approve allows spender to transfer GNS tokens from caller's account.
172//
173// Parameters:
174//   - cur: Current realm context; callers use cross(cur) when crossing into this realm.
175//   - spender: address authorized to spend
176//   - amount: maximum number of GNS base units spender can transfer
177func Approve(cur realm, spender address, amount int64) {
178	checkErr(userTeller.Approve(0, cur, spender, amount))
179}
180
181// TransferFrom transfers GNS tokens on behalf of owner.
182//
183// Parameters:
184//   - cur: Current realm context; callers use cross(cur) when crossing into this realm.
185//   - from: token owner address
186//   - to: recipient address
187//   - amount: number of GNS base units to transfer
188func TransferFrom(cur realm, from, to address, amount int64) {
189	checkErr(userTeller.TransferFrom(0, cur, from, to, amount))
190}
191
192// Render returns token information for web interface.
193//
194// Parameters:
195//   - path: render path for specific views
196//
197// Returns:
198//   - output: formatted token information
199func Render(path string) string {
200	parts := strings.Split(path, "/")
201	c := len(parts)
202
203	switch {
204	case path == "":
205		return token.RenderHome()
206	case c == 2 && parts[0] == "balance":
207		owner := address(parts[1])
208		balance := token.BalanceOf(owner)
209		return ufmt.Sprintf("%d\n", balance)
210	default:
211		return "404\n"
212	}
213}
214
215// checkErr panics if error is not nil.
216func checkErr(err error) {
217	if err != nil {
218		panic(err.Error())
219	}
220}
221
222// calculateAmountToMint calculates and allocates GNS tokens to mint for given timestamp range.
223// This function has side effects: it updates the accumulated and remaining amounts
224// for each halving year in the emission state.
225func calculateAmountToMint(state *EmissionState, fromTimestamp, toTimestamp int64) (int64, error) {
226	// Cache state to avoid repeated lookups
227	endTimestamp := state.getEndTimestamp()
228	if toTimestamp > endTimestamp {
229		toTimestamp = endTimestamp
230	}
231
232	if fromTimestamp > toTimestamp {
233		return 0, nil
234	}
235
236	startTimestamp := state.getStartTimestamp()
237	if fromTimestamp < startTimestamp {
238		fromTimestamp = startTimestamp
239	}
240
241	if toTimestamp < startTimestamp {
242		return 0, nil
243	}
244
245	fromYear := state.getCurrentYear(fromTimestamp)
246	toYear := state.getCurrentYear(toTimestamp)
247
248	if fromYear == 0 || toYear == 0 {
249		return 0, nil
250	}
251
252	totalAmountToMint := int64(0)
253
254	for year := fromYear; year <= toYear; year++ {
255		yearEndTimestamp := state.getHalvingYearEndTimestamp(year)
256		currentToTimestamp := i64Min(toTimestamp, yearEndTimestamp)
257
258		seconds := currentToTimestamp - fromTimestamp + 1
259		if seconds <= 0 {
260			break
261		}
262
263		amountPerSecond := state.getHalvingYearAmountPerSecond(year)
264		yearAmountToMint := gnsmath.SafeMulInt64(amountPerSecond, seconds)
265
266		if currentToTimestamp >= yearEndTimestamp {
267			leftover := gnsmath.SafeSubInt64(state.getHalvingYearLeftAmount(year), yearAmountToMint)
268			yearAmountToMint = gnsmath.SafeAddInt64(yearAmountToMint, leftover)
269		}
270
271		totalAmountToMint = gnsmath.SafeAddInt64(totalAmountToMint, yearAmountToMint)
272
273		err := state.addHalvingYearMintedAmount(year, yearAmountToMint)
274		if err != nil {
275			return 0, err
276		}
277
278		err = state.subHalvingYearLeftAmount(year, yearAmountToMint)
279		if err != nil {
280			return 0, err
281		}
282
283		fromTimestamp = currentToTimestamp + 1
284
285		if fromTimestamp > toTimestamp {
286			break
287		}
288	}
289
290	return totalAmountToMint, nil
291}
292
293// LastMintedTimestamp returns the timestamp of the last GNS emission mint.
294//
295// Returns:
296//   - timestamp: last minted timestamp
297func LastMintedTimestamp() int64 { return lastMintedTimestamp }
298
299// LeftEmissionAmount returns the remaining GNS tokens available for emission.
300//
301// Returns:
302//   - amount: remaining emission amount
303func LeftEmissionAmount() int64 { return leftEmissionAmount }
304
305// MintedEmissionAmount returns the total GNS tokens minted through emission,
306// excluding the initial mint amount.
307//
308// Returns:
309//   - amount: total minted emission amount
310func MintedEmissionAmount() int64 { return mintedEmissionAmount }
311
312// setLastMintedTimestamp sets the timestamp of the last emission mint.
313func setLastMintedTimestamp(timestamp int64) { lastMintedTimestamp = timestamp }
314
315// setLeftEmissionAmount sets the remaining emission amount.
316func setLeftEmissionAmount(amount int64) { leftEmissionAmount = amount }
317
318// setMintedEmissionAmount sets the total minted emission amount.
319func setMintedEmissionAmount(amount int64) { mintedEmissionAmount = amount }