grc20factory.gno
5.65 Kb ยท 224 lines
1package foo20
2
3import (
4 "std"
5
6 "gno.land/p/demo/avl"
7 p "gno.land/p/demo/avl/pager"
8 "gno.land/p/demo/avl/rotree"
9 "gno.land/p/demo/grc/grc20"
10 "gno.land/p/demo/mux"
11 "gno.land/p/demo/ownable"
12 "gno.land/p/demo/ufmt"
13 "gno.land/p/moul/md"
14 "gno.land/r/demo/grc20reg"
15)
16
17var (
18 instances avl.Tree // symbol -> *instance
19 pager = p.NewPager(rotree.Wrap(&instances, nil), 20, false)
20)
21
22type instance struct {
23 token *grc20.Token
24 ledger *grc20.PrivateLedger
25 admin *ownable.Ownable
26 faucet int64 // per-request amount. disabled if 0.
27}
28
29func New(cur realm, name, symbol string, decimals int, initialMint, faucet int64) {
30 caller := std.PreviousRealm().Address()
31 NewWithAdmin(cur, name, symbol, decimals, initialMint, faucet, caller)
32}
33
34func NewWithAdmin(cur realm, name, symbol string, decimals int, initialMint, faucet int64, admin std.Address) {
35 exists := instances.Has(symbol)
36 if exists {
37 panic("token already exists")
38 }
39
40 token, ledger := grc20.NewToken(name, symbol, decimals)
41 if initialMint > 0 {
42 ledger.Mint(admin, initialMint)
43 }
44
45 inst := instance{
46 token: token,
47 ledger: ledger,
48 admin: ownable.NewWithAddress(admin),
49 faucet: faucet,
50 }
51 instances.Set(symbol, &inst)
52
53 grc20reg.Register(cross, token, symbol)
54}
55
56func Bank(symbol string) *grc20.Token {
57 inst := mustGetInstance(symbol)
58 return inst.token
59}
60
61func TotalSupply(symbol string) int64 {
62 inst := mustGetInstance(symbol)
63 return inst.token.ReadonlyTeller().TotalSupply()
64}
65
66func HasAddr(symbol string, owner std.Address) bool {
67 inst := mustGetInstance(symbol)
68 return inst.token.HasAddr(owner)
69}
70
71func BalanceOf(symbol string, owner std.Address) int64 {
72 inst := mustGetInstance(symbol)
73 return inst.token.ReadonlyTeller().BalanceOf(owner)
74}
75
76func Allowance(symbol string, owner, spender std.Address) int64 {
77 inst := mustGetInstance(symbol)
78 return inst.token.ReadonlyTeller().Allowance(owner, spender)
79}
80
81func Transfer(cur realm, symbol string, to std.Address, amount int64) {
82 inst := mustGetInstance(symbol)
83 caller := std.PreviousRealm().Address()
84 teller := inst.ledger.ImpersonateTeller(caller)
85 checkErr(teller.Transfer(to, amount))
86}
87
88func Approve(cur realm, symbol string, spender std.Address, amount int64) {
89 inst := mustGetInstance(symbol)
90 caller := std.PreviousRealm().Address()
91 teller := inst.ledger.ImpersonateTeller(caller)
92 checkErr(teller.Approve(spender, amount))
93}
94
95func TransferFrom(cur realm, symbol string, from, to std.Address, amount int64) {
96 inst := mustGetInstance(symbol)
97 caller := std.PreviousRealm().Address()
98 teller := inst.ledger.ImpersonateTeller(caller)
99 checkErr(teller.TransferFrom(from, to, amount))
100}
101
102// faucet.
103func Faucet(cur realm, symbol string) {
104 inst := mustGetInstance(symbol)
105 if inst.faucet == 0 {
106 panic("faucet disabled for this token")
107 }
108 // FIXME: add limits?
109 // FIXME: add payment in gnot?
110 caller := std.PreviousRealm().Address()
111 checkErr(inst.ledger.Mint(caller, inst.faucet))
112}
113
114func Mint(cur realm, symbol string, to std.Address, amount int64) {
115 inst := mustGetInstance(symbol)
116 inst.admin.AssertOwnedByPrevious()
117 checkErr(inst.ledger.Mint(to, amount))
118}
119
120func Burn(cur realm, symbol string, from std.Address, amount int64) {
121 inst := mustGetInstance(symbol)
122 inst.admin.AssertOwnedByPrevious()
123 checkErr(inst.ledger.Burn(from, amount))
124}
125
126// instance admin functionality
127func DropInstanceOwnership(cur realm, symbol string) {
128 inst := mustGetInstance(symbol)
129 checkErr(inst.admin.DropOwnershipByCurrent())
130}
131
132func TransferInstanceOwnership(cur realm, symbol string, newOwner std.Address) {
133 inst := mustGetInstance(symbol)
134 checkErr(inst.admin.TransferOwnership(newOwner))
135}
136
137func ListTokens(pageNumber, pageSize int) []*grc20.Token {
138 page := pager.GetPageWithSize(pageNumber, pageSize)
139
140 tokens := make([]*grc20.Token, len(page.Items))
141 for i := range page.Items {
142 tokens[i] = page.Items[i].Value.(*instance).token
143 }
144
145 return tokens
146}
147
148func Render(path string) string {
149 router := mux.NewRouter()
150 router.HandleFunc("", renderHome)
151 router.HandleFunc("{symbol}", renderToken)
152 router.HandleFunc("{symbol}/balance/{address}", renderBalance)
153 return router.Render(path)
154}
155
156func renderHome(res *mux.ResponseWriter, req *mux.Request) {
157 out := md.H1(ufmt.Sprintf("GRC20 Tokens (%d)", instances.Size()))
158
159 // Get the current page of tokens based on the request path.
160 page := pager.MustGetPageByPath(req.RawPath)
161
162 // Render the list of tokens.
163 for _, item := range page.Items {
164 token := item.Value.(*instance).token
165 out += md.BulletItem(
166 md.Link(
167 ufmt.Sprintf("%s ($%s)", token.GetName(), token.GetSymbol()),
168 ufmt.Sprintf("/r/demo/grc20factory:%s", token.GetSymbol()),
169 ),
170 )
171 }
172 out += "\n"
173
174 // Add the page picker.
175 out += md.Paragraph(page.Picker(req.Path))
176
177 res.Write(out)
178}
179
180func renderToken(res *mux.ResponseWriter, req *mux.Request) {
181 // Get the token symbol from the request.
182 symbol := req.GetVar("symbol")
183 inst := mustGetInstance(symbol)
184
185 // Render the token details.
186 out := inst.token.RenderHome()
187 out += md.BulletItem(
188 ufmt.Sprintf("%s: %s", md.Bold("Admin"), inst.admin.Owner()),
189 )
190
191 res.Write(out)
192}
193
194func renderBalance(res *mux.ResponseWriter, req *mux.Request) {
195 var (
196 symbol = req.GetVar("symbol")
197 addr = req.GetVar("address")
198 )
199
200 // Get the balance of the specified address for the token.
201 inst := mustGetInstance(symbol)
202 balance := inst.token.CallerTeller().BalanceOf(std.Address(addr))
203
204 // Render the balance information.
205 out := md.Paragraph(
206 ufmt.Sprintf("%s balance: %d", md.Bold(addr), balance),
207 )
208
209 res.Write(out)
210}
211
212func mustGetInstance(symbol string) *instance {
213 t, exists := instances.Get(symbol)
214 if !exists {
215 panic("token instance does not exist")
216 }
217 return t.(*instance)
218}
219
220func checkErr(err error) {
221 if err != nil {
222 panic(err.Error())
223 }
224}