github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/examples/gno.land/p/demo/grc/grc20/admin_token.gno (about) 1 package grc20 2 3 import ( 4 "std" 5 6 "gno.land/p/demo/avl" 7 "gno.land/p/demo/ufmt" 8 ) 9 10 // AdminToken implements a token factory with admin helpers. 11 // 12 // Warning: you should not expose this struct to enduser directly. 13 // 14 // It allows token administrators to call privileged helpers 15 // like Mint, Burn, or any Transfer helpers by passing custom owners. 16 // 17 // You should initialize your token, then call AdminToken.SafeGRC20() to 18 // expose a safe instance to the endusers. 19 type AdminToken struct { 20 name string 21 symbol string 22 decimals uint 23 totalSupply uint64 24 balances avl.Tree // std.Address(owner) -> uint64 25 allowances avl.Tree // string(owner+":"+spender) -> uint64 26 } 27 28 // safeToken implements the IGRC20 interface. 29 // 30 // It is generated by AdminToken.SafeGRC20(). 31 // It can safely be explosed publicly. 32 type safeToken struct { 33 IGRC20 // implements the GRC20 interface. 34 35 factory *AdminToken 36 } 37 38 func NewAdminToken(name, symbol string, decimals uint) *AdminToken { 39 // FIXME: check for limits 40 41 return &AdminToken{ 42 name: name, 43 symbol: symbol, 44 decimals: decimals, 45 46 balances: avl.Tree{}, 47 allowances: avl.Tree{}, 48 } 49 } 50 51 func (t *AdminToken) GetName() string { return t.name } 52 func (t *AdminToken) GetSymbol() string { return t.symbol } 53 func (t *AdminToken) GetDecimals() uint { return t.decimals } 54 func (t *AdminToken) TotalSupply() uint64 { return t.totalSupply } 55 56 func (t *AdminToken) BalanceOf(owner std.Address) (uint64, error) { 57 return t.balanceOf(owner) 58 } 59 60 func (t *AdminToken) Transfer(owner, to std.Address, amount uint64) error { 61 return t.transfer(owner, to, amount) 62 } 63 64 func (t *AdminToken) Allowance(owner, spender std.Address) (uint64, error) { 65 return t.allowance(owner, spender) 66 } 67 68 func (t *AdminToken) Approve(owner, spender std.Address, amount uint64) error { 69 return t.approve(owner, spender, amount) 70 } 71 72 func (t *AdminToken) TransferFrom(spender, from, to std.Address, amount uint64) error { 73 if err := t.spendAllowance(from, spender, amount); err != nil { 74 return err 75 } 76 return t.transfer(from, to, amount) 77 } 78 79 // Administration helpers implementation. 80 // 81 82 func (t *AdminToken) Mint(to std.Address, amount uint64) error { 83 return t.mint(to, amount) 84 } 85 86 func (t *AdminToken) Burn(from std.Address, amount uint64) error { 87 return t.burn(from, amount) 88 } 89 90 // private helpers 91 // 92 93 func (t *AdminToken) mint(address std.Address, amount uint64) error { 94 if err := checkIsValidAddress(address); err != nil { 95 return err 96 } 97 98 // TODO: check for overflow 99 100 t.totalSupply += amount 101 currentBalance, err := t.balanceOf(address) 102 if err != nil { 103 return err 104 } 105 newBalance := currentBalance + amount 106 107 t.balances.Set(string(address), newBalance) 108 109 event := TransferEvent{zeroAddress, address, amount} 110 emit(&event) 111 112 return nil 113 } 114 115 func (t *AdminToken) burn(address std.Address, amount uint64) error { 116 if err := checkIsValidAddress(address); err != nil { 117 return err 118 } 119 // TODO: check for overflow 120 121 currentBalance, err := t.balanceOf(address) 122 if err != nil { 123 return err 124 } 125 if currentBalance < amount { 126 return ErrInsufficientBalance 127 } 128 129 t.totalSupply -= amount 130 newBalance := currentBalance - amount 131 132 t.balances.Set(string(address), newBalance) 133 134 event := TransferEvent{address, zeroAddress, amount} 135 emit(&event) 136 137 return nil 138 } 139 140 func (t *AdminToken) balanceOf(address std.Address) (uint64, error) { 141 if err := checkIsValidAddress(address); err != nil { 142 return 0, err 143 } 144 145 balance, found := t.balances.Get(address.String()) 146 if !found { 147 return 0, nil 148 } 149 return balance.(uint64), nil 150 } 151 152 func (t *AdminToken) spendAllowance(owner, spender std.Address, amount uint64) error { 153 if err := checkIsValidAddress(owner); err != nil { 154 return err 155 } 156 if err := checkIsValidAddress(spender); err != nil { 157 return err 158 } 159 160 currentAllowance, err := t.allowance(owner, spender) 161 if err != nil { 162 return err 163 } 164 if currentAllowance < amount { 165 return ErrInsufficientAllowance 166 } 167 168 key := allowanceKey(owner, spender) 169 if currentAllowance > amount { 170 t.allowances.Set(key, currentAllowance-amount) 171 } else { 172 t.allowances.Remove(key) 173 } 174 175 return nil 176 } 177 178 func (t *AdminToken) transfer(from, to std.Address, amount uint64) error { 179 if err := checkIsValidAddress(from); err != nil { 180 return err 181 } 182 if err := checkIsValidAddress(to); err != nil { 183 return err 184 } 185 186 if from == to { 187 return ErrCannotTransferToSelf 188 } 189 190 toBalance, err := t.balanceOf(to) 191 if err != nil { 192 return err 193 } 194 fromBalance, err := t.balanceOf(from) 195 if err != nil { 196 return err 197 } 198 199 // debug. 200 // println("from", from, "to", to, "amount", amount, "fromBalance", fromBalance, "toBalance", toBalance) 201 202 if fromBalance < amount { 203 return ErrInsufficientBalance 204 } 205 206 newToBalance := toBalance + amount 207 newFromBalance := fromBalance - amount 208 209 t.balances.Set(string(to), newToBalance) 210 t.balances.Set(string(from), newFromBalance) 211 212 event := TransferEvent{from, to, amount} 213 emit(&event) 214 215 return nil 216 } 217 218 func (t *AdminToken) allowance(owner, spender std.Address) (uint64, error) { 219 if err := checkIsValidAddress(owner); err != nil { 220 return 0, err 221 } 222 if err := checkIsValidAddress(spender); err != nil { 223 return 0, err 224 } 225 226 allowance, found := t.allowances.Get(allowanceKey(owner, spender)) 227 if !found { 228 return 0, nil 229 } 230 231 return allowance.(uint64), nil 232 } 233 234 func (t *AdminToken) approve(owner, spender std.Address, amount uint64) error { 235 if err := checkIsValidAddress(owner); err != nil { 236 return err 237 } 238 if err := checkIsValidAddress(spender); err != nil { 239 return err 240 } 241 242 t.allowances.Set(allowanceKey(owner, spender), amount) 243 244 event := ApprovalEvent{owner, spender, amount} 245 emit(&event) 246 247 return nil 248 } 249 250 func allowanceKey(owner, spender std.Address) string { 251 return owner.String() + ":" + spender.String() 252 } 253 254 func (t *AdminToken) RenderHome() string { 255 str := "" 256 str += ufmt.Sprintf("# %s ($%s)\n\n", t.name, t.symbol) 257 str += ufmt.Sprintf("* **Decimals**: %d\n", t.decimals) 258 str += ufmt.Sprintf("* **Total supply**: %d\n", t.totalSupply) 259 str += ufmt.Sprintf("* **Known accounts**: %d\n", t.balances.Size()) 260 return str 261 } 262 263 // GRC20 returns an instance that can be exposed to the end user. 264 func (t *AdminToken) GRC20() IGRC20 { 265 return &userToken{admin: t} 266 }