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  }