github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/docs/how-to-guides/creating-grc20.md (about)

     1  ---
     2  id: creating-grc20
     3  ---
     4  
     5  # How to Create a GRC20 Token
     6  ## Overview
     7  
     8  This guide shows you how to write a simple **GRC20**
     9  a [Realm](../concepts/realms.md), in [Gno](../concepts/gno-language.md). For actually deploying the Realm, please see the
    10  [deployment](deploy.md) guide.
    11  
    12  Our **GRC20** Realm will have the following functionality:
    13  
    14  - Minting a configurable amount of token.
    15  - Keeping track of total token supply.
    16  - Fetching the balance of an account.
    17  
    18  ## 1. Importing token package
    19  For this realm, we import the `grc20` package, as this includes
    20  the main functionality of our token realm. The package can be found the 
    21  `gno.land/p/demo/grc/grc20` path.
    22  
    23  [embedmd]:# (../assets/how-to-guides/creating-grc20/mytoken-1.gno go)
    24  ```go
    25  package mytoken
    26  
    27  import (
    28  	"std"
    29  	"strings"
    30  
    31  	"gno.land/p/demo/grc/grc20"
    32  	"gno.land/p/demo/ufmt"
    33  )
    34  
    35  var (
    36  	mytoken *grc20.AdminToken
    37  	admin   std.Address
    38  )
    39  
    40  // init is called once at time of deployment
    41  func init() {
    42  	// Set deployer of Realm to admin
    43  	admin = std.PrevRealm().Addr()
    44  
    45  	// Set token name, symbol and number of decimals
    46  	mytoken = grc20.NewAdminToken("My Token", "TKN", 4)
    47  
    48  	// Mint 1 million tokens to admin
    49  	mytoken.Mint(admin, 1000000*10000)
    50  }
    51  ```
    52  
    53  The code snippet above does the following:
    54  - Defines a new token variable, `mytoken`, and assigns it to a
    55  pointer to the GRC20 token type, `grc20.AdminToken`,
    56  - Defines and sets the value of `admin` with a type of `std.Address` to contain 
    57  the address of the deployer
    58  - Initializes `mytoken` as a new GRC20 token, and set its name, symbol, and 
    59  decimal values,
    60  - Mint 1 million units of `My Token` and assign them to the admin's address.
    61  
    62  ## 2. Adding token functionality
    63  
    64  In order to call exported functions from the `grc20` package, we also need to 
    65  expose them in the realm. Let's go through all functions in the GRC20 package,
    66  one by one:
    67  
    68  ```go
    69  // TotalSupply returns the total supply of mytoken
    70  func TotalSupply() uint64 {
    71  	return mytoken.TotalSupply()
    72  }
    73  
    74  ```
    75  Calling the `TotalSupply` method would return the total number of tokens minted.
    76  
    77  ```go
    78  // Decimals returns the number of decimals of mytoken
    79  func Decimals() uint {
    80  	return mytoken.GetDecimals()
    81  }
    82  ```
    83  Calling the `Decimals` method would return number of decimals of the token.
    84  
    85  ```go
    86  // BalanceOf returns the balance mytoken for `account`
    87  func BalanceOf(account std.Address) uint64 {
    88  	balance, err := mytoken.BalanceOf(account)
    89  	if err != nil {
    90  		panic(err)
    91  	}
    92  
    93  	return balance
    94  }
    95  ```
    96  
    97  Calling the `BalanceOf` method would return the total balance of an account.
    98  
    99  ```go
   100  // Allowance returns the allowance of spender on owner's balance
   101  func Allowance(owner, spender std.Address) uint64 {
   102  	allowance, err := mytoken.Allowance(owner, spender)
   103  	if err != nil {
   104  		panic(err)
   105  	}
   106  
   107  	return allowance
   108  }
   109  ```
   110  Calling the `Allowance` method will return the amount `spender` is allowed to spend
   111  from `owner`'s balance.
   112  
   113  ```go
   114  // Transfer transfers amount from caller to recipient
   115  func Transfer(recipient std.Address, amount uint64) {
   116  	caller := std.PrevRealm().Addr()
   117  	if err := mytoken.Transfer(caller, recipient, amount); err != nil {
   118  		panic(err)
   119  	}
   120  }
   121  ```
   122  Calling the `Transfer` method transfers amount of token from the calling account 
   123  to the recipient account.
   124  
   125  ```go
   126  func Approve(spender std.Address, amount uint64) {
   127  	caller := std.PrevRealm().Addr()
   128  	if err := mytoken.Approve(caller, spender, amount); err != nil {
   129  		panic(err)
   130  	}
   131  }
   132  ```
   133  Calling the `Approve` method approves `spender` to spend `amount` from the caller's
   134  balance of tokens.
   135  
   136  ```go
   137  // TransferFrom transfers `amount` of tokens from `from` to `to` 
   138  func TransferFrom(sender, recipient std.Address, amount uint64) {
   139  	caller := std.PrevRealm().Addr()
   140  
   141  	if amount <= 0 {
   142  		panic("transfer amount must be greater than zero")
   143  	}
   144  
   145  	if err := mytoken.TransferFrom(caller, from, to, amount); err != nil {
   146  		panic(err)
   147  	}
   148  }
   149  ```
   150  Calling the `TransferFrom` method moves `amount` of tokens from `sender` to 
   151  `recipient` using the allowance mechanism. `amount` is then deducted from the
   152  caller’s allowance.
   153  
   154  ```go
   155  // Mint mints amount of tokens to address. Callable only by admin of token
   156  func Mint(address std.Address, amount uint64) {
   157  	assertIsAdmin(std.PrevRealm().Addr())
   158  
   159  	if amount <= 0 {
   160  		panic("mint amount must be greater than zero")
   161  	}
   162  
   163  	if err := mytoken.Mint(address, amount); err != nil {
   164  		panic(err)
   165  	}
   166  }
   167  ```
   168  Calling the `Mint` method creates `amount` of tokens and assigns them to `address`,
   169  increasing the total supply.
   170  
   171  ```go
   172  // Burn burns amount of tokens from address. Callable only by admin of token
   173  func Burn(address std.Address, amount uint64) {
   174  	assertIsAdmin(std.PrevRealm().Addr())
   175  
   176  	if amount <= 0 {
   177  		panic("burn amount must be greater than zero")
   178  	}
   179  
   180  	if err := mytoken.Burn(address, amount); err != nil {
   181  		panic(err)
   182  	}
   183  }
   184  ```
   185  Calling the `Mint` method burns `amount` of tokens from the balance of `address`,
   186  decreasing the total supply.
   187  
   188  ```go
   189  // assertIsAdmin asserts the address is the admin of token
   190  func assertIsAdmin(address std.Address) {
   191  	if address != admin {
   192  		panic("restricted access")
   193  	}
   194  }
   195  ```
   196  Calling the `assertIsAdmin` method checks if `address` is equal to the 
   197  package-level `admin` variable. 
   198  
   199  ```go
   200  // Render renders the state of the realm
   201  func Render(path string) string {
   202  	parts := strings.Split(path, "/")
   203  	c := len(parts)
   204  
   205  	switch {
   206  	case path == "":
   207  		// Default GRC20 render
   208  		return mytoken.RenderHome()
   209  	case c == 2 && parts[0] == "balance":
   210  		// Render balance of specific address
   211  		owner := std.Address(parts[1])
   212  		balance, _ := mytoken.BalanceOf(owner)
   213  		return ufmt.Sprintf("%d\n", balance)
   214  	default:
   215  		return "404\n"
   216  	}
   217  }
   218  ```
   219  Calling the `Render` method returns a general render of the GRC20 realm, or
   220  if given a specific address, the user's `balance` as a formatted string.
   221  
   222  You can view the full code on [this Playground link](https://play.gno.land/p/km7Ja6WDQoL).
   223  
   224  ## Conclusion
   225  That's it 🎉
   226  
   227  You have successfully built a simple GRC20 Realm that is ready to be deployed on the Gno chain and called by users.
   228  In the upcoming guides, we will see how we can develop more complex Realm logic and have them interact with outside tools like a wallet application.