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	"strings"
  6	"time"
  7
  8	"gno.land/p/demo/tokens/grc20"
  9	ufmt "gno.land/p/nt/ufmt/v0"
 10
 11	"gno.land/r/demo/defi/grc20reg"
 12	"gno.land/r/gnoswap/access"
 13
 14	prabc "gno.land/p/gnoswap/rbac"
 15	_ "gno.land/r/gnoswap/rbac"
 16)
 17
 18const (
 19	MAXIMUM_SUPPLY      = int64(1_000_000_000_000_000)
 20	INITIAL_MINT_AMOUNT = int64(100_000_000_000_000)
 21	MAX_EMISSION_AMOUNT = int64(900_000_000_000_000) // MAXIMUM_SUPPLY - INITIAL_MINT_AMOUNT
 22)
 23
 24var (
 25	token         *grc20.Token
 26	privateLedger *grc20.PrivateLedger
 27	userTeller    grc20.Teller
 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(cur realm) {
 35	token, privateLedger = grc20.NewToken(0, cur, "Gnoswap", "GNS", 6)
 36	userTeller = token.CallerTeller()
 37
 38	adminAddr := access.MustGetAddress(prabc.ROLE_ADMIN.String())
 39	err := privateLedger.Mint(adminAddr, INITIAL_MINT_AMOUNT)
 40	if err != nil {
 41		panic(err.Error())
 42	}
 43
 44	grc20reg.Register(cross(cur), token, "")
 45
 46	// Initial amount set to 900_000_000_000_000 (MAXIMUM_SUPPLY - INITIAL_MINT_AMOUNT).
 47	// leftEmissionAmount will decrease as tokens are minted.
 48	setLeftEmissionAmount(MAX_EMISSION_AMOUNT)
 49	setMintedEmissionAmount(0)
 50	setLastMintedTimestamp(0)
 51}
 52
 53// Name returns the name of the GNS token.
 54func Name() string { return token.GetName() }
 55
 56// Symbol returns the symbol of the GNS token.
 57func Symbol() string { return token.GetSymbol() }
 58
 59// Decimals returns the number of decimal places for GNS token.
 60func Decimals() int { return token.GetDecimals() }
 61
 62// TotalSupply returns the total supply of GNS tokens in circulation.
 63func TotalSupply() int64 { return token.TotalSupply() }
 64
 65// KnownAccounts returns the number of addresses that have held GNS.
 66func KnownAccounts() int { return token.KnownAccounts() }
 67
 68// BalanceOf returns the GNS balance of a specific address.
 69func BalanceOf(owner address) int64 { return token.BalanceOf(owner) }
 70
 71// Allowance returns the amount of GNS that a spender is allowed to transfer from an owner.
 72func Allowance(owner, spender address) int64 { return token.Allowance(owner, spender) }
 73
 74// MintGns mints new GNS tokens according to the emission schedule.
 75//
 76// Parameters:
 77//   - address: recipient address for minted tokens
 78//
 79// Returns amount minted.
 80// Only callable by emission contract.
 81//
 82// Note: Halt check is performed by the caller (emission.MintAndDistributeGns)
 83// to allow graceful handling. This function assumes caller has already verified
 84// halt status before invoking.
 85func MintGns(cur realm, address address) int64 {
 86	previousRealm := cur.Previous()
 87	caller := previousRealm.Address()
 88	access.AssertIsEmission(caller)
 89
 90	lastGNSMintedTimestamp := LastMintedTimestamp()
 91	currentTime := time.Now().Unix()
 92
 93	// Skip if already minted this timestamp or emission ended.
 94	if lastGNSMintedTimestamp == currentTime || lastGNSMintedTimestamp >= GetEmissionEndTimestamp() {
 95		return 0
 96	}
 97
 98	amountToMint, err := calculateAmountToMint(getEmissionState(), lastGNSMintedTimestamp+1, currentTime)
 99	if err != nil {
100		panic(err)
101	}
102
103	err = validEmissionAmount(amountToMint)
104	if err != nil {
105		panic(err)
106	}
107
108	setLastMintedTimestamp(currentTime)
109	setMintedEmissionAmount(safeAddInt64(MintedEmissionAmount(), amountToMint))
110	setLeftEmissionAmount(safeSubInt64(LeftEmissionAmount(), amountToMint))
111
112	err = privateLedger.Mint(address, amountToMint)
113	if err != nil {
114		panic(err.Error())
115	}
116
117	chain.Emit(
118		"MintGNS",
119		"prevAddr", caller.String(),
120		"prevRealm", previousRealm.PkgPath(),
121		"mintedBlockTime", formatInt(currentTime),
122		"mintedGNSAmount", formatInt(amountToMint),
123		"accumMintedGNSAmount", formatInt(MintedEmissionAmount()),
124		"accumLeftMintGNSAmount", formatInt(LeftEmissionAmount()),
125	)
126
127	return amountToMint
128}
129
130// Transfer transfers GNS tokens from caller to recipient.
131//
132// Parameters:
133//   - to: recipient address
134//   - amount: amount to transfer
135func Transfer(cur realm, to address, amount int64) {
136	checkErr(userTeller.Transfer(0, cur, to, amount))
137}
138
139// Approve allows spender to transfer GNS tokens from caller's account.
140//
141// Parameters:
142//   - spender: address authorized to spend
143//   - amount: maximum amount spender can transfer
144func Approve(cur realm, spender address, amount int64) {
145	checkErr(userTeller.Approve(0, cur, spender, amount))
146}
147
148// TransferFrom transfers GNS tokens on behalf of owner.
149//
150// Parameters:
151//   - from: token owner address
152//   - to: recipient address
153//   - amount: amount to transfer
154func TransferFrom(cur realm, from, to address, amount int64) {
155	checkErr(userTeller.TransferFrom(0, cur, 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 }