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.