1package stdnft23import (4 "chain"56 "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)1112// Adena probe: collection realm wrapping genesis GRC721 (string TokenID,13// metadata TokenURI, enumerable TokenOfOwnerByIndex). No List/Buy escrow.1415const (16 maxImageLen = 20017 defaultImage = "https://bazaar-gno.vercel.app/gnomies.png"18)1920var (21 token *grc721.Token22 ledger *grc721.PrivateLedger23 metaView *metadata.Metadata24 metaLed *metadata.Ledger25 enumView *enumerable.Enumerable26 tokenSeq seqid.ID27 inited bool28 creator address29)3031func 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 = tok43 ledger = led44 metaView, metaLed = metadata.NewMetadata(led)45 enumView, _ = enumerable.NewEnumerable(led)46 creator = cur.Previous().Address()47 inited = true48 chain.Emit("Init", "creator", creator.String(), "name", name, "symbol", symbol)49}5051func 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 = defaultImage64 }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}8586func Name() string {87 requireInit()88 return token.GetName()89}9091func Symbol() string {92 requireInit()93 return token.GetSymbol()94}9596func TotalSupply() int64 {97 requireInit()98 return token.TotalSupply()99}100101func BalanceOf(owner string) int64 {102 requireInit()103 who := address(owner)104 if !who.IsValid() {105 return 0106 }107 n, err := token.BalanceOf(who)108 if err != nil {109 return 0110 }111 return n112}113114func 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}122123func 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 u130}131132func 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}144145func 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}153154func 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 int64166 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 out177}178179func 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}189190func 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}201202func Creator() string {203 requireInit()204 return creator.String()205}206207func Render(_ string) string {208 if !inited {209 return "# StdNFT\n\ncall Init first\n"210 }211 return token.RenderHome() + "\ncreator: " + creator.String() + "\n"212}213214func requireInit() {215 if !inited {216 panic("stdnft: call Init first")217 }218}219220func 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}230231func 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}235236func 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}252253func 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 continue260 }261 hex := "0123456789ABCDEF"262 out += "%"263 out += string([]byte{hex[c>>4], hex[c&15]})264 }265 return out266}267Approve(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, to string, tid string)
BalanceOf(owner string) int64
Creator() string
Init(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, name string, symbol string)
Mint(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, tid string, name string, image string)
Name() string
OwnerOf(tid string) string
Render(string) string
Symbol() string
TokenByIndex(index int64) string
TokenOfOwnerByIndex(owner string, index int64) string
TokensOf(owner string) string
TokenURI(tid string) string
TotalSupply() int64
TransferFrom(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, from string, to string, tid string)
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.