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

gns.gno

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