Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

stdnft.gno

5.37 Kb · 266 lines
  1package stdnft
  2
  3import (
  4	"chain"
  5
  6	"gno.land/p/nt/grc721/enumerable/v0"
  7	"gno.land/p/nt/grc721/metadata/v0"
  8	"gno.land/p/nt/grc721/v0"
  9	"gno.land/p/nt/seqid/v0"
 10)
 11
 12// Adena probe: collection realm wrapping genesis GRC721 (string TokenID,
 13// metadata TokenURI, enumerable TokenOfOwnerByIndex). No List/Buy escrow.
 14
 15const (
 16	maxImageLen  = 200
 17	defaultImage = "https://bazaar-gno.vercel.app/gnomies.png"
 18)
 19
 20var (
 21	token    *grc721.Token
 22	ledger   *grc721.PrivateLedger
 23	metaView *metadata.Metadata
 24	metaLed  *metadata.Ledger
 25	enumView *enumerable.Enumerable
 26	tokenSeq seqid.ID
 27	inited   bool
 28	creator  address
 29)
 30
 31func Init(cur realm, name, symbol string) {
 32	if !cur.IsCurrent() {
 33		panic("stdnft: spoofed realm")
 34	}
 35	if !cur.Previous().IsUserCall() {
 36		panic("stdnft: EOA only")
 37	}
 38	if inited {
 39		panic("stdnft: already initialized")
 40	}
 41	tok, led := grc721.NewToken(name, symbol, tokenSeq.Next(), cur)
 42	token = tok
 43	ledger = led
 44	metaView, metaLed = metadata.NewMetadata(led)
 45	enumView, _ = enumerable.NewEnumerable(led)
 46	creator = cur.Previous().Address()
 47	inited = true
 48	chain.Emit("Init", "creator", creator.String(), "name", name, "symbol", symbol)
 49}
 50
 51func Mint(cur realm, tid, name, image string) {
 52	caller := requireUser(cur)
 53	if caller != creator {
 54		panic("stdnft: not creator")
 55	}
 56	if tid == "" {
 57		panic("stdnft: tid")
 58	}
 59	if name == "" || len(name) > grc721.MaxNameLen {
 60		panic("stdnft: name")
 61	}
 62	if image == "" {
 63		image = defaultImage
 64	}
 65	if len(image) > maxImageLen {
 66		panic("stdnft: image")
 67	}
 68	id := grc721.TokenID(tid)
 69	if err := ledger.Mint(caller, id); err != nil {
 70		panic("stdnft: mint")
 71	}
 72	if err := metaLed.SetTokenURI(id, tokenJSON(name, image)); err != nil {
 73		panic("stdnft: tokenURI")
 74	}
 75	if err := metaLed.SetTokenMetadata(id, metadata.Data{
 76		Name:        name,
 77		Description: name,
 78		Image:       image,
 79		Attributes:  []metadata.Trait{{TraitType: "Kind", Value: "stdnft"}},
 80	}); err != nil {
 81		panic("stdnft: metadata")
 82	}
 83	chain.Emit("Mint", "id", tid, "owner", caller.String())
 84}
 85
 86func Name() string {
 87	requireInit()
 88	return token.GetName()
 89}
 90
 91func Symbol() string {
 92	requireInit()
 93	return token.GetSymbol()
 94}
 95
 96func TotalSupply() int64 {
 97	requireInit()
 98	return token.TotalSupply()
 99}
100
101func BalanceOf(owner string) int64 {
102	requireInit()
103	who := address(owner)
104	if !who.IsValid() {
105		return 0
106	}
107	n, err := token.BalanceOf(who)
108	if err != nil {
109		return 0
110	}
111	return n
112}
113
114func OwnerOf(tid string) string {
115	requireInit()
116	owner, err := token.OwnerOf(grc721.TokenID(tid))
117	if err != nil {
118		panic("stdnft: owner")
119	}
120	return owner.String()
121}
122
123func TokenURI(tid string) string {
124	requireInit()
125	u, err := metaView.TokenURI(grc721.TokenID(tid))
126	if err != nil {
127		panic("stdnft: uri")
128	}
129	return u
130}
131
132func TokenOfOwnerByIndex(owner string, index int64) string {
133	requireInit()
134	who := address(owner)
135	if !who.IsValid() {
136		panic("stdnft: addr")
137	}
138	id, err := enumView.TokenOfOwnerByIndex(who, index)
139	if err != nil {
140		panic("stdnft: index")
141	}
142	return id.String()
143}
144
145func TokenByIndex(index int64) string {
146	requireInit()
147	id, err := enumView.TokenByIndex(index)
148	if err != nil {
149		panic("stdnft: index")
150	}
151	return id.String()
152}
153
154func TokensOf(owner string) string {
155	requireInit()
156	who := address(owner)
157	if !who.IsValid() {
158		return ""
159	}
160	n, err := token.BalanceOf(who)
161	if err != nil || n == 0 {
162		return ""
163	}
164	out := ""
165	var i int64
166	for i = 0; i < n; i++ {
167		id, err := enumView.TokenOfOwnerByIndex(who, i)
168		if err != nil {
169			panic("stdnft: tokens")
170		}
171		if out != "" {
172			out += ","
173		}
174		out += id.String()
175	}
176	return out
177}
178
179func Approve(cur realm, to, tid string) {
180	caller := requireUser(cur)
181	who := address(to)
182	if to != "" && !who.IsValid() {
183		panic("stdnft: addr")
184	}
185	if err := ledger.Approve(caller, who, grc721.TokenID(tid)); err != nil {
186		panic("stdnft: approve")
187	}
188}
189
190func TransferFrom(cur realm, from, to, tid string) {
191	caller := requireUser(cur)
192	src := address(from)
193	dst := address(to)
194	if !src.IsValid() || !dst.IsValid() {
195		panic("stdnft: addr")
196	}
197	if err := ledger.TransferFrom(caller, src, dst, grc721.TokenID(tid)); err != nil {
198		panic("stdnft: transfer")
199	}
200}
201
202func Creator() string {
203	requireInit()
204	return creator.String()
205}
206
207func Render(_ string) string {
208	if !inited {
209		return "# StdNFT\n\ncall Init first\n"
210	}
211	return token.RenderHome() + "\ncreator: " + creator.String() + "\n"
212}
213
214func requireInit() {
215	if !inited {
216		panic("stdnft: call Init first")
217	}
218}
219
220func requireUser(cur realm) address {
221	requireInit()
222	if !cur.IsCurrent() {
223		panic("stdnft: spoofed realm")
224	}
225	if !cur.Previous().IsUserCall() {
226		panic("stdnft: EOA only")
227	}
228	return cur.Previous().Address()
229}
230
231func tokenJSON(name, image string) string {
232	body := `{"name":` + jsonStr(name) + `,"description":` + jsonStr(name) + `,"image":` + jsonStr(image) + `,"attributes":[{"trait_type":"Kind","value":"stdnft"}]}`
233	return "data:application/json;charset=utf-8," + pctURI(body)
234}
235
236func jsonStr(s string) string {
237	out := `"`
238	for _, r := range s {
239		switch r {
240		case '"':
241			out += `\"`
242		case '\\':
243			out += `\\`
244		case '\n':
245			out += `\n`
246		default:
247			out += string(r)
248		}
249	}
250	return out + `"`
251}
252
253func pctURI(s string) string {
254	out := ""
255	for i := 0; i < len(s); i++ {
256		c := s[i]
257		if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.' || c == '~' {
258			out += string(c)
259			continue
260		}
261		hex := "0123456789ABCDEF"
262		out += "%"
263		out += string([]byte{hex[c>>4], hex[c&15]})
264	}
265	return out
266}