grc1155.gno
6.11 Kb ยท 255 lines
1package block
2
3import (
4 "chain"
5 "chain/banker"
6 "chain/runtime"
7 "strconv"
8
9 "gno.land/p/demo/tokens/grc1155"
10 "gno.land/p/nt/avl"
11 "gno.land/p/nt/ufmt"
12 "gno.land/r/akkadia/admin"
13)
14
15const (
16 MintEvent = "Mint"
17)
18
19var (
20 token = grc1155.NewBasicGRC1155Token("")
21 supply = avl.NewTree() // tid -> uint32
22)
23
24func BalanceOf(user address, tokenID grc1155.TokenID) int64 {
25 balance, err := token.BalanceOf(user, tokenID)
26 if err != nil {
27 panic("balanceOf failed: " + err.Error())
28 }
29
30 return balance
31}
32
33func BalanceOfBatch(ul []address, tokenIDs []grc1155.TokenID) []int64 {
34 balanceBatch, err := token.BalanceOfBatch(ul, tokenIDs)
35 if err != nil {
36 panic("balanceOfBatch failed: " + err.Error())
37 }
38
39 return balanceBatch
40}
41
42func IsApprovedForAll(owner, operator address) bool {
43 return token.IsApprovedForAll(owner, operator)
44}
45
46func SetApprovalForAll(cur realm, operator address, approved bool) {
47 err := token.SetApprovalForAll(operator, approved)
48 if err != nil {
49 panic("setApprovalForAll failed: " + err.Error())
50 }
51}
52
53func TransferFrom(cur realm, from, to address, tokenID grc1155.TokenID, amount int64) {
54 err := token.SafeTransferFrom(from, to, tokenID, amount)
55 if err != nil {
56 panic("transferFrom failed: " + err.Error())
57 }
58
59 // Update user token indexes
60 addUserToken(to, tokenID)
61 removeUserToken(from, tokenID)
62}
63
64func BatchTransferFrom(cur realm, from, to address, tokenIDs []grc1155.TokenID, amounts []int64) {
65 err := token.SafeBatchTransferFrom(from, to, tokenIDs, amounts)
66 if err != nil {
67 panic("batchTransferFrom failed: " + err.Error())
68 }
69
70 // Update user token indexes in batch (efficient)
71 addUserTokenBatch(to, tokenIDs)
72 removeUserTokenBatch(from, tokenIDs)
73}
74
75// Mint mints block tokens with payment (anyone can mint)
76func Mint(cur realm, to address, blockID uint32, amount int64) {
77 if amount < 1 {
78 panic("amount must be positive")
79 }
80
81 caller := runtime.PreviousRealm().Address()
82
83 // Get block metadata
84 blockIDStr := blockIDToString(blockID)
85 blockIDKey := blockIDToKey(blockID)
86 blockVal, found := blocks.Get(blockIDKey)
87 if !found {
88 panic("block not found: " + blockIDStr)
89 }
90 block := blockVal.(map[string]string)
91
92 // Get mint price
93 mintPriceStr := block["mintPrice"]
94 mintPrice, err := strconv.ParseInt(mintPriceStr, 10, 64)
95 if err != nil {
96 panic("mint price is not a number")
97 }
98
99 // Get max supply
100 maxSupplyStr := block["maxSupply"]
101 maxSupply, err := strconv.ParseInt(maxSupplyStr, 10, 64)
102 if err != nil {
103 panic("max supply is not a number")
104 }
105
106 // -1 blocks are not mintable
107 if maxSupply == -1 {
108 panic("block not mintable")
109 }
110
111 // Check current supply
112 currentSupply, found := supply.Get(blockIDStr)
113 if !found {
114 if amount > maxSupply {
115 panic("supply exceeded")
116 }
117 supply.Set(blockIDStr, amount)
118 } else {
119 if currentSupply.(int64) + amount > maxSupply {
120 panic("supply exceeded")
121 }
122 supply.Set(blockIDStr, currentSupply.(int64) + amount)
123 }
124
125 // Check payment
126 sent := banker.OriginSend()
127 totalPrice := mintPrice * amount
128 sentAmount := sent.AmountOf("ugnot")
129
130 if sentAmount < totalPrice {
131 panic(ufmt.Sprintf("insufficient payment: required %d ugnot, got %d", totalPrice, sentAmount))
132 }
133
134 // Refund excess
135 if sentAmount > totalPrice {
136 banker_ := banker.NewBanker(banker.BankerTypeRealmSend)
137 realmAddr := runtime.CurrentRealm().Address()
138 refund := sentAmount - totalPrice
139 refundCoins := chain.NewCoins(chain.NewCoin("ugnot", refund))
140 banker_.SendCoins(realmAddr, caller, refundCoins)
141 }
142
143 // Get creator address
144 creatorStr, found := block["creator"]
145 if !found {
146 panic("creator not found in block: " + blockIDStr)
147 }
148 creator := parseAddress(creatorStr)
149
150 // Calculate shares
151 bps := GetCreatorBPS()
152 var creatorShare, operatorShare int64
153 if totalPrice > 0 {
154 creatorShare = (totalPrice * int64(bps)) / 10000
155 operatorShare = totalPrice - creatorShare
156 }
157
158 // Distribute mint revenue to creator and operator
159 if totalPrice > 0 {
160 // Send funds
161 bnk := banker.NewBanker(banker.BankerTypeRealmSend)
162 realmAddr := runtime.CurrentRealm().Address()
163
164 if creatorShare > 0 {
165 coins := chain.Coins{chain.Coin{"ugnot", creatorShare}}
166 bnk.SendCoins(realmAddr, creator, coins)
167 }
168
169 if operatorShare > 0 {
170 coins := chain.Coins{chain.Coin{"ugnot", operatorShare}}
171 bnk.SendCoins(realmAddr, admin.GetFeeCollector(), coins)
172 }
173 }
174
175 // Mint tokens
176 tokenID := blockIDToTokenID(blockID)
177 err = token.SafeMint(to, tokenID, amount)
178 if err != nil {
179 panic("mint failed: " + err.Error())
180 }
181
182 // Update user token index
183 addUserToken(to, tokenID)
184
185 // Emit mint revenue event
186 chain.Emit(MintEvent,
187 "id", blockIDStr,
188 "amount", strconv.FormatInt(amount, 10),
189 "totalPrice", strconv.FormatInt(totalPrice, 10),
190 "creator", creator.String(),
191 "creatorShare", strconv.FormatInt(creatorShare, 10),
192 "feeCollector", admin.GetFeeCollector().String(),
193 "feeCollectorShare", strconv.FormatInt(operatorShare, 10),
194 )
195}
196
197// burn - internal helper for burning tokens
198func burn(from address, tokeID grc1155.TokenID, amount int64) {
199 err := token.Burn(from, tokeID, amount)
200 if err != nil {
201 panic("burn failed: " + err.Error())
202 }
203
204 // Update user token index (removes if balance becomes 0)
205 removeUserToken(from, tokeID)
206}
207
208func Burn(cur realm, from address, tokeID grc1155.TokenID, amount int64) {
209 caller := runtime.PreviousRealm().Address()
210
211 if caller != from {
212 assertIsAdmin(caller)
213 }
214
215 burn(from, tokeID, amount)
216}
217
218func BurnBatch(cur realm, from address, tokeIDs []grc1155.TokenID, amounts []int64) {
219 caller := runtime.PreviousRealm().Address()
220
221 if caller != from {
222 assertIsAdmin(caller)
223 }
224
225 // Burn tokens in one operation
226 err := token.BatchBurn(from, tokeIDs, amounts)
227 if err != nil {
228 panic("batchBurn failed: " + err.Error())
229 }
230
231 // Update user token index in batch (efficient)
232 removeUserTokenBatch(from, tokeIDs)
233}
234
235func ListSupplies(tokeIDs ...grc1155.TokenID) []map[string]string {
236 result := []map[string]string{}
237
238 if len(tokeIDs) == 0 {
239 return result
240 }
241
242 for _, tokeID := range tokeIDs {
243 currentSupply, found := supply.Get(string(tokeID))
244 if !found {
245 continue
246 }
247
248 result = append(result, map[string]string{
249 "id": string(tokeID),
250 "supply": ufmt.Sprintf("%d", currentSupply.(int64)),
251 })
252 }
253
254 return result
255}