tba.gno
3.39 Kb · 155 lines
1package tba
2
3import (
4 "strconv"
5 "strings"
6
7 "chain"
8 "chain/banker"
9 "chain/runtime/unsafe"
10
11 "gno.land/p/test1/grc721"
12 "gno.land/p/nt/avl/v0"
13)
14
15const selfPath = "gno.land/r/zeycan1/tba"
16
17type tokenMeta struct {
18 name string
19 image string
20}
21
22var (
23 nft *grc721.BasicNFT
24 admin address
25 nextID int64 = 1
26 allTokens []grc721.TokenID
27 metaByID avl.Tree // tokenID string -> *tokenMeta
28)
29
30func init() {
31 admin = address(unsafe.PreviousRealm().Address())
32}
33
34func Setup(cur realm) {
35 if nft != nil {
36 panic("already initialized")
37 }
38 caller := cur.Previous().Address()
39 if caller != admin {
40 panic("only admin can initialize")
41 }
42 nft = grc721.NewBasicNFT(0, cur, "Token Bound Account", "TBA")
43}
44
45func Mint(cur realm, to address, name string, image string) grc721.TokenID {
46 caller := cur.Previous().Address()
47 if caller != admin {
48 panic("only admin can mint")
49 }
50 tid := grc721.TokenID(strconv.FormatInt(nextID, 10))
51 nextID++
52 if err := nft.Mint(to, tid); err != nil {
53 panic(err)
54 }
55 allTokens = append(allTokens, tid)
56 metaByID.Set(string(tid), &tokenMeta{name: name, image: image})
57 return tid
58}
59
60func vaultSubpath(tid grc721.TokenID) string {
61 return "vault/" + string(tid)
62}
63
64func VaultAddress(tid grc721.TokenID) address {
65 return chain.DerivePkgSubAddr(selfPath, vaultSubpath(tid))
66}
67
68func Balance(tid grc721.TokenID) int64 {
69 coins := banker.NewReadonlyBanker().GetCoins(VaultAddress(tid))
70 return coins.AmountOf("ugnot")
71}
72
73func Withdraw(cur realm, tid grc721.TokenID, to address, amount int64) {
74 caller := cur.Previous().Address()
75 owner, err := nft.OwnerOf(tid)
76 if err != nil {
77 panic(err)
78 }
79 if caller != owner {
80 panic("caller is not the current owner of this token")
81 }
82
83 sub := cur.Sub(vaultSubpath(tid))
84 b := banker.NewBanker(banker.BankerTypeRealmSend, sub)
85 b.SendCoins(sub.Address(), to, chain.NewCoins(chain.NewCoin("ugnot", amount)))
86}
87
88func OwnerOf(tid grc721.TokenID) (address, error) {
89 return nft.OwnerOf(tid)
90}
91
92func BalanceOfOwner(owner address) (int64, error) {
93 return nft.BalanceOf(owner)
94}
95
96func TransferFrom(cur realm, from, to address, tid grc721.TokenID) error {
97 caller := cur.Previous().Address()
98 return nft.TransferFrom(caller, from, to, tid)
99}
100
101func AllTokens() []grc721.TokenID {
102 return allTokens
103}
104
105func TokensOf(owner address) []grc721.TokenID {
106 var out []grc721.TokenID
107 for _, tid := range allTokens {
108 o, err := nft.OwnerOf(tid)
109 if err == nil && o == owner {
110 out = append(out, tid)
111 }
112 }
113 return out
114}
115
116func TokenInfo(tid grc721.TokenID) (name string, image string, owner address, vault address, balance int64, err error) {
117 owner, err = nft.OwnerOf(tid)
118 if err != nil {
119 return "", "", "", "", 0, err
120 }
121 vault = VaultAddress(tid)
122 balance = Balance(tid)
123 if v := metaByID.Get(string(tid)); v != nil {
124 m := v.(*tokenMeta)
125 name = m.name
126 image = m.image
127 }
128 return name, image, owner, vault, balance, nil
129}
130
131func Render(path string) string {
132 if nft == nil {
133 return "# Token Bound Accounts\n\nNot initialized yet. Call Setup() first.\n"
134 }
135 return nft.RenderHome()
136}
137
138// --- Frontend-friendly CSV variants (avoid slice-ref parsing issues) ---
139
140func AllTokensCSV() string {
141 ids := make([]string, len(allTokens))
142 for i, t := range allTokens {
143 ids[i] = string(t)
144 }
145 return strings.Join(ids, ",")
146}
147
148func TokensOfCSV(owner address) string {
149 toks := TokensOf(owner)
150 ids := make([]string, len(toks))
151 for i, t := range toks {
152 ids[i] = string(t)
153 }
154 return strings.Join(ids, ",")
155}