Skip to content

Commit a055c68

Browse files
r3v4s0xTopaz
andauthored
GSW-1845 refactor: gns (halving related) (#439)
* GSW-1845 refactor: use array if possible rather than avl * GSW-1845 feat: skip minting gns block - if emission is ended * GSW-1845 refactor: if block range for emission includes emission end height, mint until end height * GSW-1845 refactor: assert too many emission amount * GSW-1845 refactor: calculate height for next halving when avg time changes * fix: default interval time to 2 sec --------- Co-authored-by: 0xTopaz <[email protected]>
1 parent 3433e61 commit a055c68

14 files changed

+764
-519
lines changed

_deploy/r/gnoswap/consts/consts.gno

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const (
1212

1313
TOKEN_REGISTER_NAMESPACE string = "gno.land/r/g1er355fkjksqpdtwmhf5penwa82p0rhqxkkyhk5"
1414

15-
BLOCK_GENERATION_INTERVAL int64 = 1 // seconds
15+
BLOCK_GENERATION_INTERVAL int64 = 2 // seconds
1616
)
1717

1818
// WRAP & UNWRAP
Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,43 @@
11
package gns
22

33
import (
4+
"std"
45
"testing"
6+
"time"
57

68
"gno.land/p/demo/grc/grc20"
79
"gno.land/p/demo/ownable"
810

911
"gno.land/r/gnoswap/v1/consts"
1012
)
1113

12-
func testResetGnsTokenObject(t *testing.T) {
14+
func resetObject(t *testing.T) {
15+
t.Helper()
16+
17+
resetGnsTokenObject(t)
18+
resetHalvingRelatedObject(t)
19+
20+
height := std.GetHeight()
21+
lastMintedHeight = height
22+
startHeight = height
23+
startTimestamp = time.Now().Unix()
24+
}
25+
26+
func resetGnsTokenObject(t *testing.T) {
1327
t.Helper()
1428

1529
Token, privateLedger = grc20.NewToken("Gnoswap", "GNS", 6)
1630
UserTeller = Token.CallerTeller()
1731
owner = ownable.NewWithAddress(consts.ADMIN)
18-
1932
privateLedger.Mint(owner.Owner(), INITIAL_MINT_AMOUNT)
2033
}
34+
35+
func resetHalvingRelatedObject(t *testing.T) {
36+
t.Helper()
37+
38+
startHeight = std.GetHeight()
39+
startTimestamp = time.Now().Unix()
40+
41+
initializeHalvingData()
42+
setEndTimestamp(startTimestamp + consts.TIMESTAMP_YEAR*HALVING_END_YEAR)
43+
}

_deploy/r/gnoswap/gns/errors.gno

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import (
77
)
88

99
var (
10-
errNoPermission = errors.New("[GNOSWAP-GNS-001] caller has no permission")
10+
errNoPermission = errors.New("[GNOSWAP-GNS-001] caller has no permission")
11+
errTooManyEmission = errors.New("[GNOSWAP-GNS-002] too many emission reward")
1112
)
1213

1314
func addDetailToError(err error, detail string) string {

_deploy/r/gnoswap/gns/gns.gno

Lines changed: 69 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -22,65 +22,73 @@ const (
2222
)
2323

2424
var (
25-
lastMintedHeight = std.GetHeight()
26-
)
25+
owner *ownable.Ownable
2726

28-
var (
29-
// Initial amount set to 900_000_000_000_000 (MAXIMUM_SUPPLY - INITIAL_MINT_AMOUNT).
30-
// leftEmissionAmount will decrease as tokens are minted.
31-
leftEmissionAmount = MAX_EMISSION_AMOUNT
32-
mintedEmissionAmount = uint64(0)
33-
)
27+
Token *grc20.Token
28+
privateLedger *grc20.PrivateLedger
29+
UserTeller grc20.Teller
3430

35-
var (
36-
Token, privateLedger = grc20.NewToken("Gnoswap", "GNS", 6)
37-
UserTeller = Token.CallerTeller()
38-
owner = ownable.NewWithAddress(consts.ADMIN)
31+
leftEmissionAmount uint64
32+
mintedEmissionAmount uint64
33+
lastMintedHeight int64
34+
35+
burnAmount uint64
3936
)
4037

4138
func init() {
39+
owner = ownable.NewWithAddress(consts.ADMIN)
40+
41+
Token, privateLedger = grc20.NewToken("Gnoswap", "GNS", 6)
42+
UserTeller = Token.CallerTeller()
43+
4244
privateLedger.Mint(owner.Owner(), INITIAL_MINT_AMOUNT)
4345
getter := func() *grc20.Token { return Token }
4446
grc20reg.Register(getter, "")
45-
}
4647

47-
// MintedEmissionAmount returns the amount of GNS that has been minted by the emission contract.
48-
// It does not include initial minted amount.
49-
func MintedEmissionAmount() uint64 {
50-
return TotalSupply() - INITIAL_MINT_AMOUNT
48+
// Initial amount set to 900_000_000_000_000 (MAXIMUM_SUPPLY - INITIAL_MINT_AMOUNT).
49+
// leftEmissionAmount will decrease as tokens are minted.
50+
leftEmissionAmount = MAX_EMISSION_AMOUNT
51+
52+
lastMintedHeight = std.GetHeight()
5153
}
5254

5355
func MintGns(address pusers.AddressOrName) uint64 {
5456
lastMintedHeight := GetLastMintedHeight()
5557
currentHeight := std.GetHeight()
5658

57-
// skip minting process if gns for current block is already minted
58-
if skipIfSameHeight(lastMintedHeight, currentHeight) {
59+
// skip minting process if following conditions are met
60+
// - if gns for current block is already minted
61+
// - if last minted height is same or later than emission end height
62+
if lastMintedHeight == currentHeight || lastMintedHeight >= GetEndHeight() {
5963
return 0
6064
}
6165

6266
assertShouldNotBeHalted()
6367
assertCallerIsEmission()
6468

65-
// calculate gns amount to mint, and the mint to the target address
69+
// calculate gns amount to mint
6670
amountToMint := calculateAmountToMint(lastMintedHeight+1, currentHeight)
67-
err := privateLedger.Mint(users.Resolve(address), amountToMint)
68-
if err != nil {
69-
panic(err.Error())
70-
}
7171

7272
// update
7373
setLastMintedHeight(currentHeight)
7474
setMintedEmissionAmount(GetMintedEmissionAmount() + amountToMint)
7575
setLeftEmissionAmount(GetLeftEmissionAmount() - amountToMint)
7676

77+
// mint calculated amount to address
78+
err := privateLedger.Mint(users.Resolve(address), amountToMint)
79+
if err != nil {
80+
panic(err.Error())
81+
}
82+
7783
return amountToMint
7884
}
7985

8086
func Burn(from pusers.AddressOrName, amount uint64) {
8187
owner.AssertCallerIsOwner()
8288
fromAddr := users.Resolve(from)
8389
checkErr(privateLedger.Burn(fromAddr, amount))
90+
91+
burnAmount += amount
8492
}
8593

8694
func TotalSupply() uint64 {
@@ -137,60 +145,60 @@ func checkErr(err error) {
137145
}
138146
}
139147

140-
// helper functions
141-
142148
// calculateAmountToMint calculates the amount of gns to mint
143149
// It calculates the amount of gns to mint for each halving year for block range.
144150
// It also handles the left emission amount if the current block range includes halving year end block.
145151
func calculateAmountToMint(fromHeight, toHeight int64) uint64 {
146152
fromYear := GetHalvingYearByHeight(fromHeight)
147-
toYear := GetHalvingYearByHeight(toHeight)
148153

149-
if isEmissionEnded(fromYear) || isEmissionEnded(toYear) {
150-
return 0
154+
// if toHeight is greater than emission end height, set toHeight to emission end height
155+
if toHeight > GetEndHeight() {
156+
toHeight = GetEndHeight()
151157
}
158+
toYear := GetHalvingYearByHeight(toHeight)
152159

153160
totalAmountToMint := uint64(0)
154161

155-
for i := fromYear; i <= toYear; i++ {
156-
yearEndHeight := GetHalvingYearBlock(i)
162+
for year := fromYear; year <= toYear; year++ {
163+
yearEndHeight := GetHalvingYearEndBlock(year)
157164
mintUntilHeight := i64Min(yearEndHeight, toHeight)
158165

159166
// how many blocks to calculate
160167
blocks := uint64(mintUntilHeight-fromHeight) + 1
161168

162169
// amount of gns to mint for each block for current year
163-
singleBlockAmount := GetAmountByHeight(yearEndHeight)
170+
singleBlockAmount := GetAmountPerBlockPerHalvingYear(year)
164171

165172
// amount of gns to mint for current year
166173
yearAmountToMint := singleBlockAmount * blocks
167174

168175
// if last block of halving year, handle left emission amount
169176
if isLastBlockOfHalvingYear(mintUntilHeight) {
170-
yearAmountToMint += handleLeftEmissionAmount(i, yearAmountToMint)
177+
yearAmountToMint += handleLeftEmissionAmount(year, yearAmountToMint)
171178
}
172179
totalAmountToMint += yearAmountToMint
173-
SetHalvingYearMintAmount(i, GetHalvingYearMintAmount(i)+yearAmountToMint)
180+
setHalvingYearMintAmount(year, GetHalvingYearMintAmount(year)+yearAmountToMint)
174181

175182
// update fromHeight for next year (if necessary)
176183
fromHeight = mintUntilHeight + 1
177184
}
178185

186+
assertTooManyEmission(totalAmountToMint)
179187
return totalAmountToMint
180188
}
181189

182190
// isLastBlockOfHalvingYear returns true if the current block is the last block of a halving year.
183191
func isLastBlockOfHalvingYear(height int64) bool {
184192
year := GetHalvingYearByHeight(height)
185-
lastBlock := GetHalvingYearBlock(year)
193+
lastBlock := GetHalvingYearEndBlock(year)
186194

187195
return height == lastBlock
188196
}
189197

190198
// handleLeftEmissionAmount handles the left emission amount for a halving year.
191199
// It calculates the left emission amount by subtracting the halving year mint amount from the halving year amount.
192200
func handleLeftEmissionAmount(year int64, amount uint64) uint64 {
193-
return GetHalvingYearAmount(year) - GetHalvingYearMintAmount(year) - amount
201+
return GetHalvingYearMaxAmount(year) - GetHalvingYearMintAmount(year) - amount
194202
}
195203

196204
// skipIfSameHeight returns true if the current block height is the same as the last minted height.
@@ -199,30 +207,36 @@ func skipIfSameHeight(lastMintedHeight, currentHeight int64) bool {
199207
return lastMintedHeight == currentHeight
200208
}
201209

202-
// isEmissionEnded returns true if the emission is ended.
203-
// It returns false if the emission is not ended.
204-
func isEmissionEnded(year int64) bool {
205-
if 1 <= year && year <= 12 {
206-
return false
210+
// skipIfEmissionEnded returns true if the emission has ended.
211+
func skipIfEmissionEnded(height int64) bool {
212+
if isEmissionEnded(height) {
213+
return true
207214
}
208215

209-
return true
216+
return false
210217
}
211218

212-
// Getter
219+
// GetLastMintedHeight returns the last block height that gns was minted.
213220
func GetLastMintedHeight() int64 {
214221
return lastMintedHeight
215222
}
216223

224+
// GetLeftEmissionAmount returns the amount of GNS can be minted.
217225
func GetLeftEmissionAmount() uint64 {
218226
return leftEmissionAmount
219227
}
220228

229+
// GetBurnAmount returns the amount of GNS that has been burned.
230+
func GetBurnAmount() uint64 {
231+
return burnAmount
232+
}
233+
234+
// GetMintedEmissionAmount returns the amount of GNS that has been minted by the emission contract.
235+
// It does not include initial minted amount.
221236
func GetMintedEmissionAmount() uint64 {
222237
return mintedEmissionAmount
223238
}
224239

225-
// Setter
226240
func setLastMintedHeight(height int64) {
227241
lastMintedHeight = height
228242
}
@@ -234,3 +248,14 @@ func setLeftEmissionAmount(amount uint64) {
234248
func setMintedEmissionAmount(amount uint64) {
235249
mintedEmissionAmount = amount
236250
}
251+
252+
// assertTooManyEmission asserts if the amount of gns to mint is too many.
253+
// It checks if the amount of gns to mint is greater than the left emission amount or the total emission amount.
254+
func assertTooManyEmission(amount uint64) {
255+
if amount > GetLeftEmissionAmount() || (amount+GetMintedEmissionAmount()) > MAX_EMISSION_AMOUNT {
256+
panic(addDetailToError(
257+
errTooManyEmission,
258+
ufmt.Sprintf("amount: %d", amount),
259+
))
260+
}
261+
}

0 commit comments

Comments
 (0)