← Back to Realms

Realm

gno.land/r/akkadia/beta/craft

Overview

Realm Path
gno.land/r/akkadia/beta/craft
Exported Functions
14
State Entries
14
Source Files
7
Total Package Entries
49

Exported Functions

14 exported functions

State

14 state entries

Source Code

FILES
craft.gno
go
1package craft
2
3import (
4	"chain"
5	"chain/banker"
6	"chain/runtime/unsafe"
7	"strconv"
8
9	"gno.land/p/akkadia/beta/accesscontrol"
10	"gno.land/p/nt/bptree/v0"
11	"gno.land/r/akkadia/beta/admin"
12	"gno.land/r/akkadia/beta/equipment"
13	"gno.land/r/akkadia/beta/item"
14)
15
16const (
17	SetRecipeEvent     = "SetRecipe"
18	DeleteRecipeEvent  = "DeleteRecipe"
19	DeleteRecipesEvent = "DeleteRecipes"
20	SetListLimitEvent  = "SetListLimit"
21	SetCraftFeeEvent   = "SetCraftFee"
22	CraftEvent         = "Craft"
23)
24
25var (
26	recipes   = bptree.NewBPTree32()
27	listLimit = defaultRecipeListLimit
28	craftFee  = defaultCraftFee
29)
30
31func init() { resetCraftState() }
32
33func resetCraftState() {
34	recipes = bptree.NewBPTree32()
35	listLimit = defaultRecipeListLimit
36	craftFee = defaultCraftFee
37}
38
39// SetRecipe stores the item definition ID produced by a canonical recipe.
40func SetRecipe(cur realm, recipe string, itemID uint32) {
41	accesscontrol.AssertIsAdmin(0, cur, admin.IsAdmin)
42	assertCraftNoCoins()
43	canonicalRecipe, _ := mustParseRecipe(recipe)
44	setRecipe(canonicalRecipe, itemID)
45}
46
47// SetRecipes stores up to 100 canonical recipe and item ID pairs in one transaction.
48func SetRecipes(cur realm, recipesCSV, itemIDsCSV string) {
49	accesscontrol.AssertIsAdmin(0, cur, admin.IsAdmin)
50	assertCraftNoCoins()
51	canonicalRecipes, itemIDs := mustParseRecipeCSV(recipesCSV, itemIDsCSV)
52	for i, recipe := range canonicalRecipes {
53		setRecipe(recipe, itemIDs[i])
54	}
55}
56
57func setRecipe(canonicalRecipe string, itemID uint32) {
58	recipes.Set(canonicalRecipe, itemID)
59	chain.Emit(
60		SetRecipeEvent,
61		"recipe", canonicalRecipe,
62		"itemId", strconv.FormatUint(uint64(itemID), 10),
63	)
64}
65
66func DeleteRecipe(cur realm, recipe string) {
67	accesscontrol.AssertIsAdmin(0, cur, admin.IsAdmin)
68	assertCraftNoCoins()
69	canonicalRecipe := mustCanonicalRecipe(recipe)
70	deleteRecipe(canonicalRecipe)
71	chain.Emit(DeleteRecipeEvent, "recipe", canonicalRecipe)
72}
73
74func DeleteRecipes(cur realm, recipesCSV string) {
75	accesscontrol.AssertIsAdmin(0, cur, admin.IsAdmin)
76	assertCraftNoCoins()
77	canonicalRecipes := mustParseRecipeKeyCSV(recipesCSV)
78	for _, recipe := range canonicalRecipes {
79		if !recipes.Has(recipe) {
80			panic("recipe not found: " + recipe)
81		}
82	}
83	for _, recipe := range canonicalRecipes {
84		deleteRecipe(recipe)
85	}
86	chain.Emit(DeleteRecipesEvent, "count", strconv.Itoa(len(canonicalRecipes)))
87}
88
89func deleteRecipe(canonicalRecipe string) {
90	if _, removed := recipes.Remove(canonicalRecipe); !removed {
91		panic("recipe not found: " + canonicalRecipe)
92	}
93}
94
95func HasRecipe(recipe string) bool {
96	canonical, _, errMsg := parseRecipe(recipe)
97	if errMsg != "" {
98		return false
99	}
100	return recipes.Has(canonical)
101}
102
103func GetRecipe(recipe string) uint32 {
104	return mustGetRecipeItemID(mustCanonicalRecipe(recipe))
105}
106
107func GetRecipeCount() int {
108	return recipes.Size()
109}
110
111func ListRecipes(page, count int) []map[string]string {
112	assertRecipeListPageCount(page, count)
113	result := []map[string]string{}
114	recipes.IterateByOffset((page-1)*count, count, func(recipe string, value any) bool {
115		result = append(result, map[string]string{
116			"recipe": recipe,
117			"itemId": strconv.FormatUint(uint64(value.(uint32)), 10),
118		})
119		return false
120	})
121	return result
122}
123
124func SetListLimit(cur realm, limit int) {
125	accesscontrol.AssertIsAdmin(0, cur, admin.IsAdmin)
126	assertCraftNoCoins()
127	if limit < 1 {
128		panic("limit must be at least 1")
129	}
130
131	oldLimit := listLimit
132	listLimit = limit
133	chain.Emit(
134		SetListLimitEvent,
135		"oldLimit", strconv.Itoa(oldLimit),
136		"newLimit", strconv.Itoa(limit),
137	)
138}
139
140func GetListLimit() int {
141	return listLimit
142}
143
144func SetCraftFee(cur realm, fee int64) {
145	accesscontrol.AssertIsAdmin(0, cur, admin.IsAdmin)
146	assertCraftNoCoins()
147	if fee < 0 {
148		panic("fee must be non-negative")
149	}
150
151	oldFee := craftFee
152	craftFee = fee
153	chain.Emit(
154		SetCraftFeeEvent,
155		"oldFee", strconv.FormatInt(oldFee, 10),
156		"newFee", strconv.FormatInt(fee, 10),
157	)
158}
159
160func GetCraftFee() int64 {
161	return craftFee
162}
163
164// Craft burns a canonical recipe and mints the next GRC721 equipment token.
165func Craft(cur realm, recipe string) uint64 {
166	caller := accesscontrol.MustGetUserCaller(0, cur)
167	canonicalRecipe, ingredients := mustParseRecipe(recipe)
168	itemID := mustGetRecipeItemID(canonicalRecipe)
169	assertExactUgnotPayment(craftFee)
170
171	for _, ingredient := range ingredients {
172		item.BurnFromRealm(cross(cur), caller, ingredient.TokenID, ingredient.Amount)
173	}
174	tokenID := equipment.MintFromRealm(cross(cur), caller, itemID, "", "")
175	if craftFee > 0 {
176		bnk := banker.NewBanker(banker.BankerTypeRealmSend, cur)
177		bnk.SendCoins(cur.Address(), admin.GetFeeCollector(), chain.Coins{{"ugnot", craftFee}})
178	}
179
180	chain.Emit(
181		CraftEvent,
182		"owner", caller.String(),
183		"recipe", canonicalRecipe,
184		"equipmentTokenId", strconv.FormatUint(tokenID, 10),
185		"itemId", strconv.FormatUint(uint64(itemID), 10),
186		"fee", strconv.FormatInt(craftFee, 10),
187	)
188	return tokenID
189}
190
191func mustCanonicalRecipe(recipe string) string {
192	canonical, _ := mustParseRecipe(recipe)
193	return canonical
194}
195
196func mustGetRecipeItemID(recipe string) uint32 {
197	value := recipes.Get(recipe)
198	if value == nil {
199		panic("recipe not found: " + recipe)
200	}
201	return value.(uint32)
202}
203
204func assertCraftNoCoins() {
205	if len(unsafe.OriginSend()) != 0 {
206		panic("attached coins not allowed")
207	}
208}
209
210func assertExactUgnotPayment(expected int64) {
211	coins := unsafe.OriginSend()
212	if expected == 0 {
213		if len(coins) != 0 {
214			panic("exact payment required: expected no coins, got " + coins.String())
215		}
216		return
217	}
218	if len(coins) != 1 || coins[0].Denom != "ugnot" || coins[0].Amount != expected {
219		panic("exact payment required: expected " + strconv.FormatInt(expected, 10) + "ugnot, got " + coins.String())
220	}
221}
222

Raw Package Data

Raw JSON data