1package grc20
2
3import (
4 "math/overflow"
5 "std"
6 "strconv"
7
8 "gno.land/p/demo/ufmt"
9)
10
11// NewToken creates a new Token.
12// It returns a pointer to the Token and a pointer to the Ledger.
13// Expected usage: Token, admin := NewToken("Dummy", "DUMMY", 4)
14func NewToken(name, symbol string, decimals uint) (*Token, *PrivateLedger) {
15 if name == "" {
16 panic("name should not be empty")
17 }
18 if symbol == "" {
19 panic("symbol should not be empty")
20 }
21 // XXX additional checks (length, characters, limits, etc)
22
23 ledger := &PrivateLedger{}
24 token := &Token{
25 name: name,
26 symbol: symbol,
27 decimals: decimals,
28 ledger: ledger,
29 }
30 ledger.token = token
31 return token, ledger
32}
33
34// GetName returns the name of the token.
35func (tok Token) GetName() string { return tok.name }
36
37// GetSymbol returns the symbol of the token.
38func (tok Token) GetSymbol() string { return tok.symbol }
39
40// GetDecimals returns the number of decimals used to get the token's precision.
41func (tok Token) GetDecimals() uint { return tok.decimals }
42
43// TotalSupply returns the total supply of the token.
44func (tok Token) TotalSupply() uint64 { return tok.ledger.totalSupply }
45
46// KnownAccounts returns the number of known accounts in the bank.
47func (tok Token) KnownAccounts() int { return tok.ledger.balances.Size() }
48
49// BalanceOf returns the balance of the specified address.
50func (tok Token) BalanceOf(address std.Address) uint64 {
51 return tok.ledger.balanceOf(address)
52}
53
54// Allowance returns the allowance of the specified owner and spender.
55func (tok Token) Allowance(owner, spender std.Address) uint64 {
56 return tok.ledger.allowance(owner, spender)
57}
58
59func (tok *Token) RenderHome() string {
60 str := ""
61 str += ufmt.Sprintf("# %s ($%s)\n\n", tok.name, tok.symbol)
62 str += ufmt.Sprintf("* **Decimals**: %d\n", tok.decimals)
63 str += ufmt.Sprintf("* **Total supply**: %d\n", tok.ledger.totalSupply)
64 str += ufmt.Sprintf("* **Known accounts**: %d\n", tok.KnownAccounts())
65 return str
66}
67
68// Getter returns a TokenGetter function that returns this token. This allows
69// storing indirect pointers to a token in a remote realm.
70func (tok *Token) Getter() TokenGetter {
71 return func() *Token {
72 return tok
73 }
74}
75
76// SpendAllowance decreases the allowance of the specified owner and spender.
77func (led *PrivateLedger) SpendAllowance(owner, spender std.Address, amount uint64) error {
78 if !owner.IsValid() {
79 return ErrInvalidAddress
80 }
81 if !spender.IsValid() {
82 return ErrInvalidAddress
83 }
84
85 currentAllowance := led.allowance(owner, spender)
86 if currentAllowance < amount {
87 return ErrInsufficientAllowance
88 }
89
90 key := allowanceKey(owner, spender)
91 newAllowance := currentAllowance - amount
92
93 if newAllowance == 0 {
94 led.allowances.Remove(key)
95 } else {
96 led.allowances.Set(key, newAllowance)
97 }
98
99 return nil
100}
101
102// Transfer transfers tokens from the specified from address to the specified to address.
103func (led *PrivateLedger) Transfer(from, to std.Address, amount uint64) error {
104 if !from.IsValid() {
105 return ErrInvalidAddress
106 }
107 if !to.IsValid() {
108 return ErrInvalidAddress
109 }
110 if from == to {
111 return ErrCannotTransferToSelf
112 }
113
114 var (
115 toBalance = led.balanceOf(to)
116 fromBalance = led.balanceOf(from)
117 )
118
119 if fromBalance < amount {
120 return ErrInsufficientBalance
121 }
122
123 var (
124 newToBalance = toBalance + amount
125 newFromBalance = fromBalance - amount
126 )
127
128 led.balances.Set(string(to), newToBalance)
129 led.balances.Set(string(from), newFromBalance)
130
131 std.Emit(
132 TransferEvent,
133 "from", from.String(),
134 "to", to.String(),
135 "value", strconv.Itoa(int(amount)),
136 )
137
138 return nil
139}
140
141// TransferFrom transfers tokens from the specified owner to the specified to address.
142// It first checks if the owner has sufficient balance and then decreases the allowance.
143func (led *PrivateLedger) TransferFrom(owner, spender, to std.Address, amount uint64) error {
144 if led.balanceOf(owner) < amount {
145 return ErrInsufficientBalance
146 }
147 if err := led.SpendAllowance(owner, spender, amount); err != nil {
148 return err
149 }
150 // XXX: since we don't "panic", we should take care of rollbacking spendAllowance if transfer fails.
151 return led.Transfer(owner, to, amount)
152}
153
154// Approve sets the allowance of the specified owner and spender.
155func (led *PrivateLedger) Approve(owner, spender std.Address, amount uint64) error {
156 if !owner.IsValid() || !spender.IsValid() {
157 return ErrInvalidAddress
158 }
159
160 led.allowances.Set(allowanceKey(owner, spender), amount)
161
162 std.Emit(
163 ApprovalEvent,
164 "owner", string(owner),
165 "spender", string(spender),
166 "value", strconv.Itoa(int(amount)),
167 )
168
169 return nil
170}
171
172// Mint increases the total supply of the token and adds the specified amount to the specified address.
173func (led *PrivateLedger) Mint(address std.Address, amount uint64) error {
174 if !address.IsValid() {
175 return ErrInvalidAddress
176 }
177
178 // XXX: math/overflow is not supporting uint64.
179 // This checks prevents overflow but makes the totalSupply limited to a uint63.
180 sum, ok := overflow.Add64(int64(led.totalSupply), int64(amount))
181 if !ok {
182 return ErrOverflow
183 }
184
185 led.totalSupply = uint64(sum)
186 currentBalance := led.balanceOf(address)
187 newBalance := currentBalance + amount
188
189 led.balances.Set(string(address), newBalance)
190
191 std.Emit(
192 TransferEvent,
193 "from", "",
194 "to", string(address),
195 "value", strconv.Itoa(int(amount)),
196 )
197
198 return nil
199}
200
201// Burn decreases the total supply of the token and subtracts the specified amount from the specified address.
202func (led *PrivateLedger) Burn(address std.Address, amount uint64) error {
203 if !address.IsValid() {
204 return ErrInvalidAddress
205 }
206
207 currentBalance := led.balanceOf(address)
208 if currentBalance < amount {
209 return ErrInsufficientBalance
210 }
211
212 led.totalSupply -= amount
213 newBalance := currentBalance - amount
214
215 led.balances.Set(string(address), newBalance)
216
217 std.Emit(
218 TransferEvent,
219 "from", string(address),
220 "to", "",
221 "value", strconv.Itoa(int(amount)),
222 )
223
224 return nil
225}
226
227// balanceOf returns the balance of the specified address.
228func (led PrivateLedger) balanceOf(address std.Address) uint64 {
229 balance, found := led.balances.Get(address.String())
230 if !found {
231 return 0
232 }
233 return balance.(uint64)
234}
235
236// allowance returns the allowance of the specified owner and spender.
237func (led PrivateLedger) allowance(owner, spender std.Address) uint64 {
238 allowance, found := led.allowances.Get(allowanceKey(owner, spender))
239 if !found {
240 return 0
241 }
242 return allowance.(uint64)
243}
244
245// allowanceKey returns the key for the allowance of the specified owner and spender.
246func allowanceKey(owner, spender std.Address) string {
247 return owner.String() + ":" + spender.String()
248}
token.gno
6.45 Kb ยท 248 lines