Search Apps Documentation Source Content File Folder Download Copy

banker.gno

4.77 Kb ยท 215 lines
  1package grc20
  2
  3import (
  4	"std"
  5	"strconv"
  6
  7	"gno.land/p/demo/avl"
  8	"gno.land/p/demo/ufmt"
  9)
 10
 11// Banker implements a token banker with admin privileges.
 12//
 13// The Banker is intended to be used in two main ways:
 14//  1. as a temporary object used to make the initial minting, then deleted.
 15//  2. preserved in an unexported variable to support conditional administrative
 16//     tasks protected by the contract.
 17type Banker struct {
 18	name        string
 19	symbol      string
 20	decimals    uint
 21	totalSupply uint64
 22	balances    avl.Tree // std.Address(owner) -> uint64
 23	allowances  avl.Tree // string(owner+":"+spender) -> uint64
 24	token       *token   // to share the same pointer
 25}
 26
 27func NewBanker(name, symbol string, decimals uint) *Banker {
 28	if name == "" {
 29		panic("name should not be empty")
 30	}
 31	if symbol == "" {
 32		panic("symbol should not be empty")
 33	}
 34	// XXX additional checks (length, characters, limits, etc)
 35
 36	b := Banker{
 37		name:     name,
 38		symbol:   symbol,
 39		decimals: decimals,
 40	}
 41	t := &token{banker: &b}
 42	b.token = t
 43	return &b
 44}
 45
 46func (b Banker) Token() Token        { return b.token } // Token returns a grc20 safe-object implementation.
 47func (b Banker) GetName() string     { return b.name }
 48func (b Banker) GetSymbol() string   { return b.symbol }
 49func (b Banker) GetDecimals() uint   { return b.decimals }
 50func (b Banker) TotalSupply() uint64 { return b.totalSupply }
 51func (b Banker) KnownAccounts() int  { return b.balances.Size() }
 52
 53func (b *Banker) Mint(address std.Address, amount uint64) error {
 54	if !address.IsValid() {
 55		return ErrInvalidAddress
 56	}
 57
 58	// TODO: check for overflow
 59
 60	b.totalSupply += amount
 61	currentBalance := b.BalanceOf(address)
 62	newBalance := currentBalance + amount
 63
 64	b.balances.Set(string(address), newBalance)
 65
 66	std.Emit(
 67		MintEvent,
 68		"from", "",
 69		"to", string(address),
 70		"value", strconv.Itoa(int(amount)),
 71	)
 72
 73	return nil
 74}
 75
 76func (b *Banker) Burn(address std.Address, amount uint64) error {
 77	if !address.IsValid() {
 78		return ErrInvalidAddress
 79	}
 80	// TODO: check for overflow
 81
 82	currentBalance := b.BalanceOf(address)
 83	if currentBalance < amount {
 84		return ErrInsufficientBalance
 85	}
 86
 87	b.totalSupply -= amount
 88	newBalance := currentBalance - amount
 89
 90	b.balances.Set(string(address), newBalance)
 91
 92	std.Emit(
 93		BurnEvent,
 94		"from", string(address),
 95		"to", "",
 96		"value", strconv.Itoa(int(amount)),
 97	)
 98
 99	return nil
100}
101
102func (b Banker) BalanceOf(address std.Address) uint64 {
103	balance, found := b.balances.Get(address.String())
104	if !found {
105		return 0
106	}
107	return balance.(uint64)
108}
109
110func (b *Banker) SpendAllowance(owner, spender std.Address, amount uint64) error {
111	if !owner.IsValid() {
112		return ErrInvalidAddress
113	}
114	if !spender.IsValid() {
115		return ErrInvalidAddress
116	}
117
118	currentAllowance := b.Allowance(owner, spender)
119	if currentAllowance < amount {
120		return ErrInsufficientAllowance
121	}
122
123	key := allowanceKey(owner, spender)
124	newAllowance := currentAllowance - amount
125
126	if newAllowance == 0 {
127		b.allowances.Remove(key)
128	} else {
129		b.allowances.Set(key, newAllowance)
130	}
131
132	return nil
133}
134
135func (b *Banker) Transfer(from, to std.Address, amount uint64) error {
136	if !from.IsValid() {
137		return ErrInvalidAddress
138	}
139	if !to.IsValid() {
140		return ErrInvalidAddress
141	}
142	if from == to {
143		return ErrCannotTransferToSelf
144	}
145
146	toBalance := b.BalanceOf(to)
147	fromBalance := b.BalanceOf(from)
148
149	if fromBalance < amount {
150		return ErrInsufficientBalance
151	}
152
153	newToBalance := toBalance + amount
154	newFromBalance := fromBalance - amount
155
156	b.balances.Set(string(to), newToBalance)
157	b.balances.Set(string(from), newFromBalance)
158
159	std.Emit(
160		TransferEvent,
161		"from", from.String(),
162		"to", to.String(),
163		"value", strconv.Itoa(int(amount)),
164	)
165
166	return nil
167}
168
169func (b *Banker) TransferFrom(spender, from, to std.Address, amount uint64) error {
170	if err := b.SpendAllowance(from, spender, amount); err != nil {
171		return err
172	}
173	return b.Transfer(from, to, amount)
174}
175
176func (b *Banker) Allowance(owner, spender std.Address) uint64 {
177	allowance, found := b.allowances.Get(allowanceKey(owner, spender))
178	if !found {
179		return 0
180	}
181	return allowance.(uint64)
182}
183
184func (b *Banker) Approve(owner, spender std.Address, amount uint64) error {
185	if !owner.IsValid() {
186		return ErrInvalidAddress
187	}
188	if !spender.IsValid() {
189		return ErrInvalidAddress
190	}
191
192	b.allowances.Set(allowanceKey(owner, spender), amount)
193
194	std.Emit(
195		ApprovalEvent,
196		"owner", string(owner),
197		"spender", string(spender),
198		"value", strconv.Itoa(int(amount)),
199	)
200
201	return nil
202}
203
204func (b *Banker) RenderHome() string {
205	str := ""
206	str += ufmt.Sprintf("# %s ($%s)\n\n", b.name, b.symbol)
207	str += ufmt.Sprintf("* **Decimals**: %d\n", b.decimals)
208	str += ufmt.Sprintf("* **Total supply**: %d\n", b.totalSupply)
209	str += ufmt.Sprintf("* **Known accounts**: %d\n", b.KnownAccounts())
210	return str
211}
212
213func allowanceKey(owner, spender std.Address) string {
214	return owner.String() + ":" + spender.String()
215}