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}