Kind Realm (renderable)
Name gns
Namespace gnoswap
Exported functions 43
Module gno.land/r/gnoswap/gns
gno 0.9 This realm implements a recognized token. GNS → gns.gno gno
⧉
1 package gns 2 3 import ( 4 "chain" 5 "strings" 6 "time" 7 8 Allowance (owner string, spender string) int64
Approve (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, spender string, amount int64)
BalanceOf (owner string) int64
CalculateMintGnsAmount (fromTimestamp int64, toTimestamp int64) int64
Decimals () int
GetAmountPerSecondPerHalvingYear (year int64) int64
GetCurrentYear () int64
GetEmissionAccumulatedAmountByTimestamp (timestamp int64) int64
GetEmissionAmountPerSecondByTimestamp (timestamp int64) int64
GetEmissionAmountPerSecondInRange (fromTime int64, toTime int64) ([]int64, []int64)
GetEmissionCreatedHeight () int64
GetEmissionEndTimestamp () int64
GetEmissionLeftAmountByTimestamp (timestamp int64) int64
GetEmissionStartTimestamp () int64
GetHalvingAmountsPerYear (year int64) int64
GetHalvingInfo () *gno.land/r/gnoswap/gns.HalvingData
GetHalvingYear (timestamp int64) int64
GetHalvingYearEndTimestamp (year int64) int64
GetHalvingYearInfo (timestamp int64) (int64, int64, int64)
GetHalvingYearLeftAmount (year int64) int64
GetHalvingYearMaxAmount (year int64) int64
GetHalvingYearMintedAmount (year int64) int64
GetHalvingYearStartTimestamp (year int64) int64
GetInitialMintAmount () int64
GetMaxEmissionAmount () int64
GetMaximumSupply () int64
InitEmissionState (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, createdHeight int64, startTimestamp int64)
IsEmissionActive () bool
IsEmissionEnded () bool
IsEmissionInitialized () bool
KnownAccounts () int
LastMintedTimestamp () int64
LeftEmissionAmount () int64
MintedEmissionAmount () int64
MintGns (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, address string) int64
Name () string
NewEmissionState (createdHeight int64, startTimestamp int64) *gno.land/r/gnoswap/gns.EmissionState
NewHalvingData (startTimestamp int64) *gno.land/r/gnoswap/gns.HalvingData
Render (path string) string
Symbol () string
TotalSupply () int64
Transfer (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, to string, amount int64)
TransferFrom (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, from string, to string, amount int64)
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.
"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
19 const (
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
26 var (
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
36 func 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
56 func Name() string { return token.GetName() }
57
58 // Symbol returns the symbol of the GNS token.
59 //
60 // Returns:
61 // - symbol: token symbol
62 func 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
68 func 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
74 func 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
80 func 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
89 func 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
99 func 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.
116 func 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
167 func 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
177 func 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
188 func 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
199 func 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.
216 func 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.
225 func 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
297 func LastMintedTimestamp() int64 { return lastMintedTimestamp }
298
299 // LeftEmissionAmount returns the remaining GNS tokens available for emission.
300 //
301 // Returns:
302 // - amount: remaining emission amount
303 func 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
310 func MintedEmissionAmount() int64 { return mintedEmissionAmount }
311
312 // setLastMintedTimestamp sets the timestamp of the last emission mint.
313 func setLastMintedTimestamp(timestamp int64 ) { lastMintedTimestamp = timestamp }
314
315 // setLeftEmissionAmount sets the remaining emission amount.
316 func setLeftEmissionAmount(amount int64 ) { leftEmissionAmount = amount }
317
318 // setMintedEmissionAmount sets the total minted emission amount.
319 func setMintedEmissionAmount(amount int64 ) { mintedEmissionAmount = amount }
320