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}