grc20factory.gno

4.36 Kb ยท 195 lines
  1package foo20
  2
  3import (
  4	"std"
  5	"strings"
  6
  7	"gno.land/p/demo/avl"
  8	"gno.land/p/demo/grc/grc20"
  9	"gno.land/p/demo/ownable"
 10	"gno.land/p/demo/ufmt"
 11	"gno.land/r/demo/grc20reg"
 12)
 13
 14var instances avl.Tree // symbol -> instance
 15
 16type instance struct {
 17	token  *grc20.Token
 18	ledger *grc20.PrivateLedger
 19	admin  *ownable.Ownable
 20	faucet int64 // per-request amount. disabled if 0.
 21}
 22
 23func New(name, symbol string, decimals int, initialMint, faucet int64) {
 24	crossing()
 25
 26	caller := std.PreviousRealm().Address()
 27	NewWithAdmin(name, symbol, decimals, initialMint, faucet, caller)
 28}
 29
 30func NewWithAdmin(name, symbol string, decimals int, initialMint, faucet int64, admin std.Address) {
 31	crossing()
 32
 33	exists := instances.Has(symbol)
 34	if exists {
 35		panic("token already exists")
 36	}
 37
 38	token, ledger := grc20.NewToken(name, symbol, decimals)
 39	if initialMint > 0 {
 40		ledger.Mint(admin, initialMint)
 41	}
 42
 43	inst := instance{
 44		token:  token,
 45		ledger: ledger,
 46		admin:  ownable.NewWithAddress(admin),
 47		faucet: faucet,
 48	}
 49
 50	// XXX: Workaround to keep ownership of the token in the current Realm
 51	// before passing a pointer to the grc20reg.Register function.
 52	// See #4274.
 53	cross(func() {
 54		crossing()
 55		instances.Set(symbol, &inst)
 56	})()
 57
 58	cross(grc20reg.Register)(token, symbol)
 59}
 60
 61func (inst instance) Token() *grc20.Token {
 62	return inst.token
 63}
 64
 65func (inst instance) CallerTeller() grc20.Teller {
 66	return inst.token.CallerTeller()
 67}
 68
 69func Bank(symbol string) *grc20.Token {
 70	inst := mustGetInstance(symbol)
 71	return inst.token
 72}
 73
 74func TotalSupply(symbol string) int64 {
 75	inst := mustGetInstance(symbol)
 76	return inst.token.ReadonlyTeller().TotalSupply()
 77}
 78
 79func BalanceOf(symbol string, owner std.Address) int64 {
 80	inst := mustGetInstance(symbol)
 81	return inst.token.ReadonlyTeller().BalanceOf(owner)
 82}
 83
 84func Allowance(symbol string, owner, spender std.Address) int64 {
 85	inst := mustGetInstance(symbol)
 86	return inst.token.ReadonlyTeller().Allowance(owner, spender)
 87}
 88
 89func Transfer(symbol string, to std.Address, amount int64) {
 90	crossing()
 91
 92	inst := mustGetInstance(symbol)
 93	caller := std.PreviousRealm().Address()
 94	teller := inst.ledger.ImpersonateTeller(caller)
 95	checkErr(teller.Transfer(to, amount))
 96}
 97
 98func Approve(symbol string, spender std.Address, amount int64) {
 99	crossing()
100
101	inst := mustGetInstance(symbol)
102	caller := std.PreviousRealm().Address()
103	teller := inst.ledger.ImpersonateTeller(caller)
104	checkErr(teller.Approve(spender, amount))
105}
106
107func TransferFrom(symbol string, from, to std.Address, amount int64) {
108	crossing()
109
110	inst := mustGetInstance(symbol)
111	caller := std.PreviousRealm().Address()
112	teller := inst.ledger.ImpersonateTeller(caller)
113	checkErr(teller.TransferFrom(from, to, amount))
114}
115
116// faucet.
117func Faucet(symbol string) {
118	crossing()
119
120	inst := mustGetInstance(symbol)
121	if inst.faucet == 0 {
122		panic("faucet disabled for this token")
123	}
124	// FIXME: add limits?
125	// FIXME: add payment in gnot?
126	caller := std.PreviousRealm().Address()
127	checkErr(inst.ledger.Mint(caller, inst.faucet))
128}
129
130func Mint(symbol string, to std.Address, amount int64) {
131	crossing()
132
133	inst := mustGetInstance(symbol)
134	inst.admin.AssertOwnedByPrevious()
135	checkErr(inst.ledger.Mint(to, amount))
136}
137
138func Burn(symbol string, from std.Address, amount int64) {
139	crossing()
140
141	inst := mustGetInstance(symbol)
142	inst.admin.AssertOwnedByPrevious()
143	checkErr(inst.ledger.Burn(from, amount))
144}
145
146// instance admin functionality
147func DropInstanceOwnership(symbol string) {
148	crossing()
149
150	inst := mustGetInstance(symbol)
151	checkErr(inst.admin.DropOwnershipByCurrent())
152}
153
154func TransferInstanceOwnership(symbol string, newOwner std.Address) {
155	crossing()
156
157	inst := mustGetInstance(symbol)
158	checkErr(inst.admin.TransferOwnership(newOwner))
159}
160
161func Render(path string) string {
162	parts := strings.Split(path, "/")
163	c := len(parts)
164
165	switch {
166	case path == "":
167		return "TODO: list existing tokens and admins"
168	case c == 1:
169		symbol := parts[0]
170		inst := mustGetInstance(symbol)
171		return inst.token.RenderHome()
172	case c == 3 && parts[1] == "balance":
173		symbol := parts[0]
174		inst := mustGetInstance(symbol)
175		owner := std.Address(parts[2])
176		balance := inst.token.CallerTeller().BalanceOf(owner)
177		return ufmt.Sprintf("%d", balance)
178	default:
179		return "404\n"
180	}
181}
182
183func mustGetInstance(symbol string) *instance {
184	t, exists := instances.Get(symbol)
185	if !exists {
186		panic("token instance does not exist")
187	}
188	return t.(*instance)
189}
190
191func checkErr(err error) {
192	if err != nil {
193		panic(err.Error())
194	}
195}