Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}