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.

collection.gno

3.79 Kb · 194 lines
  1package market
  2
  3import (
  4	"strconv"
  5
  6	"chain"
  7
  8	"gno.land/p/nt/avl/v0"
  9	"gno.land/p/nt/grc721/enumerable/v0"
 10	"gno.land/p/nt/grc721/metadata/v0"
 11	"gno.land/p/nt/grc721/v0"
 12	"gno.land/p/nt/seqid/v0"
 13)
 14
 15const (
 16	maxImageLen = 200
 17	maxTidLen   = 64
 18	saleCap     = 100
 19	pageMax     = 50
 20)
 21
 22var (
 23	token    *grc721.Token
 24	ledger   *grc721.PrivateLedger
 25	metaView *metadata.Metadata
 26	metaLed  *metadata.Ledger
 27	enumView *enumerable.Enumerable
 28	tokenSeq seqid.ID
 29	creator  address
 30	ready    bool
 31	listings avl.Tree
 32	sales    [saleCap]sale
 33	saleN    int
 34)
 35
 36type listing struct {
 37	Seller address
 38	Price  int64
 39}
 40
 41type sale struct {
 42	Tid    string
 43	Seller address
 44	Buyer  address
 45	Price  int64
 46	Name   string
 47}
 48
 49// Init creates the collection once. The caller becomes the only minter.
 50func Init(cur realm, name, symbol string) {
 51	caller := requireUser(cur)
 52	if ready {
 53		panic("market: already init")
 54	}
 55	token, ledger = grc721.NewToken(name, symbol, tokenSeq.Next(), cur)
 56	metaView, metaLed = metadata.NewMetadata(ledger)
 57	enumView, _ = enumerable.NewEnumerable(ledger)
 58	creator = caller
 59	ready = true
 60	chain.Emit("Init", "creator", caller.String(), "name", name, "symbol", symbol)
 61}
 62
 63// Mint issues one token to the creator. image is an https URL.
 64func Mint(cur realm, tid, name, image string) {
 65	caller := requireUser(cur)
 66	requireReady()
 67	if caller != creator {
 68		panic("market: not creator")
 69	}
 70	id := parseTid(tid)
 71	if name == "" || len(name) > grc721.MaxNameLen || !ascii(name) {
 72		panic("market: name")
 73	}
 74	if !httpsURL(image) {
 75		panic("market: image")
 76	}
 77	if err := ledger.Mint(caller, id); err != nil {
 78		panic("market: mint")
 79	}
 80	if err := metaLed.SetTokenURI(id, image); err != nil {
 81		panic("market: tokenURI")
 82	}
 83	if err := metaLed.SetTokenMetadata(id, metadata.Data{
 84		Name:        name,
 85		Description: name,
 86		Image:       image,
 87	}); err != nil {
 88		panic("market: metadata")
 89	}
 90	chain.Emit("Mint", "tid", tid, "owner", caller.String(), "name", name)
 91}
 92
 93// Transfer moves a token the caller owns.
 94func Transfer(cur realm, to address, tid string) {
 95	caller := requireUser(cur)
 96	requireReady()
 97	if !to.IsValid() || to == caller {
 98		panic("market: to")
 99	}
100	id := parseTid(tid)
101	if err := ledger.TransferFrom(caller, caller, to, id); err != nil {
102		panic("market: transfer")
103	}
104}
105
106func requireReady() {
107	if !ready {
108		panic("market: call Init")
109	}
110}
111
112func requireUser(cur realm) address {
113	prev := cur.Previous()
114	if !prev.IsUserCall() {
115		panic("market: eoa")
116	}
117	return prev.Address()
118}
119
120func parseTid(tid string) grc721.TokenID {
121	if tid == "" || len(tid) > maxTidLen || !ascii(tid) {
122		panic("market: tid")
123	}
124	for i := 0; i < len(tid); i++ {
125		c := tid[i]
126		ok := (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '-'
127		if !ok {
128			panic("market: tid")
129		}
130	}
131	return grc721.TokenID(tid)
132}
133
134func httpsURL(s string) bool {
135	const prefix = "https://"
136	if len(s) < len(prefix)+1 || len(s) > maxImageLen || !ascii(s) {
137		return false
138	}
139	return s[:len(prefix)] == prefix
140}
141
142func ascii(s string) bool {
143	for i := 0; i < len(s); i++ {
144		if s[i] < 0x20 || s[i] > 0x7e {
145			return false
146		}
147	}
148	return true
149}
150
151func ownerOf(tid grc721.TokenID) address {
152	owner, err := token.OwnerOf(tid)
153	if err != nil {
154		panic("market: owner")
155	}
156	return owner
157}
158
159func metaNameImage(tid grc721.TokenID) (string, string) {
160	data, err := metaView.TokenMetadata(tid)
161	if err != nil {
162		return "", ""
163	}
164	return data.Name, data.Image
165}
166
167func page(offset, limit int) (int, int) {
168	if offset < 0 {
169		offset = 0
170	}
171	if limit < 1 {
172		limit = 1
173	}
174	if limit > pageMax {
175		limit = pageMax
176	}
177	return offset, limit
178}
179
180func quote(s string) string {
181	out := "\""
182	for i := 0; i < len(s); i++ {
183		c := s[i]
184		if c == '\\' || c == '"' {
185			out += "\\"
186		}
187		out += s[i : i+1]
188	}
189	return out + "\""
190}
191
192func i64(n int64) string {
193	return strconv.FormatInt(n, 10)
194}