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.

fishnft.gno

4.34 Kb · 165 lines
  1// Package fishnft is the six model tokens, one for each sea GLB.
  2// It takes no GNOT, draws no FISH, and does not replace shoal/fish.
  3package fishnft
  4
  5import (
  6	"chain"
  7	"chain/runtime/unsafe"
  8	"errors"
  9	"strconv"
 10	"strings"
 11
 12	"gno.land/p/nt/grc721/metadata/v0"
 13	"gno.land/p/nt/grc721/v0"
 14	"gno.land/p/nt/seqid/v0"
 15)
 16
 17type body struct {
 18	id      int
 19	rarity  string
 20	species string
 21	file    string
 22	owner   address
 23	uri     string
 24}
 25
 26var (
 27	token    *grc721.Token
 28	ledger   *grc721.PrivateLedger
 29	metaLed  *metadata.Ledger
 30	tokenSeq seqid.ID
 31	bodies   []*body
 32	minted   int
 33)
 34
 35// Init issues token 1 through 6 to the caller. A second call does nothing useful and panics.
 36func Init(cur realm) {
 37	if !cur.IsCurrent() {
 38		panic("fishnft: realm")
 39	}
 40	if !cur.Previous().IsUserCall() {
 41		panic("fishnft: eoa")
 42	}
 43	if token != nil {
 44		panic("fishnft: init")
 45	}
 46	rejectPay()
 47	tok, led := grc721.NewToken("Shoal Models", "MODEL", tokenSeq.Next(), cur)
 48	token = tok
 49	ledger = led
 50	_, metaLed = metadata.NewMetadata(led)
 51	bodies = []*body{nil}
 52	caller := cur.Previous().Address()
 53	issue(caller, "common", "Minnow", "common.glb")
 54	issue(caller, "uncommon", "Mackerel", "uncommon.glb")
 55	issue(caller, "rare", "Snapper", "rare.glb")
 56	issue(caller, "epic", "Angler", "epic.glb")
 57	issue(caller, "legendary", "Koi Dragon", "legendary.glb")
 58	issue(caller, "mythic", "Abyss Leviathan", "mythic.glb")
 59	chain.Emit("Init", "owner", caller.String(), "minted", strconv.Itoa(minted))
 60}
 61
 62// Transfer moves one model token. The caller must own it, and the call must send no coins.
 63func Transfer(cur realm, to address, id int) {
 64	if !cur.IsCurrent() {
 65		panic("fishnft: realm")
 66	}
 67	if !cur.Previous().IsUserCall() {
 68		panic("fishnft: eoa")
 69	}
 70	if token == nil {
 71		panic("fishnft: init")
 72	}
 73	rejectPay()
 74	if !to.IsValid() || to == cur.Previous().Address() {
 75		panic("fishnft: addr")
 76	}
 77	row := must(id)
 78	caller := cur.Previous().Address()
 79	if row.owner != caller {
 80		panic("fishnft: owner")
 81	}
 82	tid := grc721.TokenID(strconv.Itoa(id))
 83	if err := ledger.TransferFrom(caller, caller, to, tid); err != nil {
 84		panic("fishnft: transfer")
 85	}
 86	row.owner = to
 87	chain.Emit("Transfer", "id", strconv.Itoa(id), "to", to.String())
 88}
 89
 90func Version() string { return "fishnft-1" }
 91func Minted() int      { return minted }
 92func Name() string     { return "Shoal Models" }
 93func Symbol() string   { return "MODEL" }
 94
 95func RarityOf(id int) string  { return must(id).rarity }
 96func SpeciesOf(id int) string { return must(id).species }
 97func ModelOf(id int) string   { return must(id).file }
 98func TokenURI(id int) string  { return must(id).uri }
 99
100func OwnerOf(tid string) (address, error) {
101	id, err := strconv.Atoi(tid)
102	if err != nil || token == nil || id < 1 || id >= len(bodies) || bodies[id] == nil {
103		return address(""), errors.New("fishnft: unknown")
104	}
105	return bodies[id].owner, nil
106}
107
108func Render(path string) string {
109	if path != "" {
110		return "# Shoal model NFTs\n\nThat path is not a token page.\n"
111	}
112	if token == nil {
113		return "# Shoal model NFTs\n\nNot issued.\n"
114	}
115	var b strings.Builder
116	b.WriteString("# Shoal model NFTs\n\nSix tokens. No GNOT. This is not shoal/fish.\n\n")
117	for _, row := range bodies {
118		if row == nil {
119			continue
120		}
121		b.WriteString("- ")
122		b.WriteString(strconv.Itoa(row.id))
123		b.WriteString(" ")
124		b.WriteString(row.species)
125		b.WriteString(" ")
126		b.WriteString(row.rarity)
127		b.WriteString(" ")
128		b.WriteString(row.file)
129		b.WriteString("\n")
130	}
131	return b.String()
132}
133
134func issue(to address, rarity, species, file string) {
135	minted++
136	id := minted
137	uri := "data:application/json;charset=utf-8," +
138		`{"name":"` + species + `","description":"Shoal model ` + file +
139		`","attributes":[{"trait_type":"Rarity","value":"` + rarity +
140		`"},{"trait_type":"Species","value":"` + species +
141		`"},{"trait_type":"Model","value":"` + file + `"}]}`
142	row := &body{id: id, rarity: rarity, species: species, file: file, owner: to, uri: uri}
143	bodies = append(bodies, row)
144	tid := grc721.TokenID(strconv.Itoa(id))
145	if err := ledger.Mint(to, tid); err != nil {
146		panic("fishnft: mint")
147	}
148	if err := metaLed.SetTokenURI(tid, uri); err != nil {
149		panic("fishnft: uri")
150	}
151	chain.Emit("Mint", "id", strconv.Itoa(id), "model", file)
152}
153
154func must(id int) *body {
155	if token == nil || id < 1 || id >= len(bodies) || bodies[id] == nil {
156		panic("fishnft: unknown")
157	}
158	return bodies[id]
159}
160
161func rejectPay() {
162	if len(unsafe.OriginSend()) != 0 {
163		panic("fishnft: payment")
164	}
165}