@@ -16,39 +16,78 @@ import (
16
16
"gno.land/r/gnoswap/v1/consts"
17
17
)
18
18
19
- const MAXIMUM_SUPPLY = uint64(1_000_000_000_000_000) // 1B
19
+ const (
20
+ MAXIMUM_SUPPLY = uint64(1_000_000_000_000_000)
21
+ INITIAL_MINT_AMOUNT = uint64(100_000_000_000_000)
22
+ MAX_EMISSION_AMOUNT = MAXIMUM_SUPPLY - INITIAL_MINT_AMOUNT
23
+ )
24
+
25
+ var (
26
+ lastMintedHeight = std.GetHeight()
27
+ )
20
28
21
29
var (
22
- lastMintedHeight int64
23
- amountToEmission uint64
30
+ leftEmissionAmount = MAX_EMISSION_AMOUNT
24
31
)
25
32
26
33
var (
27
34
Token, privateLedger = grc20.NewToken("Gnoswap", "GNS", 6)
28
35
UserTeller = Token.CallerTeller()
29
- owner = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN
36
+ owner = ownable.NewWithAddress(consts. ADMIN)
30
37
)
31
38
32
39
func init() {
33
- privateLedger.Mint(owner.Owner(), 100_000_000_000_000) // 100_000_000 GNS ≈ 0.1B
40
+ privateLedger.Mint(owner.Owner(), INITIAL_MINT_AMOUNT)
34
41
getter := func() *grc20.Token { return Token }
35
42
grc20reg.Register(getter, "")
43
+ }
36
44
37
- amountToEmission = MAXIMUM_SUPPLY - uint64(100_000_000_000_000)
45
+ // MintedEmissionAmount returns the amount of GNS that has been minted by the emission contract.
46
+ // It does not include initial minted amount.
47
+ func MintedEmissionAmount() uint64 {
48
+ return TotalSupply() - INITIAL_MINT_AMOUNT
49
+ }
38
50
39
- lastMintedHeight = std.GetHeight()
51
+ func Mint(address pusers.AddressOrName) uint64 {
52
+ common.IsHalted()
53
+
54
+ caller := std.PrevRealm().Addr()
55
+ if err := common.EmissionOnly(caller); err != nil {
56
+ panic(err)
57
+ }
58
+
59
+ lastMintedHeight := GetLastMintedHeight()
60
+ currentHeight := std.GetHeight()
61
+
62
+ // skip minting process if gns for current block is already minted
63
+ if skipIfSameHeight(lastMintedHeight, currentHeight) {
64
+ return 0
65
+ }
66
+
67
+ // calculate gns amount to mint, and the mint to the target address
68
+ amountToMint := calculateAmountToMint(lastMintedHeight+1, currentHeight)
69
+ err := privateLedger.Mint(users.Resolve(address), amountToMint)
70
+ if err != nil {
71
+ panic(err.Error())
72
+ }
73
+
74
+ // update
75
+ setLastMintedHeight(currentHeight)
76
+ setLeftEmissionAmount(leftEmissionAmount - amountToMint)
77
+
78
+ return amountToMint
40
79
}
41
80
42
- func GetAmountToEmission() uint64 { return amountToEmission }
81
+ func Burn(from pusers.AddressOrName, amount uint64) {
82
+ owner.AssertCallerIsOwner()
83
+ fromAddr := users.Resolve(from)
84
+ checkErr(privateLedger.Burn(fromAddr, amount))
85
+ }
43
86
44
87
func TotalSupply() uint64 {
45
88
return UserTeller.TotalSupply()
46
89
}
47
90
48
- func TotalMinted() uint64 {
49
- return UserTeller.TotalSupply() - uint64(100_000_000_000_000)
50
- }
51
-
52
91
func BalanceOf(owner pusers.AddressOrName) uint64 {
53
92
ownerAddr := users.Resolve(owner)
54
93
return UserTeller.BalanceOf(ownerAddr)
@@ -93,93 +132,94 @@ func Render(path string) string {
93
132
}
94
133
}
95
134
96
- // Mint mints GNS to the address.
97
- // Only emission contract can call Mint.
98
- func Mint(address pusers.AddressOrName) uint64 {
99
- common.IsHalted()
100
-
101
- caller := std.PrevRealm().Addr()
102
- if caller != consts.EMISSION_ADDR {
103
- panic(addDetailToError(
104
- errNoPermission,
105
- ufmt.Sprintf("only emission contract(%s) can call Mint, called from %s", consts.EMISSION_ADDR, caller.String()),
106
- ))
135
+ func checkErr(err error) {
136
+ if err != nil {
137
+ panic(err.Error())
107
138
}
139
+ }
108
140
109
- // if not yet initialized, mint 0 amount
110
- if initialized == false {
111
- return 0
112
- }
141
+ // calculateAmountToMint calculates the amount of gns to mint
142
+ // It calculates the amount of gns to mint for each halving year for block range.
143
+ // It also handles the left emission amount if the current block range includes halving year end block.
144
+ func calculateAmountToMint(fromHeight, toHeight int64) uint64 {
145
+ fromYear := GetHalvingYearByHeight(fromHeight)
146
+ toYear := GetHalvingYearByHeight(toHeight)
113
147
114
- // calculate gns emission amount for every block, and send by single call
115
- // for this case, we assume that inside of block range gnoswap state hasn't changed.
116
- nowHeight := std.GetHeight()
117
- amountToMint := uint64(0)
148
+ totalAmountToMint := uint64(0)
118
149
119
- if lastMintedHeight >= nowHeight {
120
- return 0
121
- }
150
+ for i := fromYear; i <= toYear; i++ {
151
+ yearEndHeight := GetHalvingYearBlock(i)
152
+ mintUntilHeight := yearEndHeight
122
153
123
- // If from, to block is at same halving year, no need iterate
124
- fromYear := GetHalvingYearByHeight(lastMintedHeight + 1)
125
- toYear := GetHalvingYearByHeight(nowHeight)
126
-
127
- if fromYear == toYear {
128
- numBlock := uint64(nowHeight - lastMintedHeight)
129
- singleBlockAmount := GetAmountByHeight(nowHeight)
130
- totalBlockAmount := singleBlockAmount * numBlock
131
-
132
- amountToMint += totalBlockAmount
133
- amountToMint = checkAndHandleIfLastBlockOfHalvingYear(nowHeight, amountToMint)
134
-
135
- halvingYearMintAmount[fromYear] += totalBlockAmount
136
- } else {
137
- for i := lastMintedHeight + 1; i <= nowHeight; i++ {
138
- amount := GetAmountByHeight(i)
139
- amount = checkAndHandleIfLastBlockOfHalvingYear(i, amount)
140
- year := GetHalvingYearByHeight(i)
141
- halvingYearMintAmount[year] += amount
142
- amountToMint += amount
154
+ if toHeight < mintUntilHeight {
155
+ mintUntilHeight = toHeight
143
156
}
144
- }
145
157
146
- err := privateLedger.Mint(users.Resolve(address), amountToMint)
147
- if err != nil {
148
- panic(err.Error())
149
- }
158
+ // how many blocks to calculate
159
+ numBlock := uint64(mintUntilHeight-fromHeight) + 1
150
160
151
- lastMintedHeight = nowHeight
161
+ // amount of gns to mint for each block for current year
162
+ singleBlockAmount := GetAmountByHeight(yearEndHeight)
152
163
153
- return amountToMint
154
- }
164
+ // amount of gns to mint for current year
165
+ yearAmountToMint := singleBlockAmount * numBlock
155
166
156
- func Burn(from pusers.AddressOrName, amount uint64) {
157
- owner.AssertCallerIsOwner()
158
- fromAddr := users.Resolve(from)
159
- checkErr(privateLedger.Burn(fromAddr, amount))
167
+ // if last block of halving year, handle left emission amount
168
+ if isLastBlockOfHalvingYear(mintUntilHeight) {
169
+ yearAmountToMint += handleLeftEmissionAmount(i, yearAmountToMint)
170
+ totalAmountToMint += yearAmountToMint
171
+ } else {
172
+ totalAmountToMint += yearAmountToMint
173
+ }
174
+ SetHalvingYearMintAmount(i, GetHalvingYearMintAmount(i)+yearAmountToMint)
175
+
176
+ // update fromHeight for next year (if necessary)
177
+ fromHeight = mintUntilHeight + 1
178
+ }
179
+
180
+ return totalAmountToMint
160
181
}
161
182
162
- func checkAndHandleIfLastBlockOfHalvingYear(height int64, amount uint64) uint64 {
183
+ // isLastBlockOfHalvingYear returns true if the current block is the last block of a halving year.
184
+ func isLastBlockOfHalvingYear(height int64) bool {
163
185
year := GetHalvingYearByHeight(height)
164
- lastBlock := halvingYearBlock[year]
165
- if height == lastBlock {
166
- leftForThisYear := halvingYearAmount[year] - halvingYearMintAmount[year]
167
- amount = leftForThisYear
168
- return amount
169
- }
186
+ lastBlock := GetHalvingYearBlock(year)
170
187
171
- return amount
188
+ return height == lastBlock
172
189
}
173
190
174
- func checkErr(err error) {
175
- if err != nil {
176
- panic(err.Error())
177
- }
191
+ // handleLeftEmissionAmount handles the left emission amount for a halving year.
192
+ // It calculates the left emission amount by subtracting the halving year mint amount from the halving year amount.
193
+ func handleLeftEmissionAmount(year int64, amount uint64) uint64 {
194
+ return GetHalvingYearAmount(year) - GetHalvingYearMintAmount(year) - amount
195
+ }
196
+
197
+ func setLeftEmissionAmount(amount uint64) {
198
+ leftEmissionAmount = amount
199
+ }
200
+
201
+ func isInitialized() bool {
202
+ return initialized
178
203
}
179
204
180
- // TODO:
181
- // 1. when emission contract mint gns reward, last executed height should be get from gns contract.
182
- // mint function of gns contract and mintGns function of emission contract should be synchronized.
183
205
func GetLastMintedHeight() int64 {
184
206
return lastMintedHeight
185
207
}
208
+
209
+ func setLastMintedHeight(height int64) {
210
+ lastMintedHeight = height
211
+ }
212
+
213
+ func GetLeftEmissionAmount() uint64 {
214
+ return leftEmissionAmount
215
+ }
216
+
217
+ // skipIfSameHeight returns true if the current block height is the same as the last minted height.
218
+ // This prevents multiple gns minting inside the same block.
219
+ func skipIfSameHeight(lastMintedHeight, currentHeight int64) bool {
220
+ if lastMintedHeight == currentHeight {
221
+ return true
222
+ }
223
+
224
+ return false
225
+ }
0 commit comments