Search Apps Documentation Source Content File Folder Download Copy Actions Download

gns.gno

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