From 5d971496e8985c4db364611c62fb1ee8fe675933 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 12 Mar 2024 14:48:26 +0100 Subject: [PATCH 01/43] update how-to 1 --- .../simple-contract/playground_welcome.png | Bin 0 -> 26401 bytes docs/how-to-guides/simple-contract.md | 88 ++++-------------- 2 files changed, 20 insertions(+), 68 deletions(-) create mode 100644 docs/assets/how-to-guides/simple-contract/playground_welcome.png diff --git a/docs/assets/how-to-guides/simple-contract/playground_welcome.png b/docs/assets/how-to-guides/simple-contract/playground_welcome.png new file mode 100644 index 0000000000000000000000000000000000000000..bc067668538f5dc488733f5fd7ba332d491eb2af GIT binary patch literal 26401 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 literal 0 HcmV?d00001 diff --git a/docs/how-to-guides/simple-contract.md b/docs/how-to-guides/simple-contract.md index 88c7bce7a69..ea218c90a91 100644 --- a/docs/how-to-guides/simple-contract.md +++ b/docs/how-to-guides/simple-contract.md @@ -6,9 +6,10 @@ id: simple-contract ## Overview -This guide shows you how to write a simple _Counter_ Smart Contract, or rather a [Realm](../concepts/realms.md), -in [Gno (Gnolang)](../concepts/gno-language.md). For actually deploying the Realm, please see -the [deployment](deploy.md) guide. +This guide shows you how to write a simple **Counter** Smart Contract, or rather a [Realm](../concepts/realms.md), +in [Gno](../concepts/gno-language.md). + +For actually deploying the Realm, please see the [deployment](deploy.md) guide. Our _Counter_ Realm will have the following functionality: @@ -18,74 +19,21 @@ Our _Counter_ Realm will have the following functionality: ## Prerequisites -- **Text editor** - -:::info Editor support -The Gno language is based on Go, but it does not have all the bells and whistles in major text editors like Go. -Advanced language features like IntelliSense are still in the works. - -Currently, we officially have language support -for [ViM](https://github.com/gnolang/gno/blob/master/CONTRIBUTING.md#vim-support), -[Emacs](https://github.com/gnolang/gno/blob/master/CONTRIBUTING.md#emacs-support) -and [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=harry-hov.gno). -::: - -## 1. Setting up the work directory - -Gno Realms can be typically written anywhere, under any structure, just like regular Go code. -However, Gno developers have adopted a standard of organizing Gno logic under a specific directory hierarchy, which we -will explore here. +- **Internet connection** +- **An account in a Gno.land wallet, such as [Adena](https://adena.app)** -Create the main working directory for our Realm: +## 1. Gno Playground -```bash -mkdir counter-app -``` - -Since we are building a simple _Counter_ Realm, inside our created `counter-app` directory, we can create another -directory named `r`, which stands for `realm`: - -```bash -cd counter-app -mkdir r -``` +When using the Gno Playground, writing, testing, deploying, and sharing Gno code +is simple. This makes it perfect for getting started with Gno. -Alternatively, if we were writing a [Gno Package](../concepts/packages.md), we would denote this directory name -as `p` (for `package`). You can learn more about Packages in our [Package development guide](simple-library.md). +Vising the [Playground](https://play.gno.land) will greet you with a template file: -Additionally, we will create another sub-folder that will house our Realm code, named `counter`: +![Default](../assets/how-to-guides/simple-contract/playground_welcome.png) -```bash -cd r -mkdir counter -``` +## 2. Start writing code -After setting up our work directory structure, we should have something like this: - -```text -counter-app/ -├─ r/ -│ ├─ counter/ -│ │ ├─ // source code here -``` - -## 2. Create `counter.gno` - -Now that the work directory structure is set up, we can go into the `counter` sub-folder, and actually create -our _Counter_ Smart Contract: - -```bash -cd counter -touch counter.gno -``` - -:::info Gno file extension -All Gno (Gnolang) source code has the file extension `.gno`. - -This file extension is required for existing gno tools and processes to work. -::: - -We can finally write out the logic of the _Counter_ Smart Contract in `counter.gno`: +We can now write out the logic of the **Counter** Smart Contract in `package.gno`: [embedmd]:# (../assets/how-to-guides/simple-contract/counter.gno go) ```go @@ -114,16 +62,20 @@ There are a few things happening here, so let's dissect them: - We defined the logic of our Realm into a package called `counter`. - The package-level `count` variable stores the active count for the Realm (it is stateful). -- `Increment` and `Decrement` are public Realm (Smart Contract) methods, and as such are callable by users. +- `Increment` and `Decrement` are public Realm methods, and as such are callable by users. - `Increment` and `Decrement` directly modify the `count` value by making it go up or down (change state). - Calling the `Render` method would return the `count` value as a formatted string. Learn more about the `Render` method and how it's used [here](../concepts/realms.md). +Alternatively, visit [this Playground link](https://play.gno.land/p/ONBa9eUEPKJ) +to view the code. + + :::info A note on constructors Gno Realms support a concept taken from other programming languages - _constructors_. For example, to initialize the `count` variable with custom logic, we can specify that -logic within an `init` method, that is run **only once** on Realm deployment: +logic within an `init` method, that is run **only once**, upon Realm deployment: [embedmd]:# (../assets/how-to-guides/simple-contract/init.gno go) ```go @@ -146,6 +98,6 @@ func init() { That's it 🎉 -You have successfully built a simple _Counter_ Realm that is ready to be deployed on the Gno chain and called by users. +You have successfully built a simple **Counter** Realm that is ready to be deployed on the Gno chain and called by users. 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. From 1900ac39805871bd4673facd669f8c79e7b61869 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 12 Mar 2024 14:56:06 +0100 Subject: [PATCH 02/43] updaet ht 2 --- .../simple-library/playground_welcome.png | Bin 0 -> 26401 bytes docs/how-to-guides/simple-contract.md | 2 +- docs/how-to-guides/simple-library.md | 63 +++--------------- 3 files changed, 11 insertions(+), 54 deletions(-) create mode 100644 docs/assets/how-to-guides/simple-library/playground_welcome.png diff --git a/docs/assets/how-to-guides/simple-library/playground_welcome.png b/docs/assets/how-to-guides/simple-library/playground_welcome.png new file mode 100644 index 0000000000000000000000000000000000000000..bc067668538f5dc488733f5fd7ba332d491eb2af GIT binary patch literal 26401 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 literal 0 HcmV?d00001 diff --git a/docs/how-to-guides/simple-contract.md b/docs/how-to-guides/simple-contract.md index ea218c90a91..914cf9196e2 100644 --- a/docs/how-to-guides/simple-contract.md +++ b/docs/how-to-guides/simple-contract.md @@ -22,7 +22,7 @@ Our _Counter_ Realm will have the following functionality: - **Internet connection** - **An account in a Gno.land wallet, such as [Adena](https://adena.app)** -## 1. Gno Playground +## 1. Using Gno Playground When using the Gno Playground, writing, testing, deploying, and sharing Gno code is simple. This makes it perfect for getting started with Gno. diff --git a/docs/how-to-guides/simple-library.md b/docs/how-to-guides/simple-library.md index 3c22afacb5d..fefaa92c5e8 100644 --- a/docs/how-to-guides/simple-library.md +++ b/docs/how-to-guides/simple-library.md @@ -8,72 +8,29 @@ id: simple-library This guide shows you how to write a simple library (Package) in Gno, which can be used by other Packages and Realms. Packages are _stateless_, meaning they do not hold state like regular Realms (Smart Contracts). To learn more about the -intricacies of Packages, please see the [Packages reference](../concepts/packages.md). +intricacies of Packages, please see the [Packages concept page](../concepts/packages.md). The Package we will be writing today will be a simple library for suggesting a random tapas dish. We will define a set list of tapas, and define a method that randomly selects a dish from the list. ## Prerequisites -- **Text editor** +- **Internet connection** +- **An account in a Gno.land wallet, such as [Adena](https://adena.app)** -:::info Editor support -The Gno language is based on Go, but it does not have all the bells and whistles in major text editors like Go. -Advanced language features like IntelliSense are still in the works. +## 1. Using Gno Playground -Currently, we officially have language support -for [ViM](https://github.com/gnolang/gno/blob/master/CONTRIBUTING.md#vim-support), -[Emacs](https://github.com/gnolang/gno/blob/master/CONTRIBUTING.md#emacs-support) -and [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=harry-hov.gno). -::: +When using the Gno Playground, writing, testing, deploying, and sharing Gno code +is simple. This makes it perfect for getting started with Gno. -## 1. Setting up the work directory +Vising the [Playground](https://play.gno.land) will greet you with a template file: -We discussed Gno folder structures more in detail in -the [simple Smart Contract guide](simple-contract.md#1-setting-up-the-work-directory). -For now, we will just follow some rules outlined there. +![Default](../assets/how-to-guides/simple-library/playground_welcome.png) -Create the main working directory for our Package: -```bash -mkdir tapas-lib -``` - -Since we are building a simple tapas Package, inside our created `tapas-lib` directory, we can create another -directory named `p`, which stands for `package`: - -```bash -cd tapas-lib -mkdir p -``` - -Additionally, we will create another subdirectory that will house our Package code, named `tapas`: - -```bash -cd p -mkdir tapas -``` - -After setting up our work directory structure, we should have something like this: - -```text -tapas-lib/ -├─ p/ -│ ├─ tapas/ -│ │ ├─ // source code here -``` - -## 2. Create `tapas.gno` - -Now that the work directory structure is set up, we can go into the `tapas` sub-folder, and actually create -our tapas suggestion library logic: - -```bash -cd tapas -touch tapas.gno -``` +## 2. Start writing code -Inside `tapas.gno`, we will define our library logic: +Inside `package.gno`, we will define our library logic: [embedmd]:# (../assets/how-to-guides/simple-library/tapas.gno go) ```go From 67f967a6e45f57c4ba69e582bb6ef298100ffa50 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 12 Mar 2024 14:59:17 +0100 Subject: [PATCH 03/43] update links --- docs/how-to-guides/simple-contract.md | 4 +--- docs/how-to-guides/simple-library.md | 3 ++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/how-to-guides/simple-contract.md b/docs/how-to-guides/simple-contract.md index 914cf9196e2..96278e53544 100644 --- a/docs/how-to-guides/simple-contract.md +++ b/docs/how-to-guides/simple-contract.md @@ -67,9 +67,7 @@ There are a few things happening here, so let's dissect them: - Calling the `Render` method would return the `count` value as a formatted string. Learn more about the `Render` method and how it's used [here](../concepts/realms.md). -Alternatively, visit [this Playground link](https://play.gno.land/p/ONBa9eUEPKJ) -to view the code. - +You can view the code on [this Playground link](https://play.gno.land/p/ONBa9eUEPKJ). :::info A note on constructors Gno Realms support a concept taken from other programming languages - _constructors_. diff --git a/docs/how-to-guides/simple-library.md b/docs/how-to-guides/simple-library.md index fefaa92c5e8..9b7bc0ba67e 100644 --- a/docs/how-to-guides/simple-library.md +++ b/docs/how-to-guides/simple-library.md @@ -27,7 +27,6 @@ Vising the [Playground](https://play.gno.land) will greet you with a template fi ![Default](../assets/how-to-guides/simple-library/playground_welcome.png) - ## 2. Start writing code Inside `package.gno`, we will define our library logic: @@ -82,6 +81,8 @@ There are a few things happening here, so let's dissect them: - The package imports another gno package, which is deployed at `gno.land/p/demo/rand` - We use the imported package inside of `GetTapaSuggestion` to generate a random index value for a tapa +You can view the code on [this Playground link](https://play.gno.land/p/5SQQ-r2_Vos). + ## Conclusion That's it 🎉 From 3672511fded11327f2fc036601e4e5dced9e3db9 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 12 Mar 2024 15:06:15 +0100 Subject: [PATCH 04/43] remove unnecessary adena mention --- docs/how-to-guides/simple-contract.md | 1 - docs/how-to-guides/simple-library.md | 1 - 2 files changed, 2 deletions(-) diff --git a/docs/how-to-guides/simple-contract.md b/docs/how-to-guides/simple-contract.md index 96278e53544..08e5cdebedf 100644 --- a/docs/how-to-guides/simple-contract.md +++ b/docs/how-to-guides/simple-contract.md @@ -20,7 +20,6 @@ Our _Counter_ Realm will have the following functionality: ## Prerequisites - **Internet connection** -- **An account in a Gno.land wallet, such as [Adena](https://adena.app)** ## 1. Using Gno Playground diff --git a/docs/how-to-guides/simple-library.md b/docs/how-to-guides/simple-library.md index 9b7bc0ba67e..1f19ce86a37 100644 --- a/docs/how-to-guides/simple-library.md +++ b/docs/how-to-guides/simple-library.md @@ -16,7 +16,6 @@ We will define a set list of tapas, and define a method that randomly selects a ## Prerequisites - **Internet connection** -- **An account in a Gno.land wallet, such as [Adena](https://adena.app)** ## 1. Using Gno Playground From 57176633d5ee96eced1b85b2717c398fbd822b18 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 12 Mar 2024 15:31:52 +0100 Subject: [PATCH 05/43] ht-3-testing --- .../testing-gno/package_test.png | Bin 0 -> 35040 bytes docs/how-to-guides/testing-gno.md | 89 +++++++----------- 2 files changed, 36 insertions(+), 53 deletions(-) create mode 100644 docs/assets/how-to-guides/testing-gno/package_test.png diff --git a/docs/assets/how-to-guides/testing-gno/package_test.png b/docs/assets/how-to-guides/testing-gno/package_test.png new file mode 100644 index 0000000000000000000000000000000000000000..17ea90965fdeff766eec5bfbb39802e2399090ac GIT binary patch literal 35040 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 literal 0 HcmV?d00001 diff --git a/docs/how-to-guides/testing-gno.md b/docs/how-to-guides/testing-gno.md index b4d366083ff..1516e0a1d90 100644 --- a/docs/how-to-guides/testing-gno.md +++ b/docs/how-to-guides/testing-gno.md @@ -6,24 +6,22 @@ id: testing-gno ## Overview -In this guide, we will explore the available tooling in testing out the Gno Realms and Packages we write. -We will go over different CLI tools available to developers, gno testing libraries as well as -testing techniques that involve data mocking. +In this guide, we will explore the available tooling in testing out the Gno +Realms and Packages we write. We will go over different CLI tools available to +developers, gno testing libraries as well as testing techniques that involve +data mocking. ## Prerequisites -- **`gno` set up. Reference the [Installation](../getting-started/local-setup.md#3-installing-other-gno-tools) guide - for steps** +- **Internet connection** ## Example Realm -For the purpose of this guide, we will be testing the simple *Counter* Realm created in +For the purpose of this guide, we will be testing the simple **Counter** Realm created in the [How to write a simple Gno Smart Contract (Realm)](simple-contract.md) guide. [embedmd]:# (../assets/how-to-guides/testing-gno/counter-1.gno go) ```go -// counter-app/r/counter/counter.gno - package counter import ( @@ -45,24 +43,17 @@ func Render(_ string) string { } ``` +Visit [this Playground link](https://play.gno.land/p/XbkFKAIpLO8) to get started. + ## 1. Writing the Gno test -Gno tests are written in the same manner and format as regular Go tests, just in `_test.gno` files. +Gno tests are written in the same manner and format as regular Go tests, just in +`_test.gno` files. -We can place the Gno tests for the `Counter` Realm in the same directory as `counter.gno`: +We can get started by adding a new file in the Playground, called `package_test.gno`: -```text -counter-app/ -├─ r/ -│ ├─ counter/ -│ │ ├─ counter.gno -│ │ ├─ counter_test.gno <--- the test source code -``` +![Test](../assets/how-to-guides/testing-gno/package_test.png) -```bash -cd counter -touch counter_test.gno -``` What should be tested in this _Counter_ Realm example? Mainly, we want to verify that: @@ -71,12 +62,10 @@ Mainly, we want to verify that: - Decrement decrements the value. - Render returns a valid formatted value. -Let's write the required unit tests: +Let's write the required unit tests in `package_test.gno`: [embedmd]:# (../assets/how-to-guides/testing-gno/counter-2.gno go) ```go -// counter-app/r/counter/counter_test.gno - package counter import "testing" @@ -130,32 +119,23 @@ func TestCounter_Render(t *testing.T) { :::warning Testing package-level variables -In practice, it is not advisable to test and validate package level variables like this, as their value is mutated -between test runs. For the sake of keeping this guide simple, we went ahead and reset the variable value for each test, -however, -you should employ more robust test strategies. +In practice, it is not advisable to test and validate package level variables +like this, as their value is mutated between test runs. For the sake of keeping +this guide simple, we went ahead and reset the variable value for each test, +however, you should employ more robust test strategies. ::: -## 2. Running the Gno test - -To run the prepared Gno tests, we can utilize the `gno test` CLI tool. - -Simply point it to the location containing our testing source code, and the tests will execute. -For example, we can run the following command from the `counter-app/r/counter` directory: - -```bash -gno test -v . -``` +You can view the code on [this Playground link](https://play.gno.land/p/A74fKPLQgQi). -Let's look into the different parts of this command: +## 2. Running the test -- `-v` enables the verbose output. -- `-root-dir` specifies the root directory to our cloned `gno` GitHub repository -- `.` specifies the location containing our test files. Since we are already located in that directory, we specify - a `.`. +To run the prepared Gno tests, you can use the built-in testing functionality in +the Playground. -Running the test command should produce a successful output: +By simply click "Test" in the top bar, the Playground will look for `_test.gno` +files and execute them. If all went well, you will receive the following output +in a terminal: ```bash === RUN TestCounter_Increment @@ -164,24 +144,27 @@ Running the test command should produce a successful output: --- PASS: TestCounter_Decrement (0.00s) === RUN TestCounter_Render --- PASS: TestCounter_Render (0.00s) -ok ./. 1.00s +ok /src 3.60s ``` ## Additional test support -As we grow more familiar with Gno development, our Realm / Package logic can become more complex. As such, we need -more robust testing support in the form of mocking values ahead of time that would normally be only available on a +As we grow more familiar with Gno development, our Realm / Package logic can +become more complex. As such, we need more robust testing support in the form of +mocking values ahead of time that would normally be only available on a live (deployed) Realm / Package. -Luckily, the Gno standard library provides ample support for functionality such as setting predefined values ahead of -time, such as the request caller address, or the calling package address. +Luckily, the Gno standard library provides ample support for functionality such +as setting predefined values ahead of time, such as the caller address, block +height, etc. -You can learn more about these methods, that are importable using the `std` import declaration, -in the [Standard Library](../concepts/standard-library/overview.md) reference section. +You can learn more about these methods, which are importable using the `std` +import declaration, in the [standard library](../reference/standard-library/std/testing.md) +testing reference section. ## Conclusion That's it 🎉 -You have successfully written and tested Gno code. Additionally, you have utilized the `gno test` tool, and understood -how it can be configured to make the developer experience smooth. +You have successfully written and tested Gno code. Additionally, you have +utilized the built-in Gno Playground testing functionality. From 574a373c04bfd5a197e132e931d9b9cfb6f6eaa7 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 12 Mar 2024 15:32:27 +0100 Subject: [PATCH 06/43] remove italic --- docs/how-to-guides/testing-gno.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/how-to-guides/testing-gno.md b/docs/how-to-guides/testing-gno.md index 1516e0a1d90..0fbc7917c27 100644 --- a/docs/how-to-guides/testing-gno.md +++ b/docs/how-to-guides/testing-gno.md @@ -55,7 +55,7 @@ We can get started by adding a new file in the Playground, called `package_test. ![Test](../assets/how-to-guides/testing-gno/package_test.png) -What should be tested in this _Counter_ Realm example? +What should be tested in this **Counter** Realm example? Mainly, we want to verify that: - Increment increments the value. From 50763c4c21b65e2d36635d673c2e79efe197ca3d Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 12 Mar 2024 15:32:54 +0100 Subject: [PATCH 07/43] add code formatting --- docs/how-to-guides/testing-gno.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/how-to-guides/testing-gno.md b/docs/how-to-guides/testing-gno.md index 0fbc7917c27..c3823174471 100644 --- a/docs/how-to-guides/testing-gno.md +++ b/docs/how-to-guides/testing-gno.md @@ -58,9 +58,9 @@ We can get started by adding a new file in the Playground, called `package_test. What should be tested in this **Counter** Realm example? Mainly, we want to verify that: -- Increment increments the value. -- Decrement decrements the value. -- Render returns a valid formatted value. +- `Increment()` increments the value. +- `Decrement()` decrements the value. +- `Render()` returns a valid formatted value. Let's write the required unit tests in `package_test.gno`: From 0a2bd9bf087d37132fa3ba7cfa9c593ef5d03d0a Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 12 Mar 2024 17:27:38 +0100 Subject: [PATCH 08/43] start deploy --- docs/how-to-guides/deploy.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/how-to-guides/deploy.md b/docs/how-to-guides/deploy.md index ab31716f014..4c67c0ed4c3 100644 --- a/docs/how-to-guides/deploy.md +++ b/docs/how-to-guides/deploy.md @@ -6,7 +6,19 @@ id: deploy ## Overview -This guide shows you how to deploy any realm or package to the Gno chain. Deployment is be done by utilizing `gnokey`'s `maketx addpkg` API. +This guide shows you how to deploy any realm or package to the Gno chain. It will +show you how to: +- Deploy Gno code in your browser via the Playground, +- Deploy Gno code from your local machine using `gnokey`'s `maketx addpkg` API. + + +## Deployment via the Playground + + + + +## Deployment by using `gnokey` + :::info Regardless of whether you're deploying a realm or a package, you will be using `gnokey`'s `maketx addpkg` - the usage of `maketx addpkg` in both cases is identical. From 641034ee0bb6c56c1eaa69dd9c2684f556efcb76 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 12 Mar 2024 21:12:55 +0100 Subject: [PATCH 09/43] start deploy - playground --- docs/how-to-guides/deploy.md | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/docs/how-to-guides/deploy.md b/docs/how-to-guides/deploy.md index 4c67c0ed4c3..57a06afa2a4 100644 --- a/docs/how-to-guides/deploy.md +++ b/docs/how-to-guides/deploy.md @@ -14,24 +14,40 @@ show you how to: ## Deployment via the Playground +Deployment via the Playground is recommended for smaller realms and packages. +For larger projects, it is recommended to write, test, and deploy your code from +a local environment. +For this, check out [**Deployment from a local environment**](#deployment-from-a-local-environment). +### Prerequisites +- **Internet connection** +- **A keypair in a Gno.land wallet, such as [Adena](https://adena.app)** -## Deployment by using `gnokey` +## Using Gno Playground +You can write, test, and deploy packages and realms using Gno Playground. +To start using the Playground, you can check out XYZ. + + + + + + +## Deployment from a local environment :::info Regardless of whether you're deploying a realm or a package, you will be using `gnokey`'s `maketx addpkg` - the usage of `maketx addpkg` in both cases is identical. ::: -## Prerequisites +### Prerequisites - **Have `gnokey` installed** - **Have access to a `gnoland` node (local or remote)** - **Have generated a keypair with `gnokey` & funded it with `gnot`** - **Have a Realm or Package ready to deploy** -## Deploying +### Deploying To illustrate deployment, we will use a realm. Consider the following folder structure: From c2e640b5929a7e8ac342760306e5b68ed3e5c288 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 13 Mar 2024 00:29:47 +0100 Subject: [PATCH 10/43] finish deploy --- .../how-to-guides/deploy/deploy_connect.png | Bin 0 -> 25329 bytes .../how-to-guides/deploy/deploy_default.png | Bin 0 -> 32418 bytes .../how-to-guides/deploy/deploy_success.png | Bin 0 -> 42783 bytes docs/how-to-guides/deploy.md | 38 ++++++++++++++++-- 4 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 docs/assets/how-to-guides/deploy/deploy_connect.png create mode 100644 docs/assets/how-to-guides/deploy/deploy_default.png create mode 100644 docs/assets/how-to-guides/deploy/deploy_success.png diff --git a/docs/assets/how-to-guides/deploy/deploy_connect.png b/docs/assets/how-to-guides/deploy/deploy_connect.png new file mode 100644 index 0000000000000000000000000000000000000000..984f93701b71047f52a86fa00c5fef1f81b4f54b GIT binary patch literal 25329 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

!J>4UPgTAw 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 AiU0rr literal 0 HcmV?d00001 diff --git a/docs/assets/how-to-guides/deploy/deploy_success.png b/docs/assets/how-to-guides/deploy/deploy_success.png new file mode 100644 index 0000000000000000000000000000000000000000..9d5a11eb201494f71fa485325760af971366e64a GIT binary patch literal 42783 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 literal 0 HcmV?d00001 diff --git a/docs/how-to-guides/deploy.md b/docs/how-to-guides/deploy.md index 57a06afa2a4..649435f80ed 100644 --- a/docs/how-to-guides/deploy.md +++ b/docs/how-to-guides/deploy.md @@ -24,22 +24,52 @@ For this, check out [**Deployment from a local environment**](#deployment-from-a - **Internet connection** - **A keypair in a Gno.land wallet, such as [Adena](https://adena.app)** -## Using Gno Playground +### Using Gno Playground You can write, test, and deploy packages and realms using Gno Playground. To start using the Playground, you can check out XYZ. +For this example, we will be using the **Counter** realm. You can find the code +on [this Playground link](https://play.gno.land/p/iUWTha99D1J). +Once you have confirmed that the code is without bugs, you can click on "**Deploy**". +If your wallet is not connected, you will receive a prompt to connect it: +![DeployConnect](../assets/how-to-guides/deploy/deploy_connect.png) +After connecting your wallet to the Playground, you will be prompted with a +new toolbox: +![DeployDefault](../assets/how-to-guides/deploy/deploy_default.png) -## Deployment from a local environment +Here, you can choose the deployment path of your realm or package, as well as the network +to deploy to. You can also deploy to a local node from the Playground +if you are running one. :::info -Regardless of whether you're deploying a realm or a package, you will be using `gnokey`'s `maketx addpkg` - the usage of `maketx addpkg` in both cases is identical. +A few things to consider when deploying packages and realms: +- The **name** field in the path should match your package name, in this case `counter` +- Packages are usually deployed under `p/`, while realms are deployed under `r/` + +An example path for the Counter realm could be the following: +```go +gno.land/r//counter +``` ::: +After choosing a path and network, you can click **Deploy**. This will prompt +a wallet pop-up asking you to sign the deployment transaction. + +![DeployDefault](../assets/how-to-guides/deploy/deploy_success.png) + +If all went well, you will have successfully deployed your the Counter package. +Congratulations 🎉 + +You can check the status of your transaction by visiting the link displayed in the +popup, such as [this one](https://gnoscan.io/transactions/details?txhash=q1YO2wV2n9nYfiT7mWqFd/FAUMvjAvDqYYxR5OpbRwQ=). + +## Deployment from a local environment + ### Prerequisites - **Have `gnokey` installed** @@ -99,4 +129,4 @@ Depending on the size of the package/realm, you might need to increase amount gi That's it 🎉 -You have now successfully deployed a realm/package to a Gno.land chain. +You have now successfully deployed a realm/package to a Gno.land chain. From 184659063fe5ede598a407b9222fedd7d1b59576 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 13 Mar 2024 00:35:44 +0100 Subject: [PATCH 11/43] add link to gnokey tooling, fixups --- docs/how-to-guides/deploy.md | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/docs/how-to-guides/deploy.md b/docs/how-to-guides/deploy.md index 649435f80ed..ef275d9faac 100644 --- a/docs/how-to-guides/deploy.md +++ b/docs/how-to-guides/deploy.md @@ -79,7 +79,8 @@ popup, such as [this one](https://gnoscan.io/transactions/details?txhash=q1YO2wV ### Deploying -To illustrate deployment, we will use a realm. Consider the following folder structure: +To illustrate deployment, we will use a realm. Consider the following folder +structure: ``` counter-app/ @@ -88,7 +89,8 @@ counter-app/ │ │ ├─ counter.gno ``` -We would like to deploy the realm found in `counter.gno`. To do this, open a terminal at `counter-app/` and use the following `gnokey` command: +We would like to deploy the realm found in `counter.gno`. To do this, open a +terminal at `counter-app/` and use the following `gnokey` command: ```bash gnokey maketx addpkg \ @@ -105,7 +107,8 @@ MyKey Let's analyze all of the flags in detail: - `--pkgpath` - path where the package/realm will be placed on-chain - `--pkgdir` - local path where the package/realm is located -- `--gas-wanted` - the upper limit for units of gas for the execution of the transaction - similar to Solidity's gas limit +- `--gas-wanted` - the upper limit for units of gas for the execution of the +transaction - similar to Solidity's gas limit - `--gas-fee` - similar to Solidity's gas-price - `--broadcast` - broadcast the transaction on-chain - `--chain-id` - id of the chain to connect to - local or remote @@ -113,20 +116,31 @@ Let's analyze all of the flags in detail: - `MyKey` - the keypair to use for the transaction :::info -As of October 2023, `--gas-fee` is fixed to 1gnot (10000000ugnot), with plans to change it down the line. +As of October 2023, `--gas-fee` is fixed to 1gnot (10000000ugnot), with plans +to change it down the line. ::: -Next, confirm the transaction with your keypair passphrase. If deployment was successful, you will be presented with a message similar to the following: +Next, confirm the transaction with your keypair passphrase. If deployment was +successful, you will be presented with a message similar to the following: ``` OK! GAS WANTED: 800000 GAS USED: 775097 ``` -Depending on the size of the package/realm, you might need to increase amount given in the `--gas-wanted` flag to cover the deployment cost. +Depending on the size of the package/realm, you might need to increase amount +given in the `--gas-wanted` flag to cover the deployment cost. + +:::info +Regardless of whether you're deploying a realm or a package, you will be using +`gnokey`'s `maketx addpkg` - the usage of `maketx addpkg` in both cases is identical. +To read more about the `maketx addpkg` +subcommand, view the `gnokey` [reference](../gno-tooling/cli/gnokey.md#addpkg). +::: + ## Conclusion That's it 🎉 -You have now successfully deployed a realm/package to a Gno.land chain. +You have now successfully deployed a realm/package to a Gno.land chain. From 60f0b42d4abf134966d4f6abcd5c3e2860356953 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 13 Mar 2024 01:01:27 +0100 Subject: [PATCH 12/43] update simple dapp code --- .../write-simple-dapp/poll-1.gno | 4 +-- .../write-simple-dapp/poll-2.gno | 22 ++++++------- docs/how-to-guides/write-simple-dapp.md | 32 ++++++++----------- 3 files changed, 25 insertions(+), 33 deletions(-) diff --git a/docs/assets/how-to-guides/write-simple-dapp/poll-1.gno b/docs/assets/how-to-guides/write-simple-dapp/poll-1.gno index 45ac073a387..bf2a64743d0 100644 --- a/docs/assets/how-to-guides/write-simple-dapp/poll-1.gno +++ b/docs/assets/how-to-guides/write-simple-dapp/poll-1.gno @@ -43,12 +43,12 @@ func NewPoll(title, description string, deadline int64) *Poll { // Vote Votes for a user func (p *Poll) Vote(voter std.Address, vote bool) { - p.Voters().Set(string(voter), vote) + p.Voters().Set(voter.String(), vote) } // HasVoted vote: yes - true, no - false func (p *Poll) HasVoted(address std.Address) (bool, bool) { - vote, exists := p.Voters().Get(string(address)) + vote, exists := p.Voters().Get(address.String()) if exists { return true, vote.(bool) } diff --git a/docs/assets/how-to-guides/write-simple-dapp/poll-2.gno b/docs/assets/how-to-guides/write-simple-dapp/poll-2.gno index 7720c042f98..c7dbaedfbb2 100644 --- a/docs/assets/how-to-guides/write-simple-dapp/poll-2.gno +++ b/docs/assets/how-to-guides/write-simple-dapp/poll-2.gno @@ -5,52 +5,48 @@ import ( "gno.land/p/demo/avl" "gno.land/p/demo/poll" + "gno.land/p/demo/seqid" "gno.land/p/demo/ufmt" ) // state variables var ( polls *avl.Tree // id -> Poll - pollIDCounter int + pollIDCounter seqid.ID ) func init() { polls = avl.NewTree() - pollIDCounter = 0 } // NewPoll - Creates a new Poll instance func NewPoll(title, description string, deadline int64) string { // get block height if deadline <= std.GetHeight() { - return "Error: Deadline has to be in the future." + panic("deadline has to be in the future") } - // convert int ID to string used in AVL tree - id := ufmt.Sprintf("%d", pollIDCounter) + // Generate int + id := pollIDCounter.Next().String() p := poll.NewPoll(title, description, deadline) // add new poll in avl tree polls.Set(id, p) - // increment ID counter - pollIDCounter = pollIDCounter + 1 - return ufmt.Sprintf("Successfully created poll #%s!", id) } // Vote - vote for a specific Poll // yes - true, no - false -func Vote(pollID int, vote bool) string { +func Vote(id string, vote bool) string { // get txSender txSender := std.GetOrigCaller() - id := ufmt.Sprintf("%d", pollID) // get specific Poll from AVL tree pollRaw, exists := polls.Get(id) if !exists { - return "Error: Poll with specified doesn't exist." + panic("poll with specified doesn't exist") } // cast Poll into proper format @@ -58,11 +54,11 @@ func Vote(pollID int, vote bool) string { voted, _ := poll.HasVoted(txSender) if voted { - return "Error: You've already voted!" + panic("you've already voted!") } if poll.Deadline() <= std.GetHeight() { - return "Error: Voting for this poll is closed." + panic("voting for this poll is closed") } // record vote diff --git a/docs/how-to-guides/write-simple-dapp.md b/docs/how-to-guides/write-simple-dapp.md index 4c5c5fce032..ccfc818430b 100644 --- a/docs/how-to-guides/write-simple-dapp.md +++ b/docs/how-to-guides/write-simple-dapp.md @@ -12,7 +12,7 @@ YAY or NAY for any poll that has not exceeded the voting deadline. ## Prerequisites -- **Text editor** +- **Internet connection** ## Defining dApp functionality @@ -77,12 +77,12 @@ func NewPoll(title, description string, deadline int64) *Poll { // Vote Votes for a user func (p *Poll) Vote(voter std.Address, vote bool) { - p.Voters().Set(string(voter), vote) + p.Voters().Set(voter.String(), vote) } // HasVoted vote: yes - true, no - false func (p *Poll) HasVoted(address std.Address) (bool, bool) { - vote, exists := p.Voters().Get(string(address)) + vote, exists := p.Voters().Get(address.String()) if exists { return true, vote.(bool) } @@ -107,12 +107,12 @@ A few remarks: - We are using the `std` library for accessing blockchain-related functionality and types, such as `std.Address`. - Since the `map` data type is not deterministic in Go, we need to use the AVL tree structure, defined - under `p/demo/avl`. + under `gno.land/p/demo/avl`. It behaves similarly to a map; it maps a key of type `string` onto a value of any type - `interface{}`. - We are importing the `p/demo/avl` package directly from on-chain storage, which can be accessed through the path `gno.land/`. As of October 2023, you can find already-deployed packages & libraries which provide additional Gno functionality in - the [monorepo](https://github.com/gnolang/gno), under the `examples/gno.land` folder. + the [Gno monorepo](https://github.com/gnolang/gno), under the `examples/` folder. :::info After testing the `Poll` package, we need to deploy it in order to use it in our realm. @@ -138,52 +138,48 @@ import ( "gno.land/p/demo/avl" "gno.land/p/demo/poll" + "gno.land/p/demo/seqid" "gno.land/p/demo/ufmt" ) // state variables var ( polls *avl.Tree // id -> Poll - pollIDCounter int + pollIDCounter seqid.ID ) func init() { polls = avl.NewTree() - pollIDCounter = 0 } // NewPoll - Creates a new Poll instance func NewPoll(title, description string, deadline int64) string { // get block height if deadline <= std.GetHeight() { - return "Error: Deadline has to be in the future." + panic("deadline has to be in the future") } - // convert int ID to string used in AVL tree - id := ufmt.Sprintf("%d", pollIDCounter) + // Generate int + id := pollIDCounter.Next().String() p := poll.NewPoll(title, description, deadline) // add new poll in avl tree polls.Set(id, p) - // increment ID counter - pollIDCounter = pollIDCounter + 1 - return ufmt.Sprintf("Successfully created poll #%s!", id) } // Vote - vote for a specific Poll // yes - true, no - false -func Vote(pollID int, vote bool) string { +func Vote(id string, vote bool) string { // get txSender txSender := std.GetOrigCaller() - id := ufmt.Sprintf("%d", pollID) // get specific Poll from AVL tree pollRaw, exists := polls.Get(id) if !exists { - return "Error: Poll with specified doesn't exist." + panic("poll with specified doesn't exist") } // cast Poll into proper format @@ -191,11 +187,11 @@ func Vote(pollID int, vote bool) string { voted, _ := poll.HasVoted(txSender) if voted { - return "Error: You've already voted!" + panic("you've already voted!") } if poll.Deadline() <= std.GetHeight() { - return "Error: Voting for this poll is closed." + panic("voting for this poll is closed") } // record vote From 7176b0e051b78740518dc7e8783fc368128e1c13 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 13 Mar 2024 01:08:39 +0100 Subject: [PATCH 13/43] add playground links to simple dapp --- docs/how-to-guides/write-simple-dapp.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/how-to-guides/write-simple-dapp.md b/docs/how-to-guides/write-simple-dapp.md index ccfc818430b..4d7cad8ee28 100644 --- a/docs/how-to-guides/write-simple-dapp.md +++ b/docs/how-to-guides/write-simple-dapp.md @@ -103,6 +103,8 @@ func (p Poll) VoteCount() (int, int) { } ``` +View this code in the Playground [here](https://play.gno.land/p/H-fLjDfLjd9). + A few remarks: - We are using the `std` library for accessing blockchain-related functionality and types, such as `std.Address`. @@ -289,6 +291,8 @@ func Render(path string) string { } ``` +View this code in the Playground [here](https://play.gno.land/p/4FzhuB6dyVM). + ## Conclusion That's it 🎉 From daf6f2d7aa9b22e734acee59e38afa40e141246e Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 13 Mar 2024 12:05:06 +0100 Subject: [PATCH 14/43] fixup --- docs/how-to-guides/write-simple-dapp.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/how-to-guides/write-simple-dapp.md b/docs/how-to-guides/write-simple-dapp.md index 4d7cad8ee28..a2f5eef723c 100644 --- a/docs/how-to-guides/write-simple-dapp.md +++ b/docs/how-to-guides/write-simple-dapp.md @@ -293,6 +293,8 @@ func Render(path string) string { View this code in the Playground [here](https://play.gno.land/p/4FzhuB6dyVM). +To see how to deploy this app, visit the [Deployment guide](./deploy.md). + ## Conclusion That's it 🎉 From b84769cc60a4359f3f4dab6947798681e050ca31 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 13 Mar 2024 23:44:00 +0100 Subject: [PATCH 15/43] fixup grc20 code --- .../creating-grc20/mytoken-1.gno | 100 ++++++++- .../creating-grc20/mytoken-2.gno | 79 ++++--- docs/how-to-guides/creating-grc20.md | 192 ++++++++++++++---- 3 files changed, 298 insertions(+), 73 deletions(-) diff --git a/docs/assets/how-to-guides/creating-grc20/mytoken-1.gno b/docs/assets/how-to-guides/creating-grc20/mytoken-1.gno index 5500f019886..b84513ab4e4 100644 --- a/docs/assets/how-to-guides/creating-grc20/mytoken-1.gno +++ b/docs/assets/how-to-guides/creating-grc20/mytoken-1.gno @@ -1,21 +1,107 @@ package mytoken import ( - "std" - "gno.land/p/demo/grc/grc20" + "gno.land/p/demo/ufmt" + "std" ) var ( mytoken *grc20.AdminToken - admin std.Address = "g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj" // set admin account + admin std.Address ) -// init is a constructor function that runs only once (at time of deployment) +// init is called once at time of deployment func init() { + // set admin as deployer + admin = std.PrevRealm().Addr() + // provision the token's name, symbol and number of decimals - mytoken = grc20.NewAdminToken("Mytoken", "MTKN", 4) + mytoken = grc20.NewAdminToken("My Token", "TKN", 4) + + // mint 1 million tokens to admin + mytoken.Mint(admin, 1000000*10000) +} + +func TotalSupply() uint64 { + return mytoken.TotalSupply() +} + +func BalanceOf(account std.Address) uint64 { + balance, err := mytoken.BalanceOf(account) + if err != nil { + panic(err) + } + + return balance +} + +func Allowance(owner, spender std.Address) uint64 { + allowance, err := mytoken.Allowance(owner, spender) + if err != nil { + panic(err) + } + + return allowance +} + +func Transfer(recipient std.Address, amount uint64) { + caller := std.PrevRealm().Addr() + if err := mytoken.Transfer(caller, recipient, amount); err != nil { + panic(err) + } +} + +func Approve(spender std.Address, amount uint64) { + caller := std.PrevRealm().Addr() + if err := mytoken.Approve(caller, spender, amount); err != nil { + panic(err) + } +} + +func TransferFrom(from, to std.Address, amount uint64) { + caller := std.PrevRealm().Addr() + if err := mytoken.TransferFrom(caller, from, to, amount); err != nil { + panic(err) + } +} + +func Mint(address std.Address, amount uint64) { + caller := std.PrevRealm().Addr() + assertIsAdmin(caller) + + if err := mytoken.Mint(address, amount); err != nil { + panic(err) + } +} + +func Burn(address std.Address, amount uint64) { + caller := std.PrevRealm().Addr() + assertIsAdmin(caller) + + if err := mytoken.Burn(address, amount); err != nil { + panic(err) + } +} + +func assertIsAdmin(address std.Address) { + if address != admin { + panic("restricted access") + } +} + +func Render(path string) string { + parts := strings.Split(path, "/") + c := len(parts) - // set the total supply - mytoken.Mint(admin, 1000000*10000) // @administrator (supply = 1 million) + switch { + case path == "": + return mytoken.RenderHome() + case c == 2 && parts[0] == "balance": + owner := std.Address(parts[1]) + balance, _ := mytoken.BalanceOf(owner) + return ufmt.Sprintf("%d\n", balance) + default: + return "404\n" + } } diff --git a/docs/assets/how-to-guides/creating-grc20/mytoken-2.gno b/docs/assets/how-to-guides/creating-grc20/mytoken-2.gno index 25ff85c55ab..31caaa07c17 100644 --- a/docs/assets/how-to-guides/creating-grc20/mytoken-2.gno +++ b/docs/assets/how-to-guides/creating-grc20/mytoken-2.gno @@ -1,65 +1,96 @@ +package leon_token + +import ( + "gno.land/p/demo/grc/grc20" + "gno.land/p/demo/ufmt" + "std" + "strings" +) + +var ( + mytoken *grc20.AdminToken + admin std.Address +) + +// init is called once at time of deployment +func init() { + // set admin as deployer + admin = std.PrevRealm().Addr() + + // provision the token's name, symbol and number of decimals + mytoken = grc20.NewAdminToken("Leon Token", "LEON", 4) + + // mint 1 million tokens to admin + mytoken.Mint(admin, 1000000*10000) +} + func TotalSupply() uint64 { return mytoken.TotalSupply() } -func BalanceOf(owner users.AddressOrName) uint64 { - balance, err := mytoken.BalanceOf(owner.Resolve()) +func BalanceOf(account std.Address) uint64 { + balance, err := mytoken.BalanceOf(account) if err != nil { panic(err) } + return balance } -func Allowance(owner, spender users.AddressOrName) uint64 { - allowance, err := mytoken.Allowance(owner.Resolve(), spender.Resolve()) +func Allowance(owner, spender std.Address) uint64 { + allowance, err := mytoken.Allowance(owner, spender) if err != nil { panic(err) } + return allowance } -func Transfer(to users.AddressOrName, amount uint64) { +func Transfer(recipient std.Address, amount uint64) { caller := std.PrevRealm().Addr() - err := mytoken.Transfer(caller, to.Resolve(), amount) - if err != nil { + if err := mytoken.Transfer(caller, recipient, amount); err != nil { panic(err) } } -func Approve(spender users.AddressOrName, amount uint64) { +func Approve(spender std.Address, amount uint64) { caller := std.PrevRealm().Addr() - err := mytoken.Approve(caller, spender.Resolve(), amount) - if err != nil { + if err := mytoken.Approve(caller, spender, amount); err != nil { panic(err) } } -func TransferFrom(from, to users.AddressOrName, amount uint64) { +func TransferFrom(from, to std.Address, amount uint64) { caller := std.PrevRealm().Addr() - err := mytoken.TransferFrom(caller, from.Resolve(), to.Resolve(), amount) - if err != nil { + if err := mytoken.TransferFrom(caller, from, to, amount); err != nil { panic(err) } } -func Mint(address users.AddressOrName, amount uint64) { +func Mint(address std.Address, amount uint64) { caller := std.PrevRealm().Addr() assertIsAdmin(caller) - err := mytoken.Mint(address.Resolve(), amount) - if err != nil { + + if err := mytoken.Mint(address, amount); err != nil { panic(err) } } -func Burn(address users.AddressOrName, amount uint64) { +func Burn(address std.Address, amount uint64) { caller := std.PrevRealm().Addr() assertIsAdmin(caller) - err := mytoken.Burn(address.Resolve(), amount) - if err != nil { + + if err := mytoken.Burn(address, amount); err != nil { panic(err) } } +func assertIsAdmin(address std.Address) { + if address != admin { + panic("restricted access") + } +} + func Render(path string) string { parts := strings.Split(path, "/") c := len(parts) @@ -68,16 +99,10 @@ func Render(path string) string { case path == "": return mytoken.RenderHome() case c == 2 && parts[0] == "balance": - owner := users.AddressOrName(parts[1]) - balance, _ := mytoken.BalanceOf(owner.Resolve()) + owner := std.Address(parts[1]) + balance, _ := mytoken.BalanceOf(owner) return ufmt.Sprintf("%d\n", balance) default: return "404\n" } } - -func assertIsAdmin(address std.Address) { - if address != admin { - panic("restricted access") - } -} diff --git a/docs/how-to-guides/creating-grc20.md b/docs/how-to-guides/creating-grc20.md index d00fd8c349e..deab6fd8c4d 100644 --- a/docs/how-to-guides/creating-grc20.md +++ b/docs/how-to-guides/creating-grc20.md @@ -3,12 +3,14 @@ id: creating-grc20 --- # How to create a GRC20 Token - ## Overview -This guide shows you how to write a simple _GRC20_ Smart Contract, or rather a [Realm](../concepts/realms.md), in [Gno (Gno)](../concepts/gno-language.md). For actually deploying the Realm, please see the [deployment](deploy.md) guide. -Our _GRC20_ Realm will have the following functionality: +This guide shows you how to write a simple **GRC20** Smart Contract, or rather +a [Realm](../concepts/realms.md), in [Gno](../concepts/gno-language.md). For actually deploying the Realm, please see the +[deployment](deploy.md) guide. + +Our **GRC20** Realm will have the following functionality: - Minting a configurable amount of token. - Keeping track of total token supply. @@ -16,33 +18,120 @@ Our _GRC20_ Realm will have the following functionality: ## Prerequisites -We will proceed using the typical directory structure for a Realm found within the [simple-contract guide](simple-contract.md). It is also worthwhile to consult the [GRC20 interface](https://github.com/gnolang/gno/blob/master/examples/gno.land/p/demo/grc/grc20/igrc20.gno) which we will be importing and utilizing within this guide. +- **Internet connection** ## 1. Importing token package -For this realm, we'll want to import the `grc20` package as this will include the main functionality of our token factory realm. +For this realm, we'll want to import the `grc20` package as this will include +the main functionality of our token factory realm. [embedmd]:# (../assets/how-to-guides/creating-grc20/mytoken-1.gno go) ```go package mytoken import ( - "std" - "gno.land/p/demo/grc/grc20" + "gno.land/p/demo/ufmt" + "std" ) var ( mytoken *grc20.AdminToken - admin std.Address = "g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj" // set admin account + admin std.Address ) -// init is a constructor function that runs only once (at time of deployment) +// init is called once at time of deployment func init() { + // set admin as deployer + admin = std.PrevRealm().Addr() + // provision the token's name, symbol and number of decimals - mytoken = grc20.NewAdminToken("Mytoken", "MTKN", 4) + mytoken = grc20.NewAdminToken("My Token", "TKN", 4) + + // mint 1 million tokens to admin + mytoken.Mint(admin, 1000000*10000) +} + +func TotalSupply() uint64 { + return mytoken.TotalSupply() +} + +func BalanceOf(account std.Address) uint64 { + balance, err := mytoken.BalanceOf(account) + if err != nil { + panic(err) + } + + return balance +} + +func Allowance(owner, spender std.Address) uint64 { + allowance, err := mytoken.Allowance(owner, spender) + if err != nil { + panic(err) + } + + return allowance +} + +func Transfer(recipient std.Address, amount uint64) { + caller := std.PrevRealm().Addr() + if err := mytoken.Transfer(caller, recipient, amount); err != nil { + panic(err) + } +} + +func Approve(spender std.Address, amount uint64) { + caller := std.PrevRealm().Addr() + if err := mytoken.Approve(caller, spender, amount); err != nil { + panic(err) + } +} + +func TransferFrom(from, to std.Address, amount uint64) { + caller := std.PrevRealm().Addr() + if err := mytoken.TransferFrom(caller, from, to, amount); err != nil { + panic(err) + } +} + +func Mint(address std.Address, amount uint64) { + caller := std.PrevRealm().Addr() + assertIsAdmin(caller) + + if err := mytoken.Mint(address, amount); err != nil { + panic(err) + } +} + +func Burn(address std.Address, amount uint64) { + caller := std.PrevRealm().Addr() + assertIsAdmin(caller) + + if err := mytoken.Burn(address, amount); err != nil { + panic(err) + } +} - // set the total supply - mytoken.Mint(admin, 1000000*10000) // @administrator (supply = 1 million) +func assertIsAdmin(address std.Address) { + if address != admin { + panic("restricted access") + } +} + +func Render(path string) string { + parts := strings.Split(path, "/") + c := len(parts) + + switch { + case path == "": + return mytoken.RenderHome() + case c == 2 && parts[0] == "balance": + owner := std.Address(parts[1]) + balance, _ := mytoken.BalanceOf(owner) + return ufmt.Sprintf("%d\n", balance) + default: + return "404\n" + } } ``` @@ -58,68 +147,99 @@ The following section will be about introducing Public functions to expose funct [embedmd]:# (../assets/how-to-guides/creating-grc20/mytoken-2.gno go) ```go +package leon_token + +import ( + "gno.land/p/demo/grc/grc20" + "gno.land/p/demo/ufmt" + "std" + "strings" +) + +var ( + mytoken *grc20.AdminToken + admin std.Address +) + +// init is called once at time of deployment +func init() { + // set admin as deployer + admin = std.PrevRealm().Addr() + + // provision the token's name, symbol and number of decimals + mytoken = grc20.NewAdminToken("Leon Token", "LEON", 4) + + // mint 1 million tokens to admin + mytoken.Mint(admin, 1000000*10000) +} + func TotalSupply() uint64 { return mytoken.TotalSupply() } -func BalanceOf(owner users.AddressOrName) uint64 { - balance, err := mytoken.BalanceOf(owner.Resolve()) +func BalanceOf(account std.Address) uint64 { + balance, err := mytoken.BalanceOf(account) if err != nil { panic(err) } + return balance } -func Allowance(owner, spender users.AddressOrName) uint64 { - allowance, err := mytoken.Allowance(owner.Resolve(), spender.Resolve()) +func Allowance(owner, spender std.Address) uint64 { + allowance, err := mytoken.Allowance(owner, spender) if err != nil { panic(err) } + return allowance } -func Transfer(to users.AddressOrName, amount uint64) { +func Transfer(recipient std.Address, amount uint64) { caller := std.PrevRealm().Addr() - err := mytoken.Transfer(caller, to.Resolve(), amount) - if err != nil { + if err := mytoken.Transfer(caller, recipient, amount); err != nil { panic(err) } } -func Approve(spender users.AddressOrName, amount uint64) { +func Approve(spender std.Address, amount uint64) { caller := std.PrevRealm().Addr() - err := mytoken.Approve(caller, spender.Resolve(), amount) - if err != nil { + if err := mytoken.Approve(caller, spender, amount); err != nil { panic(err) } } -func TransferFrom(from, to users.AddressOrName, amount uint64) { +func TransferFrom(from, to std.Address, amount uint64) { caller := std.PrevRealm().Addr() - err := mytoken.TransferFrom(caller, from.Resolve(), to.Resolve(), amount) - if err != nil { + if err := mytoken.TransferFrom(caller, from, to, amount); err != nil { panic(err) } } -func Mint(address users.AddressOrName, amount uint64) { +func Mint(address std.Address, amount uint64) { caller := std.PrevRealm().Addr() assertIsAdmin(caller) - err := mytoken.Mint(address.Resolve(), amount) - if err != nil { + + if err := mytoken.Mint(address, amount); err != nil { panic(err) } } -func Burn(address users.AddressOrName, amount uint64) { +func Burn(address std.Address, amount uint64) { caller := std.PrevRealm().Addr() assertIsAdmin(caller) - err := mytoken.Burn(address.Resolve(), amount) - if err != nil { + + if err := mytoken.Burn(address, amount); err != nil { panic(err) } } +func assertIsAdmin(address std.Address) { + if address != admin { + panic("restricted access") + } +} + func Render(path string) string { parts := strings.Split(path, "/") c := len(parts) @@ -128,19 +248,13 @@ func Render(path string) string { case path == "": return mytoken.RenderHome() case c == 2 && parts[0] == "balance": - owner := users.AddressOrName(parts[1]) - balance, _ := mytoken.BalanceOf(owner.Resolve()) + owner := std.Address(parts[1]) + balance, _ := mytoken.BalanceOf(owner) return ufmt.Sprintf("%d\n", balance) default: return "404\n" } } - -func assertIsAdmin(address std.Address) { - if address != admin { - panic("restricted access") - } -} ``` Detailing what is happening in the above code: From 4158a533722cb8bcead78fdb2ebca486b4406119 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 13 Mar 2024 23:55:13 +0100 Subject: [PATCH 16/43] update --- docs/how-to-guides/creating-grc20.md | 112 +-------------------------- 1 file changed, 1 insertion(+), 111 deletions(-) diff --git a/docs/how-to-guides/creating-grc20.md b/docs/how-to-guides/creating-grc20.md index deab6fd8c4d..7baec9f4c98 100644 --- a/docs/how-to-guides/creating-grc20.md +++ b/docs/how-to-guides/creating-grc20.md @@ -5,7 +5,6 @@ id: creating-grc20 # How to create a GRC20 Token ## Overview - This guide shows you how to write a simple **GRC20** Smart Contract, or rather a [Realm](../concepts/realms.md), in [Gno](../concepts/gno-language.md). For actually deploying the Realm, please see the [deployment](deploy.md) guide. @@ -30,7 +29,7 @@ package mytoken import ( "gno.land/p/demo/grc/grc20" - "gno.land/p/demo/ufmt" + "gno.land/p/demo/ufmt" "std" ) @@ -50,89 +49,6 @@ func init() { // mint 1 million tokens to admin mytoken.Mint(admin, 1000000*10000) } - -func TotalSupply() uint64 { - return mytoken.TotalSupply() -} - -func BalanceOf(account std.Address) uint64 { - balance, err := mytoken.BalanceOf(account) - if err != nil { - panic(err) - } - - return balance -} - -func Allowance(owner, spender std.Address) uint64 { - allowance, err := mytoken.Allowance(owner, spender) - if err != nil { - panic(err) - } - - return allowance -} - -func Transfer(recipient std.Address, amount uint64) { - caller := std.PrevRealm().Addr() - if err := mytoken.Transfer(caller, recipient, amount); err != nil { - panic(err) - } -} - -func Approve(spender std.Address, amount uint64) { - caller := std.PrevRealm().Addr() - if err := mytoken.Approve(caller, spender, amount); err != nil { - panic(err) - } -} - -func TransferFrom(from, to std.Address, amount uint64) { - caller := std.PrevRealm().Addr() - if err := mytoken.TransferFrom(caller, from, to, amount); err != nil { - panic(err) - } -} - -func Mint(address std.Address, amount uint64) { - caller := std.PrevRealm().Addr() - assertIsAdmin(caller) - - if err := mytoken.Mint(address, amount); err != nil { - panic(err) - } -} - -func Burn(address std.Address, amount uint64) { - caller := std.PrevRealm().Addr() - assertIsAdmin(caller) - - if err := mytoken.Burn(address, amount); err != nil { - panic(err) - } -} - -func assertIsAdmin(address std.Address) { - if address != admin { - panic("restricted access") - } -} - -func Render(path string) string { - parts := strings.Split(path, "/") - c := len(parts) - - switch { - case path == "": - return mytoken.RenderHome() - case c == 2 && parts[0] == "balance": - owner := std.Address(parts[1]) - balance, _ := mytoken.BalanceOf(owner) - return ufmt.Sprintf("%d\n", balance) - default: - return "404\n" - } -} ``` In this code preview, we have: @@ -147,32 +63,6 @@ The following section will be about introducing Public functions to expose funct [embedmd]:# (../assets/how-to-guides/creating-grc20/mytoken-2.gno go) ```go -package leon_token - -import ( - "gno.land/p/demo/grc/grc20" - "gno.land/p/demo/ufmt" - "std" - "strings" -) - -var ( - mytoken *grc20.AdminToken - admin std.Address -) - -// init is called once at time of deployment -func init() { - // set admin as deployer - admin = std.PrevRealm().Addr() - - // provision the token's name, symbol and number of decimals - mytoken = grc20.NewAdminToken("Leon Token", "LEON", 4) - - // mint 1 million tokens to admin - mytoken.Mint(admin, 1000000*10000) -} - func TotalSupply() uint64 { return mytoken.TotalSupply() } From a76feb6c752a63339fca3fe2ab1762f94cb7a75f Mon Sep 17 00:00:00 2001 From: leohhhn Date: Thu, 14 Mar 2024 09:55:38 +0100 Subject: [PATCH 17/43] update asset files --- .../creating-grc20/mytoken-1.gno | 84 ------------------- .../creating-grc20/mytoken-2.gno | 26 ------ 2 files changed, 110 deletions(-) diff --git a/docs/assets/how-to-guides/creating-grc20/mytoken-1.gno b/docs/assets/how-to-guides/creating-grc20/mytoken-1.gno index b84513ab4e4..35bb3c57ff9 100644 --- a/docs/assets/how-to-guides/creating-grc20/mytoken-1.gno +++ b/docs/assets/how-to-guides/creating-grc20/mytoken-1.gno @@ -2,7 +2,6 @@ package mytoken import ( "gno.land/p/demo/grc/grc20" - "gno.land/p/demo/ufmt" "std" ) @@ -22,86 +21,3 @@ func init() { // mint 1 million tokens to admin mytoken.Mint(admin, 1000000*10000) } - -func TotalSupply() uint64 { - return mytoken.TotalSupply() -} - -func BalanceOf(account std.Address) uint64 { - balance, err := mytoken.BalanceOf(account) - if err != nil { - panic(err) - } - - return balance -} - -func Allowance(owner, spender std.Address) uint64 { - allowance, err := mytoken.Allowance(owner, spender) - if err != nil { - panic(err) - } - - return allowance -} - -func Transfer(recipient std.Address, amount uint64) { - caller := std.PrevRealm().Addr() - if err := mytoken.Transfer(caller, recipient, amount); err != nil { - panic(err) - } -} - -func Approve(spender std.Address, amount uint64) { - caller := std.PrevRealm().Addr() - if err := mytoken.Approve(caller, spender, amount); err != nil { - panic(err) - } -} - -func TransferFrom(from, to std.Address, amount uint64) { - caller := std.PrevRealm().Addr() - if err := mytoken.TransferFrom(caller, from, to, amount); err != nil { - panic(err) - } -} - -func Mint(address std.Address, amount uint64) { - caller := std.PrevRealm().Addr() - assertIsAdmin(caller) - - if err := mytoken.Mint(address, amount); err != nil { - panic(err) - } -} - -func Burn(address std.Address, amount uint64) { - caller := std.PrevRealm().Addr() - assertIsAdmin(caller) - - if err := mytoken.Burn(address, amount); err != nil { - panic(err) - } -} - -func assertIsAdmin(address std.Address) { - if address != admin { - panic("restricted access") - } -} - -func Render(path string) string { - parts := strings.Split(path, "/") - c := len(parts) - - switch { - case path == "": - return mytoken.RenderHome() - case c == 2 && parts[0] == "balance": - owner := std.Address(parts[1]) - balance, _ := mytoken.BalanceOf(owner) - return ufmt.Sprintf("%d\n", balance) - default: - return "404\n" - } -} diff --git a/docs/assets/how-to-guides/creating-grc20/mytoken-2.gno b/docs/assets/how-to-guides/creating-grc20/mytoken-2.gno index 31caaa07c17..3acd9158a23 100644 --- a/docs/assets/how-to-guides/creating-grc20/mytoken-2.gno +++ b/docs/assets/how-to-guides/creating-grc20/mytoken-2.gno @@ -1,29 +1,3 @@ -package leon_token - -import ( - "gno.land/p/demo/grc/grc20" - "gno.land/p/demo/ufmt" - "std" - "strings" -) - -var ( - mytoken *grc20.AdminToken - admin std.Address -) - -// init is called once at time of deployment -func init() { - // set admin as deployer - admin = std.PrevRealm().Addr() - - // provision the token's name, symbol and number of decimals - mytoken = grc20.NewAdminToken("Leon Token", "LEON", 4) - - // mint 1 million tokens to admin - mytoken.Mint(admin, 1000000*10000) -} - func TotalSupply() uint64 { return mytoken.TotalSupply() } From 306841dfc4c516d68840274673802741a5355a0d Mon Sep 17 00:00:00 2001 From: leohhhn Date: Thu, 14 Mar 2024 10:42:27 +0100 Subject: [PATCH 18/43] add bytes --- docs/how-to-guides/creating-grc20.md | 2 +- docs/how-to-guides/write-simple-dapp.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/how-to-guides/creating-grc20.md b/docs/how-to-guides/creating-grc20.md index 7baec9f4c98..d5c83338fb5 100644 --- a/docs/how-to-guides/creating-grc20.md +++ b/docs/how-to-guides/creating-grc20.md @@ -29,7 +29,7 @@ package mytoken import ( "gno.land/p/demo/grc/grc20" - "gno.land/p/demo/ufmt" + "gno.land/p/demo/ufmt" "std" ) diff --git a/docs/how-to-guides/write-simple-dapp.md b/docs/how-to-guides/write-simple-dapp.md index a2f5eef723c..51048006401 100644 --- a/docs/how-to-guides/write-simple-dapp.md +++ b/docs/how-to-guides/write-simple-dapp.md @@ -136,6 +136,7 @@ The realm will contain the following functionality: package poll import ( + "bytes" "std" "gno.land/p/demo/avl" From 5a16afd400885f1c4dba49d52b61966718c7ef53 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 19 Mar 2024 18:58:05 +0100 Subject: [PATCH 19/43] update tapas --- .../how-to-guides/simple-library/tapas.gno | 17 +++++++---------- docs/how-to-guides/simple-library.md | 19 ++++++++----------- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/docs/assets/how-to-guides/simple-library/tapas.gno b/docs/assets/how-to-guides/simple-library/tapas.gno index 7d8eb184898..c55fceaf3b8 100644 --- a/docs/assets/how-to-guides/simple-library/tapas.gno +++ b/docs/assets/how-to-guides/simple-library/tapas.gno @@ -1,8 +1,6 @@ package tapas -import ( - "gno.land/p/demo/rand" -) +import "std" // List of tapas suggestions var listOfTapas = []string{ @@ -27,14 +25,13 @@ var listOfTapas = []string{ } // GetTapaSuggestion randomly selects and returns a tapa suggestion -func GetTapaSuggestion() string { - // Create a new instance of the random number generator. - // Notice that this is from an imported Gno library - generator := rand.New() +func GetTapaSuggestion(userInput string) string { - // Generate a random index - randomIndex := generator.Intn(len(listOfTapas)) + // Create a random number depending on the block height. + // We get the block height using std.GetHeight(), which is from an imported Gno library, "std" + // Note: this value is not fully random and is easily guessable + randomNumber := int(std.GetHeight()) % len(listOfTapas) // Return the random suggestion - return listOfTapas[randomIndex] + return listOfTapas[randomNumber] } diff --git a/docs/how-to-guides/simple-library.md b/docs/how-to-guides/simple-library.md index 1f19ce86a37..006ea0bbace 100644 --- a/docs/how-to-guides/simple-library.md +++ b/docs/how-to-guides/simple-library.md @@ -34,9 +34,7 @@ Inside `package.gno`, we will define our library logic: ```go package tapas -import ( - "gno.land/p/demo/rand" -) +import "std" // List of tapas suggestions var listOfTapas = []string{ @@ -61,16 +59,15 @@ var listOfTapas = []string{ } // GetTapaSuggestion randomly selects and returns a tapa suggestion -func GetTapaSuggestion() string { - // Create a new instance of the random number generator. - // Notice that this is from an imported Gno library - generator := rand.New() +func GetTapaSuggestion(userInput string) string { - // Generate a random index - randomIndex := generator.Intn(len(listOfTapas)) + // Create a random number depending on the block height. + // We get the block height using std.GetHeight(), which is from an imported Gno library, "std" + // Note: this value is not fully random and is easily guessable + randomNumber := int(std.GetHeight()) % len(listOfTapas) // Return the random suggestion - return listOfTapas[randomIndex] + return listOfTapas[randomNumber] } ``` @@ -80,7 +77,7 @@ There are a few things happening here, so let's dissect them: - The package imports another gno package, which is deployed at `gno.land/p/demo/rand` - We use the imported package inside of `GetTapaSuggestion` to generate a random index value for a tapa -You can view the code on [this Playground link](https://play.gno.land/p/5SQQ-r2_Vos). +You can view the code on [this Playground link](https://play.gno.land/p/3uwBqP66ekC). ## Conclusion From 74dbccf033b07f3ecfd261b6e27fd5acccb7b625 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 20 Mar 2024 22:31:53 +0100 Subject: [PATCH 20/43] update pseudorandom --- docs/how-to-guides/simple-library.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/how-to-guides/simple-library.md b/docs/how-to-guides/simple-library.md index 006ea0bbace..843c934178c 100644 --- a/docs/how-to-guides/simple-library.md +++ b/docs/how-to-guides/simple-library.md @@ -61,7 +61,7 @@ var listOfTapas = []string{ // GetTapaSuggestion randomly selects and returns a tapa suggestion func GetTapaSuggestion(userInput string) string { - // Create a random number depending on the block height. + // Create a pseudorandom number depending on the block height. // We get the block height using std.GetHeight(), which is from an imported Gno library, "std" // Note: this value is not fully random and is easily guessable randomNumber := int(std.GetHeight()) % len(listOfTapas) From c6d329ea14f3e1d219873197c92b596238be31d5 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 20 Mar 2024 22:43:43 +0100 Subject: [PATCH 21/43] remove deploy paragraph --- docs/how-to-guides/deploy.md | 3 --- docs/how-to-guides/simple-library.md | 6 ++++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/how-to-guides/deploy.md b/docs/how-to-guides/deploy.md index ef275d9faac..a179c34149d 100644 --- a/docs/how-to-guides/deploy.md +++ b/docs/how-to-guides/deploy.md @@ -65,9 +65,6 @@ a wallet pop-up asking you to sign the deployment transaction. If all went well, you will have successfully deployed your the Counter package. Congratulations 🎉 -You can check the status of your transaction by visiting the link displayed in the -popup, such as [this one](https://gnoscan.io/transactions/details?txhash=q1YO2wV2n9nYfiT7mWqFd/FAUMvjAvDqYYxR5OpbRwQ=). - ## Deployment from a local environment ### Prerequisites diff --git a/docs/how-to-guides/simple-library.md b/docs/how-to-guides/simple-library.md index 843c934178c..42426d89905 100644 --- a/docs/how-to-guides/simple-library.md +++ b/docs/how-to-guides/simple-library.md @@ -74,8 +74,10 @@ func GetTapaSuggestion(userInput string) string { There are a few things happening here, so let's dissect them: - We defined the logic of our library into a package called `tapas`. -- The package imports another gno package, which is deployed at `gno.land/p/demo/rand` -- We use the imported package inside of `GetTapaSuggestion` to generate a random index value for a tapa +- The package imports `std`, which +is the [Gno standard library](../concepts/standard-library/overview.md) +- We use the imported package inside of `GetTapaSuggestion` to generate a +random index value for a tapa You can view the code on [this Playground link](https://play.gno.land/p/3uwBqP66ekC). From ddd06b7a7c3f3ffb2af994fe017299bf38ab1c1d Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 20 Mar 2024 23:07:15 +0100 Subject: [PATCH 22/43] update simple dapp hw --- docs/how-to-guides/deploy.md | 2 +- docs/how-to-guides/write-simple-dapp.md | 26 +++++++++++++++---------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/docs/how-to-guides/deploy.md b/docs/how-to-guides/deploy.md index a179c34149d..82ba4de1155 100644 --- a/docs/how-to-guides/deploy.md +++ b/docs/how-to-guides/deploy.md @@ -77,7 +77,7 @@ Congratulations 🎉 ### Deploying To illustrate deployment, we will use a realm. Consider the following folder -structure: +structure on a local file system: ``` counter-app/ diff --git a/docs/how-to-guides/write-simple-dapp.md b/docs/how-to-guides/write-simple-dapp.md index 51048006401..0c10f2f8ef4 100644 --- a/docs/how-to-guides/write-simple-dapp.md +++ b/docs/how-to-guides/write-simple-dapp.md @@ -103,18 +103,19 @@ func (p Poll) VoteCount() (int, int) { } ``` -View this code in the Playground [here](https://play.gno.land/p/H-fLjDfLjd9). +View this code in the Playground [here](https://play.gno.land/p/dwARIIq0meB). A few remarks: -- We are using the `std` library for accessing blockchain-related functionality and types, such as `std.Address`. -- Since the `map` data type is not deterministic in Go, we need to use the AVL tree structure, defined - under `gno.land/p/demo/avl`. - It behaves similarly to a map; it maps a key of type `string` onto a value of any type - `interface{}`. -- We are importing the `p/demo/avl` package directly from on-chain storage, which can be accessed through the - path `gno.land/`. - As of October 2023, you can find already-deployed packages & libraries which provide additional Gno functionality in - the [Gno monorepo](https://github.com/gnolang/gno), under the `examples/` folder. +- We are using the `std` library for accessing blockchain-related functionality +and types, such as `std.Address`. +- Since the `map` data type is not deterministic in Go, we need to use the AVL +tree structure, defined +under `gno.land/p/demo/avl`. It behaves similarly to a map; it maps a key of +type `string` onto a value of any type - `interface{}`. +- We are importing the `gno.land/p/demo/avl` package directly from on-chain storage. +You can find predeployed packages & libraries which provide additional Gno +functionality in the [Gno monorepo](https://github.com/gnolang/gno), under the `examples/` folder. :::info After testing the `Poll` package, we need to deploy it in order to use it in our realm. @@ -210,6 +211,11 @@ func Vote(id string, vote bool) string { } ``` +:::info +Depending on where you deployed your `Poll` package, you will have to change its +import path in the realm code. +::: + With that we have written the core functionality of the realm, and all that is left is the [Render function](http://localhost:3000/explanation/realms). Its purpose is to help us display the state of the realm in Markdown, by formatting the state into a string buffer: @@ -292,7 +298,7 @@ func Render(path string) string { } ``` -View this code in the Playground [here](https://play.gno.land/p/4FzhuB6dyVM). +View this code in the Playground [here](https://play.gno.land/p/5jgHw29sGq4). To see how to deploy this app, visit the [Deployment guide](./deploy.md). From 43015638afa4434fbe1a95c71870e558cf1a43d4 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 20 Mar 2024 23:43:39 +0100 Subject: [PATCH 23/43] fixup grc20 guide --- docs/how-to-guides/creating-grc20.md | 62 ++++++++++++++++++---------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/docs/how-to-guides/creating-grc20.md b/docs/how-to-guides/creating-grc20.md index d5c83338fb5..a68e533b0a5 100644 --- a/docs/how-to-guides/creating-grc20.md +++ b/docs/how-to-guides/creating-grc20.md @@ -25,12 +25,12 @@ the main functionality of our token factory realm. [embedmd]:# (../assets/how-to-guides/creating-grc20/mytoken-1.gno go) ```go -package mytoken - import ( + "std" + "strings" + "gno.land/p/demo/grc/grc20" "gno.land/p/demo/ufmt" - "std" ) var ( @@ -40,26 +40,31 @@ var ( // init is called once at time of deployment func init() { - // set admin as deployer - admin = std.PrevRealm().Addr() + // Set deployer of Realm to admin + admin = std.PrevRealm().Addr() - // provision the token's name, symbol and number of decimals - mytoken = grc20.NewAdminToken("My Token", "TKN", 4) + // Set token name, symbol and number of decimals + mytoken = grc20.NewAdminToken("My Token", "TKN", 4) - // mint 1 million tokens to admin - mytoken.Mint(admin, 1000000*10000) + // Mint 1 million tokens to admin + mytoken.Mint(admin, 1000000*10000) } + ``` In this code preview, we have: -- Defined a new local variable `mytoken` and assigned that the type of pointer to `grc20.AdminToken`. -- Defined and set the value of local variable `admin` to point to a specific gno.land address of type `std.Address`. -- Set the value of `mytoken` (type `*AdminToken`) to equal the result of creating a new token and configuring its name, symbol + decimal representation. -- Minted 1 million `Mytoken` and set the administrator as the owner of these tokens. +- Defined a new local variable `mytoken` and assigned that the type of +pointer to `grc20.AdminToken`, +- Defined and set the value of local variable `admin` to point to a specific +address of type `std.Address`, +- Initialize `mytoken` as a new GRC20 token, and set its name, symbol, and +decimal values, +- Minted 1 million units of `My Token` and to the admin's address. ## 2. Adding token functionality -The following section will be about introducing Public functions to expose functionality imported from the [grc20 package](https://github.com/gnolang/gno/tree/master/examples/gno.land/p/demo/grc/grc20). +In order to call exported functions from the `grc20` package, we also need to +expose them in the Realm. [embedmd]:# (../assets/how-to-guides/creating-grc20/mytoken-2.gno go) ```go @@ -67,6 +72,10 @@ func TotalSupply() uint64 { return mytoken.TotalSupply() } +func Decimals() uint { + return mytoken.GetDecimals() +} + func BalanceOf(account std.Address) uint64 { balance, err := mytoken.BalanceOf(account) if err != nil { @@ -150,15 +159,24 @@ func Render(path string) string { Detailing what is happening in the above code: - Calling the `TotalSupply` method would return the total number of tokens minted. - Calling the `BalanceOf` method would return the total balance of an account. -- Calling the `Allowance` method would set an account as an allowed spender to serve on behalf of the owner. -- Calling the `transfer` method would transfer a configurable amount of token from the calling account to another account, either owned or unowned. -- Calling the `Approve` method would approve a calling account to spend a configurable amount of token on behalf of the token owner. -- Calling the `TransferFrom` method would transfer a configurable amount of token from an account that granted approval to another account, either owned or unowned. -- Calling the `Mint` method would create a configurable number of tokens by the administrator. -- Calling the `Burn` method would destroy a configurable number of tokens by the administrator. -- Calling the `Render` method would return a user's `balance` as a formatted string. Learn more about the `Render` +- Calling the `Allowance` method would set an account as an allowed spender to +serve on behalf of the owner. +- Calling the `transfer` method would transfer a configurable amount of token +from the calling account to another account, either owned or unowned. +- Calling the `Approve` method would approve a calling account to spend a +configurable amount of token on behalf of the token owner. +- Calling the `TransferFrom` method would transfer a configurable amount of +token from an account that granted approval to another account, either owned or unowned. +- Calling the `Mint` method would create a configurable number of tokens by +the administrator. +- Calling the `Burn` method would destroy a configurable number of tokens by +the administrator. +- Calling the `Render` method would return a user's `balance` as a formatted +string. Learn more about the `Render` method and how it's used [here](../concepts/realms.md). -- Finally, we provide a local function to assert that the calling account is in fact the owner, otherwise panic. This is a very important function that serves to prevent abuse by non-administrators. +- Finally, we provide a local function to assert that the calling account is in +fact the owner, otherwise panic. This is a very important function that serves +to prevent abuse by non-administrators. ## Conclusion From adc7479b8d11c1ccb910a4ec02a5a35bf3cb9300 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 20 Mar 2024 23:46:37 +0100 Subject: [PATCH 24/43] update assets --- .../how-to-guides/creating-grc20/mytoken-1.gno | 15 ++++++++------- .../how-to-guides/creating-grc20/mytoken-2.gno | 4 ++++ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/docs/assets/how-to-guides/creating-grc20/mytoken-1.gno b/docs/assets/how-to-guides/creating-grc20/mytoken-1.gno index 35bb3c57ff9..3de66ff7dc4 100644 --- a/docs/assets/how-to-guides/creating-grc20/mytoken-1.gno +++ b/docs/assets/how-to-guides/creating-grc20/mytoken-1.gno @@ -1,8 +1,9 @@ -package mytoken - import ( - "gno.land/p/demo/grc/grc20" "std" + "strings" + + "gno.land/p/demo/grc/grc20" + "gno.land/p/demo/ufmt" ) var ( @@ -12,12 +13,12 @@ var ( // init is called once at time of deployment func init() { - // set admin as deployer + // Set deployer of Realm to admin admin = std.PrevRealm().Addr() - // provision the token's name, symbol and number of decimals + // Set token name, symbol and number of decimals mytoken = grc20.NewAdminToken("My Token", "TKN", 4) - // mint 1 million tokens to admin + // Mint 1 million tokens to admin mytoken.Mint(admin, 1000000*10000) -} +} \ No newline at end of file diff --git a/docs/assets/how-to-guides/creating-grc20/mytoken-2.gno b/docs/assets/how-to-guides/creating-grc20/mytoken-2.gno index 3acd9158a23..4f8bdcbc1c3 100644 --- a/docs/assets/how-to-guides/creating-grc20/mytoken-2.gno +++ b/docs/assets/how-to-guides/creating-grc20/mytoken-2.gno @@ -2,6 +2,10 @@ func TotalSupply() uint64 { return mytoken.TotalSupply() } +func Decimals() uint { + return mytoken.GetDecimals() +} + func BalanceOf(account std.Address) uint64 { balance, err := mytoken.BalanceOf(account) if err != nil { From 8bfff773e6559d934f5a679887d3165488f3a9e7 Mon Sep 17 00:00:00 2001 From: Leon Hudak <33522493+leohhhn@users.noreply.github.com> Date: Thu, 21 Mar 2024 19:46:18 +0100 Subject: [PATCH 25/43] Update docs/how-to-guides/deploy.md Co-authored-by: Danny --- docs/how-to-guides/deploy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/how-to-guides/deploy.md b/docs/how-to-guides/deploy.md index 82ba4de1155..1d030c93a34 100644 --- a/docs/how-to-guides/deploy.md +++ b/docs/how-to-guides/deploy.md @@ -47,7 +47,7 @@ to deploy to. You can also deploy to a local node from the Playground if you are running one. :::info -A few things to consider when deploying packages and realms: +Here are a few things to keep in mind when deploying packages and realms: - The **name** field in the path should match your package name, in this case `counter` - Packages are usually deployed under `p/`, while realms are deployed under `r/` From 1d145a78694ae60a143115b94e48c97259837b29 Mon Sep 17 00:00:00 2001 From: Leon Hudak <33522493+leohhhn@users.noreply.github.com> Date: Thu, 21 Mar 2024 19:50:04 +0100 Subject: [PATCH 26/43] Apply suggestions from code review Co-authored-by: Danny --- docs/how-to-guides/deploy.md | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/docs/how-to-guides/deploy.md b/docs/how-to-guides/deploy.md index 1d030c93a34..04e848e60af 100644 --- a/docs/how-to-guides/deploy.md +++ b/docs/how-to-guides/deploy.md @@ -6,34 +6,33 @@ id: deploy ## Overview -This guide shows you how to deploy any realm or package to the Gno chain. It will -show you how to: -- Deploy Gno code in your browser via the Playground, -- Deploy Gno code from your local machine using `gnokey`'s `maketx addpkg` API. - +This guide shows you how to deploy any realm or package to the Gno chain, +including how to: +- deploy Gno code in your browser via the Playground, +- deploy Gno code from your local machine using `gnokey`'s `maketx addpkg` API. ## Deployment via the Playground Deployment via the Playground is recommended for smaller realms and packages. For larger projects, it is recommended to write, test, and deploy your code from a local environment. -For this, check out [**Deployment from a local environment**](#deployment-from-a-local-environment). +For this, check out the [**Deployment from a local environment**](#deployment-from-a-local-environment) section. ### Prerequisites - **Internet connection** -- **A keypair in a Gno.land wallet, such as [Adena](https://adena.app)** +- **A Gno.land-compatible wallet, such as [Adena](https://adena.app)** ### Using Gno Playground You can write, test, and deploy packages and realms using Gno Playground. To start using the Playground, you can check out XYZ. -For this example, we will be using the **Counter** realm. You can find the code -on [this Playground link](https://play.gno.land/p/iUWTha99D1J). +For this example, we'll use the **Counter** realm. Access the sample code +via [this Playground link](https://play.gno.land/p/iUWTha99D1J). -Once you have confirmed that the code is without bugs, you can click on "**Deploy**". -If your wallet is not connected, you will receive a prompt to connect it: +Review the code and when you're ready, click on "**Deploy**". +Ensure your wallet is connected to proceed. If it isn't, a prompt will appear for connection: ![DeployConnect](../assets/how-to-guides/deploy/deploy_connect.png) @@ -43,13 +42,13 @@ new toolbox: ![DeployDefault](../assets/how-to-guides/deploy/deploy_default.png) Here, you can choose the deployment path of your realm or package, as well as the network -to deploy to. You can also deploy to a local node from the Playground +to deploy to. The Playground also allows for deployment to a local node if you are running one. :::info Here are a few things to keep in mind when deploying packages and realms: -- The **name** field in the path should match your package name, in this case `counter` -- Packages are usually deployed under `p/`, while realms are deployed under `r/` +- The `name` field in the path should match your package name, in this case `counter`. +- Packages are deployed under `p/`, while realms are deployed under `r/`. An example path for the Counter realm could be the following: ```go @@ -57,8 +56,8 @@ gno.land/r//counter ``` ::: -After choosing a path and network, you can click **Deploy**. This will prompt -a wallet pop-up asking you to sign the deployment transaction. +After choosing a path and network, click on **Deploy**. A pop-up window +from your connected wallet will prompt you to sign and approve the deployment transaction. ![DeployDefault](../assets/how-to-guides/deploy/deploy_success.png) From e53b6acc2b0695196660a9a19994c66edbe43191 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Thu, 21 Mar 2024 19:53:43 +0100 Subject: [PATCH 27/43] remove bad paragraph --- docs/how-to-guides/deploy.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/how-to-guides/deploy.md b/docs/how-to-guides/deploy.md index 04e848e60af..5a38e7c4149 100644 --- a/docs/how-to-guides/deploy.md +++ b/docs/how-to-guides/deploy.md @@ -25,9 +25,6 @@ For this, check out the [**Deployment from a local environment**](#deployment-fr ### Using Gno Playground -You can write, test, and deploy packages and realms using Gno Playground. -To start using the Playground, you can check out XYZ. - For this example, we'll use the **Counter** realm. Access the sample code via [this Playground link](https://play.gno.land/p/iUWTha99D1J). From cac35f0c77368ab6d693ef6431b73a21f64c679d Mon Sep 17 00:00:00 2001 From: leohhhn Date: Thu, 21 Mar 2024 19:56:47 +0100 Subject: [PATCH 28/43] update parag --- docs/how-to-guides/deploy.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/how-to-guides/deploy.md b/docs/how-to-guides/deploy.md index 5a38e7c4149..ea82b6c4e6b 100644 --- a/docs/how-to-guides/deploy.md +++ b/docs/how-to-guides/deploy.md @@ -25,8 +25,9 @@ For this, check out the [**Deployment from a local environment**](#deployment-fr ### Using Gno Playground -For this example, we'll use the **Counter** realm. Access the sample code -via [this Playground link](https://play.gno.land/p/iUWTha99D1J). +To demonstrate deployment via the Playground, we'll use the **Counter** realm. +You can access the sample code via +[this Playground link](https://play.gno.land/p/iUWTha99D1J). Review the code and when you're ready, click on "**Deploy**". Ensure your wallet is connected to proceed. If it isn't, a prompt will appear for connection: From 40223137bfe57e44e17d48039f61783f4f9a095e Mon Sep 17 00:00:00 2001 From: leohhhn Date: Thu, 21 Mar 2024 19:59:42 +0100 Subject: [PATCH 29/43] suggest IDE --- docs/how-to-guides/deploy.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/how-to-guides/deploy.md b/docs/how-to-guides/deploy.md index ea82b6c4e6b..9024ee3941c 100644 --- a/docs/how-to-guides/deploy.md +++ b/docs/how-to-guides/deploy.md @@ -15,7 +15,8 @@ including how to: Deployment via the Playground is recommended for smaller realms and packages. For larger projects, it is recommended to write, test, and deploy your code from -a local environment. +a more appropriate environment, such as a local or online IDE. + For this, check out the [**Deployment from a local environment**](#deployment-from-a-local-environment) section. ### Prerequisites From 4aec3e3f9575c0480e3382a8ab19f1def65ca188 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Sat, 23 Mar 2024 16:56:22 +0900 Subject: [PATCH 30/43] remove useless prerequisites --- docs/how-to-guides/creating-grc20.md | 4 ---- docs/how-to-guides/deploy.md | 1 - docs/how-to-guides/simple-contract.md | 4 ---- docs/how-to-guides/simple-library.md | 4 ---- docs/how-to-guides/testing-gno.md | 4 ---- docs/how-to-guides/write-simple-dapp.md | 4 ---- 6 files changed, 21 deletions(-) diff --git a/docs/how-to-guides/creating-grc20.md b/docs/how-to-guides/creating-grc20.md index a68e533b0a5..08d4c4a376c 100644 --- a/docs/how-to-guides/creating-grc20.md +++ b/docs/how-to-guides/creating-grc20.md @@ -15,10 +15,6 @@ Our **GRC20** Realm will have the following functionality: - Keeping track of total token supply. - Fetching the balance of an account. -## Prerequisites - -- **Internet connection** - ## 1. Importing token package For this realm, we'll want to import the `grc20` package as this will include the main functionality of our token factory realm. diff --git a/docs/how-to-guides/deploy.md b/docs/how-to-guides/deploy.md index 9024ee3941c..37050dea4e1 100644 --- a/docs/how-to-guides/deploy.md +++ b/docs/how-to-guides/deploy.md @@ -21,7 +21,6 @@ For this, check out the [**Deployment from a local environment**](#deployment-fr ### Prerequisites -- **Internet connection** - **A Gno.land-compatible wallet, such as [Adena](https://adena.app)** ### Using Gno Playground diff --git a/docs/how-to-guides/simple-contract.md b/docs/how-to-guides/simple-contract.md index 08e5cdebedf..cbbbd147589 100644 --- a/docs/how-to-guides/simple-contract.md +++ b/docs/how-to-guides/simple-contract.md @@ -17,10 +17,6 @@ Our _Counter_ Realm will have the following functionality: - Incrementing / decrementing the count. - Fetching the current count value. -## Prerequisites - -- **Internet connection** - ## 1. Using Gno Playground When using the Gno Playground, writing, testing, deploying, and sharing Gno code diff --git a/docs/how-to-guides/simple-library.md b/docs/how-to-guides/simple-library.md index 42426d89905..27432246237 100644 --- a/docs/how-to-guides/simple-library.md +++ b/docs/how-to-guides/simple-library.md @@ -13,10 +13,6 @@ intricacies of Packages, please see the [Packages concept page](../concepts/pack The Package we will be writing today will be a simple library for suggesting a random tapas dish. We will define a set list of tapas, and define a method that randomly selects a dish from the list. -## Prerequisites - -- **Internet connection** - ## 1. Using Gno Playground When using the Gno Playground, writing, testing, deploying, and sharing Gno code diff --git a/docs/how-to-guides/testing-gno.md b/docs/how-to-guides/testing-gno.md index c3823174471..89478acde0a 100644 --- a/docs/how-to-guides/testing-gno.md +++ b/docs/how-to-guides/testing-gno.md @@ -11,10 +11,6 @@ Realms and Packages we write. We will go over different CLI tools available to developers, gno testing libraries as well as testing techniques that involve data mocking. -## Prerequisites - -- **Internet connection** - ## Example Realm For the purpose of this guide, we will be testing the simple **Counter** Realm created in diff --git a/docs/how-to-guides/write-simple-dapp.md b/docs/how-to-guides/write-simple-dapp.md index 7543fc83f6e..72501599cef 100644 --- a/docs/how-to-guides/write-simple-dapp.md +++ b/docs/how-to-guides/write-simple-dapp.md @@ -10,10 +10,6 @@ This guide will show you how to write a complete dApp that combines both a packa Our app will allow any user to create a poll, and subsequently vote YAY or NAY for any poll that has not exceeded the voting deadline. -## Prerequisites - -- **Internet connection** - ## Defining dApp functionality Our dApp will consist of a Poll package, which will handle all things related to the Poll struct, From 71795e8eb2d627b5ad41c5c681fa0c2747cebf1c Mon Sep 17 00:00:00 2001 From: Leon Hudak <33522493+leohhhn@users.noreply.github.com> Date: Sat, 23 Mar 2024 17:02:27 +0900 Subject: [PATCH 31/43] Apply suggestions from code review Co-authored-by: Danny --- docs/how-to-guides/creating-grc20.md | 38 ++++++++++++++-------------- docs/how-to-guides/deploy.md | 2 +- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/how-to-guides/creating-grc20.md b/docs/how-to-guides/creating-grc20.md index 08d4c4a376c..c6dd502076c 100644 --- a/docs/how-to-guides/creating-grc20.md +++ b/docs/how-to-guides/creating-grc20.md @@ -2,10 +2,10 @@ id: creating-grc20 --- -# How to create a GRC20 Token +# How to Create a GRC20 Token ## Overview -This guide shows you how to write a simple **GRC20** Smart Contract, or rather +This guide shows you how to write a simple **GRC20** a [Realm](../concepts/realms.md), in [Gno](../concepts/gno-language.md). For actually deploying the Realm, please see the [deployment](deploy.md) guide. @@ -16,7 +16,7 @@ Our **GRC20** Realm will have the following functionality: - Fetching the balance of an account. ## 1. Importing token package -For this realm, we'll want to import the `grc20` package as this will include +For this realm, we import the `grc20` package, as this includes the main functionality of our token factory realm. [embedmd]:# (../assets/how-to-guides/creating-grc20/mytoken-1.gno go) @@ -48,14 +48,14 @@ func init() { ``` -In this code preview, we have: -- Defined a new local variable `mytoken` and assigned that the type of -pointer to `grc20.AdminToken`, -- Defined and set the value of local variable `admin` to point to a specific +In this code preview, we will: +- Defines a new token variable (local), `mytoken`, and assigns it to a +pointer, `grc20.AdminToken`, +- Defines and sets the value of an administrator variable (local), `admin`, to point to a specific address of type `std.Address`, -- Initialize `mytoken` as a new GRC20 token, and set its name, symbol, and +- Initializes `mytoken` as a new GRC20 token, and set its name, symbol, and decimal values, -- Minted 1 million units of `My Token` and to the admin's address. +- Mint 1 million units of `My Token` and assign them to the admin's address. ## 2. Adding token functionality @@ -157,22 +157,22 @@ Detailing what is happening in the above code: - Calling the `BalanceOf` method would return the total balance of an account. - Calling the `Allowance` method would set an account as an allowed spender to serve on behalf of the owner. -- Calling the `transfer` method would transfer a configurable amount of token +- Calling the `transfer` method transfers a configurable amount of token from the calling account to another account, either owned or unowned. -- Calling the `Approve` method would approve a calling account to spend a -configurable amount of token on behalf of the token owner. -- Calling the `TransferFrom` method would transfer a configurable amount of +- Calling the `Approve` method approves a calling account to spend a +configurable amount of token(s) on behalf of the token owner. +- Calling the `TransferFrom` method transfers a configurable amount of token from an account that granted approval to another account, either owned or unowned. -- Calling the `Mint` method would create a configurable number of tokens by +- Calling the `Mint` method creates a configurable number of tokens by the administrator. -- Calling the `Burn` method would destroy a configurable number of tokens by +- Calling the `Burn` method destroys a configurable number of tokens by the administrator. -- Calling the `Render` method would return a user's `balance` as a formatted +- Calling the `Render` method returns a user's `balance` as a formatted string. Learn more about the `Render` method and how it's used [here](../concepts/realms.md). -- Finally, we provide a local function to assert that the calling account is in -fact the owner, otherwise panic. This is a very important function that serves -to prevent abuse by non-administrators. +- Lastly, we provide a local function designed to verify that the calling account is +indeed the owner; it triggers a panic if this is not the case. This critical function acts +as a safeguard to prevent unauthorized actions by non-administrators. ## Conclusion diff --git a/docs/how-to-guides/deploy.md b/docs/how-to-guides/deploy.md index 37050dea4e1..54a0ef582ce 100644 --- a/docs/how-to-guides/deploy.md +++ b/docs/how-to-guides/deploy.md @@ -25,7 +25,7 @@ For this, check out the [**Deployment from a local environment**](#deployment-fr ### Using Gno Playground -To demonstrate deployment via the Playground, we'll use the **Counter** realm. +To demonstrate deployment using the Playground, we'll use the **Counter** realm. You can access the sample code via [this Playground link](https://play.gno.land/p/iUWTha99D1J). From fe6255c51c93ed22c638b24ab017ce26d84ba639 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Mon, 25 Mar 2024 12:06:50 +0900 Subject: [PATCH 32/43] fixup poll --- docs/how-to-guides/write-simple-dapp.md | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/docs/how-to-guides/write-simple-dapp.md b/docs/how-to-guides/write-simple-dapp.md index 72501599cef..a46d6688fa9 100644 --- a/docs/how-to-guides/write-simple-dapp.md +++ b/docs/how-to-guides/write-simple-dapp.md @@ -133,7 +133,7 @@ The realm will contain the following functionality: package poll import ( - "bytes" + "bytes" "std" "gno.land/p/demo/avl" @@ -213,16 +213,9 @@ import path in the realm code. ::: With that we have written the core functionality of the realm, and all that is left is -the [Render function](http://localhost:3000/explanation/realms). +the [Render function](../concepts/realms.md). Its purpose is to help us display the state of the realm in Markdown, by formatting the state into a string buffer: -Add this library: -```go -import ( - ... - "bytes" -) -``` [embedmd]:# (../assets/how-to-guides/write-simple-dapp/poll-3.gno go) ```go From 5e690d4d6f25d6943d6454b72b84b54274f876be Mon Sep 17 00:00:00 2001 From: leohhhn Date: Mon, 25 Mar 2024 12:26:06 +0900 Subject: [PATCH 33/43] add amt checks to mytoken --- .../creating-grc20/mytoken-2.gno | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/docs/assets/how-to-guides/creating-grc20/mytoken-2.gno b/docs/assets/how-to-guides/creating-grc20/mytoken-2.gno index 4f8bdcbc1c3..72bcf126e1c 100644 --- a/docs/assets/how-to-guides/creating-grc20/mytoken-2.gno +++ b/docs/assets/how-to-guides/creating-grc20/mytoken-2.gno @@ -1,3 +1,7 @@ +package mytoken + +import "github.com/gnolang/gno/examples/gno.land/p/demo/ufmt" + func TotalSupply() uint64 { return mytoken.TotalSupply() } @@ -40,14 +44,22 @@ func Approve(spender std.Address, amount uint64) { func TransferFrom(from, to std.Address, amount uint64) { caller := std.PrevRealm().Addr() + + if amount <= 0 { + panic("transfer amount must be greater than zero") + } + if err := mytoken.TransferFrom(caller, from, to, amount); err != nil { panic(err) } } func Mint(address std.Address, amount uint64) { - caller := std.PrevRealm().Addr() - assertIsAdmin(caller) + assertIsAdmin(std.PrevRealm().Addr()) + + if amount <= 0 { + panic("mint amount must be greater than zero") + } if err := mytoken.Mint(address, amount); err != nil { panic(err) @@ -55,8 +67,11 @@ func Mint(address std.Address, amount uint64) { } func Burn(address std.Address, amount uint64) { - caller := std.PrevRealm().Addr() - assertIsAdmin(caller) + assertIsAdmin(std.PrevRealm().Addr()) + + if amount <= 0 { + panic("burn amount must be greater than zero") + } if err := mytoken.Burn(address, amount); err != nil { panic(err) From eb208f3e6833a891c6f3624ea11cda675d85fb13 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Mon, 25 Mar 2024 14:16:56 +0900 Subject: [PATCH 34/43] save --- docs/how-to-guides/creating-grc20.md | 48 ++++++++++++++++++---------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/docs/how-to-guides/creating-grc20.md b/docs/how-to-guides/creating-grc20.md index c6dd502076c..c30843d3422 100644 --- a/docs/how-to-guides/creating-grc20.md +++ b/docs/how-to-guides/creating-grc20.md @@ -21,6 +21,8 @@ the main functionality of our token factory realm. [embedmd]:# (../assets/how-to-guides/creating-grc20/mytoken-1.gno go) ```go +package mytoken + import ( "std" "strings" @@ -36,23 +38,22 @@ var ( // init is called once at time of deployment func init() { - // Set deployer of Realm to admin - admin = std.PrevRealm().Addr() + // Set deployer of Realm to admin + admin = std.PrevRealm().Addr() - // Set token name, symbol and number of decimals - mytoken = grc20.NewAdminToken("My Token", "TKN", 4) + // Set token name, symbol and number of decimals + mytoken = grc20.NewAdminToken("My Token", "TKN", 4) - // Mint 1 million tokens to admin - mytoken.Mint(admin, 1000000*10000) + // Mint 1 million tokens to admin + mytoken.Mint(admin, 1000000*10000) } - ``` -In this code preview, we will: -- Defines a new token variable (local), `mytoken`, and assigns it to a -pointer, `grc20.AdminToken`, -- Defines and sets the value of an administrator variable (local), `admin`, to point to a specific -address of type `std.Address`, +The code snippet above does the following: +- Defines a new token variable, `mytoken`, and assigns it to a +pointer to the GRC20 token type, `grc20.AdminToken`, +- Defines and sets the value of `admin` with a type of `std.Address` to contain +the address of the deployer - Initializes `mytoken` as a new GRC20 token, and set its name, symbol, and decimal values, - Mint 1 million units of `My Token` and assign them to the admin's address. @@ -106,14 +107,22 @@ func Approve(spender std.Address, amount uint64) { func TransferFrom(from, to std.Address, amount uint64) { caller := std.PrevRealm().Addr() + + if amount <= 0 { + panic("transfer amount must be greater than zero") + } + if err := mytoken.TransferFrom(caller, from, to, amount); err != nil { panic(err) } } func Mint(address std.Address, amount uint64) { - caller := std.PrevRealm().Addr() - assertIsAdmin(caller) + assertIsAdmin(std.PrevRealm().Addr()) + + if amount <= 0 { + panic("mint amount must be greater than zero") + } if err := mytoken.Mint(address, amount); err != nil { panic(err) @@ -121,8 +130,11 @@ func Mint(address std.Address, amount uint64) { } func Burn(address std.Address, amount uint64) { - caller := std.PrevRealm().Addr() - assertIsAdmin(caller) + assertIsAdmin(std.PrevRealm().Addr()) + + if amount <= 0 { + panic("burn amount must be greater than zero") + } if err := mytoken.Burn(address, amount); err != nil { panic(err) @@ -174,6 +186,10 @@ string. Learn more about the `Render` indeed the owner; it triggers a panic if this is not the case. This critical function acts as a safeguard to prevent unauthorized actions by non-administrators. + +You can view the full code on [this Playground link](https://play.gno.land/p/1UXqufodX6f). + + ## Conclusion That's it 🎉 From 0875c674e3c629214d39975fdb60262a5b84c433 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Mon, 25 Mar 2024 23:36:17 +0900 Subject: [PATCH 35/43] remove grc721 for now --- .../creating-grc20/mytoken-1.gno | 3 +- .../creating-grc20/mytoken-2.gno | 4 - .../creating-grc721/mynonfungibletoken-1.gno | 101 ++++++++++++++++-- docs/how-to-guides/creating-grc20.md | 3 +- docs/how-to-guides/creating-grc721.md | 39 +++---- misc/docusaurus/sidebars.js | 1 - 6 files changed, 118 insertions(+), 33 deletions(-) diff --git a/docs/assets/how-to-guides/creating-grc20/mytoken-1.gno b/docs/assets/how-to-guides/creating-grc20/mytoken-1.gno index 3de66ff7dc4..2a9856bb6d6 100644 --- a/docs/assets/how-to-guides/creating-grc20/mytoken-1.gno +++ b/docs/assets/how-to-guides/creating-grc20/mytoken-1.gno @@ -21,4 +21,5 @@ func init() { // Mint 1 million tokens to admin mytoken.Mint(admin, 1000000*10000) -} \ No newline at end of file +} + diff --git a/docs/assets/how-to-guides/creating-grc20/mytoken-2.gno b/docs/assets/how-to-guides/creating-grc20/mytoken-2.gno index 72bcf126e1c..128e605da73 100644 --- a/docs/assets/how-to-guides/creating-grc20/mytoken-2.gno +++ b/docs/assets/how-to-guides/creating-grc20/mytoken-2.gno @@ -1,7 +1,3 @@ -package mytoken - -import "github.com/gnolang/gno/examples/gno.land/p/demo/ufmt" - func TotalSupply() uint64 { return mytoken.TotalSupply() } diff --git a/docs/assets/how-to-guides/creating-grc721/mynonfungibletoken-1.gno b/docs/assets/how-to-guides/creating-grc721/mynonfungibletoken-1.gno index 14e25bd7264..ff567f47a9c 100644 --- a/docs/assets/how-to-guides/creating-grc721/mynonfungibletoken-1.gno +++ b/docs/assets/how-to-guides/creating-grc721/mynonfungibletoken-1.gno @@ -1,17 +1,104 @@ -package mynonfungibletoken +package mynft import ( + "github.com/gnolang/gno/examples/gno.land/p/demo/grc/grc721" "std" - - "gno.land/p/demo/grc/grc721" + //"gno.land/p/demo/grc/grc721" ) var ( - admin std.Address = "g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj" // set admin account - // provision the token's name and symbol - mynonfungibletoken = grc721.NewBasicNFT("mynonfungibletoken", "MNFT") + mynft *grc721.IGRC721 + admin std.Address ) +// init is called once at time of deployment func init() { - mintNNFT(admin, 10) // @administrator (supply = 10) + // Set deployer of Realm to admin + admin = std.PrevRealm().Addr() + + // Set token name, symbol and number of decimals + mynft = grc721.NewBasicNFT("My NFT", "MNFT") +} + +func BalanceOf(owner std.Address) uint64 { + balance, err := mynft.BalanceOf(owner) + if err != nil { + panic(err) + } + + return balance +} + +func OwnerOf(tid grc721.TokenID) std.Address { + owner, err := mynft.OwnerOf(tid) + if err != nil { + panic(err) + } + + return owner +} + +func IsApprovedForAll(owner, operator std.Address) bool { + return mynft.IsApprovedForAll(owner, operator) +} + +func GetApproved(tid grc721.TokenID) std.Address { + addr, err := mynft.GetApproved(tid) + if err != nil { + panic(err) + } + + return addr +} + +func Approve(to std.Address, tid grc721.TokenID) { + err := mynft.Approve(to, tid) + if err != nil { + panic(err) + } +} + +func SetApprovalForAll(operator std.Address, approved bool) { + err := mynft.SetApprovalForAll(operator, approved) + if err != nil { + panic(err) + } +} + +func TransferFrom(from, to std.Address, tid grc721.TokenID) { + err := mynft.TransferFrom(from, to, tid) + if err != nil { + panic(err) + } +} + +func Mint(to std.Address, tid grc721.TokenID) { + assertIsAdmin(std.PrevRealm().Addr()) + err := mynft.Mint(to, tid) + if err != nil { + panic(err) + } +} + +func Burn(tid grc721.TokenID) { + assertIsAdmin(std.PrevRealm().Addr()) + err := mynft.Burn(tid) + if err != nil { + panic(err) + } +} + +func Render(path string) string { + switch { + case path == "": + return mynft.RenderHome() + default: + return "404\n" + } +} + +func assertIsAdmin(address std.Address) { + if address != admin { + panic("restricted access") + } } diff --git a/docs/how-to-guides/creating-grc20.md b/docs/how-to-guides/creating-grc20.md index c30843d3422..2422baaab2a 100644 --- a/docs/how-to-guides/creating-grc20.md +++ b/docs/how-to-guides/creating-grc20.md @@ -17,7 +17,8 @@ Our **GRC20** Realm will have the following functionality: ## 1. Importing token package For this realm, we import the `grc20` package, as this includes -the main functionality of our token factory realm. +the main functionality of our token realm. The package can be found the +`gno.land/p/demo/grc/grc20` path. [embedmd]:# (../assets/how-to-guides/creating-grc20/mytoken-1.gno go) ```go diff --git a/docs/how-to-guides/creating-grc721.md b/docs/how-to-guides/creating-grc721.md index f8049e8a135..4bf93c9c3eb 100644 --- a/docs/how-to-guides/creating-grc721.md +++ b/docs/how-to-guides/creating-grc721.md @@ -6,31 +6,25 @@ id: creating-grc721 ## Overview -This guide shows you how to write a simple _GRC721_ Smart Contract, or rather a [Realm](../concepts/realms.md), -in [Gno (Gnolang)](../concepts/gno-language.md). For actually deploying the Realm, please see -the [deployment](deploy.md) guide. +This guide shows you how to write a simple **GRC721** Smart Contract, or rather +a [Realm](../concepts/realms.md), in [Gno](../concepts/gno-language.md). +For actually deploying the Realm, please see the [deployment](deploy.md) guide. -Our _GRC721_ Realm will have the following functionality: +Our **GRC721** Realm will have the following functionality: -- Minting a configurable amount of token. +- Minting a configurable amount of tokens. - Keeping track of total token supply. - Fetching the balance of an account. -## Prerequisites - -We will proceed using the typical directory structure for a Realm found within -the [simple-contract guide](simple-contract.md). It is also worthwhile to consult -the [GRC721 interface](https://github.com/gnolang/gno/blob/master/examples/gno.land/p/demo/grc/grc721/igrc721.gno) which we will be borrowing from within -this guide. - ## 1. Importing token package -For this realm, we'll want to import the `grc20` package as this will include the main functionality of our token -factory realm. +For this realm, we'll want to import the `grc721` package as this will include +the main functionality of our NFT realm. The package can be found the +`gno.land/p/demo/grc/grc721` path. [embedmd]:# (../assets/how-to-guides/creating-grc721/mynonfungibletoken-1.gno go) ```go -package mynonfungibletoken +package mynft import ( "std" @@ -39,13 +33,20 @@ import ( ) var ( - admin std.Address = "g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj" // set admin account - // provision the token's name and symbol - mynonfungibletoken = grc721.NewBasicNFT("mynonfungibletoken", "MNFT") + mytoken *grc20.AdminToken + admin std.Address ) +// init is called once at time of deployment func init() { - mintNNFT(admin, 10) // @administrator (supply = 10) + // Set deployer of Realm to admin + admin = std.PrevRealm().Addr() + + // Set token name, symbol and number of decimals + mynft = grc721.NewBasicNFT("My NFT", "MNFT") + + // Mint 1 million tokens to admin + mytoken.Mint(admin, 1000000*10000) } ``` diff --git a/misc/docusaurus/sidebars.js b/misc/docusaurus/sidebars.js index c878dea87c0..3c6bd23132c 100644 --- a/misc/docusaurus/sidebars.js +++ b/misc/docusaurus/sidebars.js @@ -34,7 +34,6 @@ const sidebars = { 'how-to-guides/deploy', 'how-to-guides/write-simple-dapp', 'how-to-guides/creating-grc20', - 'how-to-guides/creating-grc721', 'how-to-guides/connect-wallet-dapp', ], }, From c2d43d5d33dc1229640ae0e172e1a3e03872c67c Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 26 Mar 2024 11:01:28 +0900 Subject: [PATCH 36/43] add comments to grc20 --- .../how-to-guides/creating-grc20/mytoken-2.gno | 13 +++++++++++++ docs/how-to-guides/creating-grc20.md | 16 +++++++++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/docs/assets/how-to-guides/creating-grc20/mytoken-2.gno b/docs/assets/how-to-guides/creating-grc20/mytoken-2.gno index 128e605da73..3ce2346b903 100644 --- a/docs/assets/how-to-guides/creating-grc20/mytoken-2.gno +++ b/docs/assets/how-to-guides/creating-grc20/mytoken-2.gno @@ -1,11 +1,14 @@ +// TotalSupply returns the total supply of mytoken func TotalSupply() uint64 { return mytoken.TotalSupply() } +// Decimals returns the number of decimals of mytoken func Decimals() uint { return mytoken.GetDecimals() } +// BalanceOf returns the balance mytoken for `account` func BalanceOf(account std.Address) uint64 { balance, err := mytoken.BalanceOf(account) if err != nil { @@ -15,6 +18,7 @@ func BalanceOf(account std.Address) uint64 { return balance } +// Allowance returns the allowance of spender on owner's balance func Allowance(owner, spender std.Address) uint64 { allowance, err := mytoken.Allowance(owner, spender) if err != nil { @@ -24,6 +28,7 @@ func Allowance(owner, spender std.Address) uint64 { return allowance } +// Transfer transfers amount from caller to recipient func Transfer(recipient std.Address, amount uint64) { caller := std.PrevRealm().Addr() if err := mytoken.Transfer(caller, recipient, amount); err != nil { @@ -31,6 +36,7 @@ func Transfer(recipient std.Address, amount uint64) { } } +// Approve approves amount of caller's tokens to be spent by spender func Approve(spender std.Address, amount uint64) { caller := std.PrevRealm().Addr() if err := mytoken.Approve(caller, spender, amount); err != nil { @@ -38,6 +44,7 @@ func Approve(spender std.Address, amount uint64) { } } +// TransferFrom transfers `amount` of tokens from `from` to `to` func TransferFrom(from, to std.Address, amount uint64) { caller := std.PrevRealm().Addr() @@ -50,6 +57,7 @@ func TransferFrom(from, to std.Address, amount uint64) { } } +// Mint mints amount of tokens to address. Callable only by admin of token func Mint(address std.Address, amount uint64) { assertIsAdmin(std.PrevRealm().Addr()) @@ -62,6 +70,7 @@ func Mint(address std.Address, amount uint64) { } } +// Burn burns amount of tokens from address. Callable only by admin of token func Burn(address std.Address, amount uint64) { assertIsAdmin(std.PrevRealm().Addr()) @@ -74,20 +83,24 @@ func Burn(address std.Address, amount uint64) { } } +// assertIsAdmin asserts the address is the admin of token func assertIsAdmin(address std.Address) { if address != admin { panic("restricted access") } } +// Render renders the state of the realm func Render(path string) string { parts := strings.Split(path, "/") c := len(parts) switch { case path == "": + // Default GRC20 render return mytoken.RenderHome() case c == 2 && parts[0] == "balance": + // Render balance of specific address owner := std.Address(parts[1]) balance, _ := mytoken.BalanceOf(owner) return ufmt.Sprintf("%d\n", balance) diff --git a/docs/how-to-guides/creating-grc20.md b/docs/how-to-guides/creating-grc20.md index 2422baaab2a..c8792bd6e90 100644 --- a/docs/how-to-guides/creating-grc20.md +++ b/docs/how-to-guides/creating-grc20.md @@ -66,14 +66,16 @@ expose them in the Realm. [embedmd]:# (../assets/how-to-guides/creating-grc20/mytoken-2.gno go) ```go +// TotalSupply returns the total supply of mytoken func TotalSupply() uint64 { return mytoken.TotalSupply() } - +// Decimals returns the number of decimals of mytoken func Decimals() uint { return mytoken.GetDecimals() } +// BalanceOf returns the balance mytoken for `account` func BalanceOf(account std.Address) uint64 { balance, err := mytoken.BalanceOf(account) if err != nil { @@ -83,6 +85,7 @@ func BalanceOf(account std.Address) uint64 { return balance } +// Allowance returns the allowance of spender on owner's balance func Allowance(owner, spender std.Address) uint64 { allowance, err := mytoken.Allowance(owner, spender) if err != nil { @@ -92,6 +95,7 @@ func Allowance(owner, spender std.Address) uint64 { return allowance } +// Transfer transfers amount from caller to recipient func Transfer(recipient std.Address, amount uint64) { caller := std.PrevRealm().Addr() if err := mytoken.Transfer(caller, recipient, amount); err != nil { @@ -99,6 +103,7 @@ func Transfer(recipient std.Address, amount uint64) { } } +// Approve approves amount of caller's tokens to be spent by spender func Approve(spender std.Address, amount uint64) { caller := std.PrevRealm().Addr() if err := mytoken.Approve(caller, spender, amount); err != nil { @@ -106,6 +111,7 @@ func Approve(spender std.Address, amount uint64) { } } +// TransferFrom transfers `amount` of tokens from `from` to `to` func TransferFrom(from, to std.Address, amount uint64) { caller := std.PrevRealm().Addr() @@ -118,6 +124,7 @@ func TransferFrom(from, to std.Address, amount uint64) { } } +// Mint mints amount of tokens to address. Callable only by admin of token func Mint(address std.Address, amount uint64) { assertIsAdmin(std.PrevRealm().Addr()) @@ -130,6 +137,7 @@ func Mint(address std.Address, amount uint64) { } } +// Burn burns amount of tokens from address. Callable only by admin of token func Burn(address std.Address, amount uint64) { assertIsAdmin(std.PrevRealm().Addr()) @@ -142,20 +150,24 @@ func Burn(address std.Address, amount uint64) { } } +// assertIsAdmin asserts the address is the admin of token func assertIsAdmin(address std.Address) { if address != admin { panic("restricted access") } } +// Render renders the state of the realm func Render(path string) string { parts := strings.Split(path, "/") c := len(parts) switch { case path == "": + // Default GRC20 render return mytoken.RenderHome() case c == 2 && parts[0] == "balance": + // Render balance of specific address owner := std.Address(parts[1]) balance, _ := mytoken.BalanceOf(owner) return ufmt.Sprintf("%d\n", balance) @@ -187,10 +199,8 @@ string. Learn more about the `Render` indeed the owner; it triggers a panic if this is not the case. This critical function acts as a safeguard to prevent unauthorized actions by non-administrators. - You can view the full code on [this Playground link](https://play.gno.land/p/1UXqufodX6f). - ## Conclusion That's it 🎉 From dcdf60799dfbfb64df15e7113e7e9ba2a9b61d78 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 26 Mar 2024 11:15:47 +0900 Subject: [PATCH 37/43] add css to images (thx @alexiscolin!), remove italic --- docs/how-to-guides/simple-contract.md | 2 +- misc/docusaurus/src/css/custom.css | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/how-to-guides/simple-contract.md b/docs/how-to-guides/simple-contract.md index cbbbd147589..8741d9513d8 100644 --- a/docs/how-to-guides/simple-contract.md +++ b/docs/how-to-guides/simple-contract.md @@ -11,7 +11,7 @@ in [Gno](../concepts/gno-language.md). For actually deploying the Realm, please see the [deployment](deploy.md) guide. -Our _Counter_ Realm will have the following functionality: +Our **Counter** Realm will have the following functionality: - Keeping track of the current count. - Incrementing / decrementing the count. diff --git a/misc/docusaurus/src/css/custom.css b/misc/docusaurus/src/css/custom.css index a0512068943..1cf4968ad03 100644 --- a/misc/docusaurus/src/css/custom.css +++ b/misc/docusaurus/src/css/custom.css @@ -341,3 +341,10 @@ a.footer__link-item > svg { font-size: 0.8em; } } + +.container img { + margin: auto; + display: block; + border-radius: 0.5rem; + margin-block: 3rem; +} \ No newline at end of file From efc1d41f613f68a6faa779f921991670a7da5b60 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 2 Apr 2024 14:51:58 +0900 Subject: [PATCH 38/43] update grc20 to have better sections --- docs/how-to-guides/creating-grc20.md | 75 +++++++++++++++++----------- docs/how-to-guides/testing-gno.md | 2 +- 2 files changed, 48 insertions(+), 29 deletions(-) diff --git a/docs/how-to-guides/creating-grc20.md b/docs/how-to-guides/creating-grc20.md index c8792bd6e90..8b950074f81 100644 --- a/docs/how-to-guides/creating-grc20.md +++ b/docs/how-to-guides/creating-grc20.md @@ -62,19 +62,27 @@ decimal values, ## 2. Adding token functionality In order to call exported functions from the `grc20` package, we also need to -expose them in the Realm. +expose them in the realm. Let's go through all functions in the GRC20 package, +one by one: -[embedmd]:# (../assets/how-to-guides/creating-grc20/mytoken-2.gno go) ```go // TotalSupply returns the total supply of mytoken func TotalSupply() uint64 { return mytoken.TotalSupply() } + +``` +Calling the `TotalSupply` method would return the total number of tokens minted. + +```go // Decimals returns the number of decimals of mytoken func Decimals() uint { return mytoken.GetDecimals() } +``` +Calling the `Decimals` method would return number of decimals of the token. +```go // BalanceOf returns the balance mytoken for `account` func BalanceOf(account std.Address) uint64 { balance, err := mytoken.BalanceOf(account) @@ -84,7 +92,11 @@ func BalanceOf(account std.Address) uint64 { return balance } +``` + +Calling the `BalanceOf` method would return the total balance of an account. +```go // Allowance returns the allowance of spender on owner's balance func Allowance(owner, spender std.Address) uint64 { allowance, err := mytoken.Allowance(owner, spender) @@ -94,7 +106,11 @@ func Allowance(owner, spender std.Address) uint64 { return allowance } +``` +Calling the `Allowance` method will return the amount `spender` is allowed to spend +from `owner`'s balance. +```go // Transfer transfers amount from caller to recipient func Transfer(recipient std.Address, amount uint64) { caller := std.PrevRealm().Addr() @@ -102,17 +118,24 @@ func Transfer(recipient std.Address, amount uint64) { panic(err) } } +``` +Calling the `Transfer` method transfers amount of token from the calling account +to the recipient account. -// Approve approves amount of caller's tokens to be spent by spender +```go func Approve(spender std.Address, amount uint64) { caller := std.PrevRealm().Addr() if err := mytoken.Approve(caller, spender, amount); err != nil { panic(err) } } +``` +Calling the `Approve` method approves `spender` to spend `amount` from the caller's +balance of tokens. +```go // TransferFrom transfers `amount` of tokens from `from` to `to` -func TransferFrom(from, to std.Address, amount uint64) { +func TransferFrom(sender, recipient std.Address, amount uint64) { caller := std.PrevRealm().Addr() if amount <= 0 { @@ -123,7 +146,12 @@ func TransferFrom(from, to std.Address, amount uint64) { panic(err) } } +``` +Calling the `TransferFrom` method moves `amount` of tokens from `sender` to +`recipient` using the allowance mechanism. `amount` is then deducted from the +caller’s allowance. +```go // Mint mints amount of tokens to address. Callable only by admin of token func Mint(address std.Address, amount uint64) { assertIsAdmin(std.PrevRealm().Addr()) @@ -136,7 +164,11 @@ func Mint(address std.Address, amount uint64) { panic(err) } } +``` +Calling the `Mint` method creates `amount` of tokens and assigns them to `address`, +increasing the total supply. +```go // Burn burns amount of tokens from address. Callable only by admin of token func Burn(address std.Address, amount uint64) { assertIsAdmin(std.PrevRealm().Addr()) @@ -149,14 +181,22 @@ func Burn(address std.Address, amount uint64) { panic(err) } } +``` +Calling the `Mint` method burns `amount` of tokens from the balance of `address`, +decreasing the total supply. +```go // assertIsAdmin asserts the address is the admin of token func assertIsAdmin(address std.Address) { if address != admin { panic("restricted access") } } +``` +Calling the `assertIsAdmin` method checks if `address` is equal to the +package-level `admin` variable. +```go // Render renders the state of the realm func Render(path string) string { parts := strings.Split(path, "/") @@ -176,33 +216,12 @@ func Render(path string) string { } } ``` +Calling the `Render` method returns a general render of the GRC20 realm, or +if given a specific address, the user's `balance` as a formatted string. -Detailing what is happening in the above code: -- Calling the `TotalSupply` method would return the total number of tokens minted. -- Calling the `BalanceOf` method would return the total balance of an account. -- Calling the `Allowance` method would set an account as an allowed spender to -serve on behalf of the owner. -- Calling the `transfer` method transfers a configurable amount of token -from the calling account to another account, either owned or unowned. -- Calling the `Approve` method approves a calling account to spend a -configurable amount of token(s) on behalf of the token owner. -- Calling the `TransferFrom` method transfers a configurable amount of -token from an account that granted approval to another account, either owned or unowned. -- Calling the `Mint` method creates a configurable number of tokens by -the administrator. -- Calling the `Burn` method destroys a configurable number of tokens by -the administrator. -- Calling the `Render` method returns a user's `balance` as a formatted -string. Learn more about the `Render` - method and how it's used [here](../concepts/realms.md). -- Lastly, we provide a local function designed to verify that the calling account is -indeed the owner; it triggers a panic if this is not the case. This critical function acts -as a safeguard to prevent unauthorized actions by non-administrators. - -You can view the full code on [this Playground link](https://play.gno.land/p/1UXqufodX6f). +You can view the full code on [this Playground link](https://play.gno.land/p/km7Ja6WDQoL). ## Conclusion - That's it 🎉 You have successfully built a simple GRC20 Realm that is ready to be deployed on the Gno chain and called by users. diff --git a/docs/how-to-guides/testing-gno.md b/docs/how-to-guides/testing-gno.md index 89478acde0a..dc84ae6bcc9 100644 --- a/docs/how-to-guides/testing-gno.md +++ b/docs/how-to-guides/testing-gno.md @@ -41,7 +41,7 @@ func Render(_ string) string { Visit [this Playground link](https://play.gno.land/p/XbkFKAIpLO8) to get started. -## 1. Writing the Gno test +## 1. Testing in Gno Gno tests are written in the same manner and format as regular Go tests, just in `_test.gno` files. From b3e6fac02d005548c0082c0cd04eb14833ca621c Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 2 Apr 2024 17:10:59 +0900 Subject: [PATCH 39/43] save --- docs/how-to-guides/simple-contract.md | 67 +++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/docs/how-to-guides/simple-contract.md b/docs/how-to-guides/simple-contract.md index 8741d9513d8..aa549b218f6 100644 --- a/docs/how-to-guides/simple-contract.md +++ b/docs/how-to-guides/simple-contract.md @@ -6,18 +6,79 @@ id: simple-contract ## Overview -This guide shows you how to write a simple **Counter** Smart Contract, or rather a [Realm](../concepts/realms.md), +This guide shows you how to write a simple **Counter** Smart Contract, or rather a [realm](../concepts/realms.md), in [Gno](../concepts/gno-language.md). For actually deploying the Realm, please see the [deployment](deploy.md) guide. -Our **Counter** Realm will have the following functionality: +Our **Counter** realm will have the following functionality: - Keeping track of the current count. - Incrementing / decrementing the count. - Fetching the current count value. -## 1. Using Gno Playground +## 1. Environment +Currently, Gno apps can be developed locally or via the online editor, Gno +Playground. Below we detail how to set up and use both. + +### Local setup +To get started with a local setup +Gno Realms can be typically written anywhere, under any structure, just like +regular Go code. However, Gno developers have adopted a standard of organizing +Gno logic under a specific directory hierarchy, which we +will explore here. + +Create the main working directory for our Realm: + +```bash +mkdir counter-app +``` + +Since we are building a simple _Counter_ Realm, inside our created `counter-app` directory, we can create another +directory named `r`, which stands for `realm`: + +```bash +cd counter-app +mkdir r +``` + +Alternatively, if we were writing a [Gno Package](../concepts/packages.md), we would denote this directory name +as `p` (for `package`). You can learn more about Packages in our [Package development guide](simple-library.md). + +Additionally, we will create another sub-folder that will house our Realm code, named `counter`: + +```bash +cd r +mkdir counter +``` + +After setting up our work directory structure, we should have something like this: + +```text +counter-app/ +├─ r/ +│ ├─ counter/ +│ │ ├─ // source code here +``` + +### 2. Create `counter.gno` + +Now that the work directory structure is set up, we can go into the `counter` sub-folder, and actually create +our _Counter_ Smart Contract: + +```bash +cd counter +touch counter.gno +``` + +:::info Gno file extension +All Gno (Gnolang) source code has the file extension `.gno`. + +This file extension is required for existing gno tools and processes to work. +::: + + +### Using the Gno Playground When using the Gno Playground, writing, testing, deploying, and sharing Gno code is simple. This makes it perfect for getting started with Gno. From 504ed921b7968539618b04a2c0d8083fd406535c Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 2 Apr 2024 17:53:57 +0900 Subject: [PATCH 40/43] add local setup to simple realm --- docs/how-to-guides/simple-contract.md | 42 +++++++++++++++------------ 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/docs/how-to-guides/simple-contract.md b/docs/how-to-guides/simple-contract.md index aa549b218f6..2840452049a 100644 --- a/docs/how-to-guides/simple-contract.md +++ b/docs/how-to-guides/simple-contract.md @@ -22,30 +22,30 @@ Currently, Gno apps can be developed locally or via the online editor, Gno Playground. Below we detail how to set up and use both. ### Local setup -To get started with a local setup -Gno Realms can be typically written anywhere, under any structure, just like -regular Go code. However, Gno developers have adopted a standard of organizing -Gno logic under a specific directory hierarchy, which we -will explore here. - -Create the main working directory for our Realm: +To get started with a local setup, simply create a new empty folder. ```bash mkdir counter-app ``` -Since we are building a simple _Counter_ Realm, inside our created `counter-app` directory, we can create another -directory named `r`, which stands for `realm`: +Gno realms can be typically written anywhere, under any structure, just like +regular Go code. However, Gno developers have adopted a standard of organizing +Gno logic under a specific directory hierarchy, which we +will explore in this section. + +Since we are building a simple **Counter** realm, inside our created `counter-app` +directory, we can create another directory named `r`, which stands for `realm`: ```bash cd counter-app mkdir r ``` -Alternatively, if we were writing a [Gno Package](../concepts/packages.md), we would denote this directory name -as `p` (for `package`). You can learn more about Packages in our [Package development guide](simple-library.md). +Alternatively, if we were writing a [Gno package](../concepts/packages.md), we +would denote this directory name as `p` (for `package`). You can learn more about +Packages in our [Package development guide](simple-library.md). -Additionally, we will create another sub-folder that will house our Realm code, named `counter`: +Additionally, we will create another sub-folder that will house our realm code, named `counter`: ```bash cd r @@ -61,10 +61,8 @@ counter-app/ │ │ ├─ // source code here ``` -### 2. Create `counter.gno` - Now that the work directory structure is set up, we can go into the `counter` sub-folder, and actually create -our _Counter_ Smart Contract: +our **Counter** Smart Contract: ```bash cd counter @@ -72,24 +70,30 @@ touch counter.gno ``` :::info Gno file extension -All Gno (Gnolang) source code has the file extension `.gno`. +All Gno source code has the file extension `.gno`. This file extension is required for existing gno tools and processes to work. ::: +You're ready to write Gno code! Skip to ["Start writing code"](#2-start-writing-code) +to see how to start. ### Using the Gno Playground -When using the Gno Playground, writing, testing, deploying, and sharing Gno code -is simple. This makes it perfect for getting started with Gno. +For smaller apps and Gno code snippets, the Gno Playground can be used. It provides +a simple sandbox environment where developers can write Gno code. Vising the [Playground](https://play.gno.land) will greet you with a template file: ![Default](../assets/how-to-guides/simple-contract/playground_welcome.png) +Create a new file named `counter.gno`, and delete the default file. You are now +ready to write some Gno code! + ## 2. Start writing code -We can now write out the logic of the **Counter** Smart Contract in `package.gno`: +After setting up your environment, we can start defining the logic of our counter +app. Inside `counter.gno`: [embedmd]:# (../assets/how-to-guides/simple-contract/counter.gno go) ```go From cb7685b5093ac54053cf218bffc2e4d39ff2122d Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 2 Apr 2024 23:46:08 +0900 Subject: [PATCH 41/43] fixup package how-to --- docs/how-to-guides/simple-contract.md | 27 +++++++--- docs/how-to-guides/simple-library.md | 74 ++++++++++++++++++++++++++- 2 files changed, 93 insertions(+), 8 deletions(-) diff --git a/docs/how-to-guides/simple-contract.md b/docs/how-to-guides/simple-contract.md index 2840452049a..c9aac45971d 100644 --- a/docs/how-to-guides/simple-contract.md +++ b/docs/how-to-guides/simple-contract.md @@ -17,11 +17,26 @@ Our **Counter** realm will have the following functionality: - Incrementing / decrementing the count. - Fetching the current count value. -## 1. Environment +## 1. Development environment Currently, Gno apps can be developed locally or via the online editor, Gno Playground. Below we detail how to set up and use both. - + ### Local setup + +#### Prerequisites + +- **Text editor** + +:::info Editor support +The Gno language is based on Go, but it does not have all the bells and whistles in major text editors like Go. +Advanced language features like IntelliSense are still in the works. + +Currently, we officially have language support +for [ViM](https://github.com/gnolang/gno/blob/master/CONTRIBUTING.md#vim-support), +[Emacs](https://github.com/gnolang/gno/blob/master/CONTRIBUTING.md#emacs-support) +and [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=harry-hov.gno). +::: + To get started with a local setup, simply create a new empty folder. ```bash @@ -61,8 +76,8 @@ counter-app/ │ │ ├─ // source code here ``` -Now that the work directory structure is set up, we can go into the `counter` sub-folder, and actually create -our **Counter** Smart Contract: +Now that the work directory structure is set up, we can go into the `counter` +sub-folder, and actually create the file to store our **Counter** realm: ```bash cd counter @@ -81,9 +96,9 @@ to see how to start. ### Using the Gno Playground For smaller apps and Gno code snippets, the Gno Playground can be used. It provides -a simple sandbox environment where developers can write Gno code. +a simple sandbox environment where developers can write, test, and deploy Gno code. -Vising the [Playground](https://play.gno.land) will greet you with a template file: +Visiting the [Playground](https://play.gno.land) will greet you with a template file: ![Default](../assets/how-to-guides/simple-contract/playground_welcome.png) diff --git a/docs/how-to-guides/simple-library.md b/docs/how-to-guides/simple-library.md index 27432246237..968a985dc38 100644 --- a/docs/how-to-guides/simple-library.md +++ b/docs/how-to-guides/simple-library.md @@ -13,7 +13,72 @@ intricacies of Packages, please see the [Packages concept page](../concepts/pack The Package we will be writing today will be a simple library for suggesting a random tapas dish. We will define a set list of tapas, and define a method that randomly selects a dish from the list. -## 1. Using Gno Playground +## Development environment +Currently, Gno packages can be developed locally or via the online editor, Gno +Playground. Below we detail how to set up and use both. + +### Local setup + +#### Prerequisites + +- **Text editor** + +:::info Editor support +The Gno language is based on Go, but it does not have all the bells and whistles in major text editors like Go. +Advanced language features like IntelliSense are still in the works. + +Currently, we officially have language support +for [ViM](https://github.com/gnolang/gno/blob/master/CONTRIBUTING.md#vim-support), +[Emacs](https://github.com/gnolang/gno/blob/master/CONTRIBUTING.md#emacs-support) +and [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=harry-hov.gno). +::: + +We discussed Gno folder structures more in detail in +the [simple Smart Contract guide](simple-contract.md#1-setting-up-the-work-directory). +For now, we will just follow some rules outlined there. + +Create the main working directory for our Package: + +```bash +mkdir tapas-lib +``` + +Since we are building a simple tapas Package, inside our created `tapas-lib` directory, we can create another +directory named `p`, which stands for `package`: + +```bash +cd tapas-lib +mkdir p +``` + +Additionally, we will create another subdirectory that will house our Package code, named `tapas`: + +```bash +cd p +mkdir tapas +``` + +After setting up our work directory structure, we should have something like this: + +```text +tapas-lib/ +├─ p/ +│ ├─ tapas/ +│ │ ├─ // source code here +``` + +Now that the work directory structure is set up, we can go into the `tapas` sub-folder, and actually create +our tapas suggestion library logic: + +```bash +cd tapas +touch tapas.gno +``` + +You're ready to write Gno code! Skip to ["Start writing code"](#2-start-writing-code) +to see how to start. + +### Using Gno Playground When using the Gno Playground, writing, testing, deploying, and sharing Gno code is simple. This makes it perfect for getting started with Gno. @@ -22,9 +87,14 @@ Vising the [Playground](https://play.gno.land) will greet you with a template fi ![Default](../assets/how-to-guides/simple-library/playground_welcome.png) +Create a new file named `tapas.gno`, and delete the default file. You are now +ready to write some Gno code! + + ## 2. Start writing code -Inside `package.gno`, we will define our library logic: +After setting up your environment, we can start defining our library logic. +Inside `tapas.gno`: [embedmd]:# (../assets/how-to-guides/simple-library/tapas.gno go) ```go From 8d297a5c35d78bf96e649c4de3d8ab7bdec9130c Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 3 Apr 2024 01:01:19 +0900 Subject: [PATCH 42/43] revert how-to-test --- docs/how-to-guides/testing-gno.md | 101 ++++++++++++++++++------------ 1 file changed, 61 insertions(+), 40 deletions(-) diff --git a/docs/how-to-guides/testing-gno.md b/docs/how-to-guides/testing-gno.md index dc84ae6bcc9..d517a8c9e24 100644 --- a/docs/how-to-guides/testing-gno.md +++ b/docs/how-to-guides/testing-gno.md @@ -6,18 +6,24 @@ id: testing-gno ## Overview -In this guide, we will explore the available tooling in testing out the Gno -Realms and Packages we write. We will go over different CLI tools available to -developers, gno testing libraries as well as testing techniques that involve -data mocking. +In this guide, we will explore the available tooling in testing out the Gno Realms and Packages we write. +We will go over different CLI tools available to developers, gno testing libraries as well as +testing techniques that involve data mocking. + +## Prerequisites + +- **`gno` set up. Reference the [Installation](../getting-started/local-setup/local-setup.md#3-installing-other-gno-tools) guide + for steps** ## Example Realm -For the purpose of this guide, we will be testing the simple **Counter** Realm created in +For the purpose of this guide, we will be testing the simple *Counter* Realm created in the [How to write a simple Gno Smart Contract (Realm)](simple-contract.md) guide. [embedmd]:# (../assets/how-to-guides/testing-gno/counter-1.gno go) ```go +// counter-app/r/counter/counter.gno + package counter import ( @@ -39,29 +45,38 @@ func Render(_ string) string { } ``` -Visit [this Playground link](https://play.gno.land/p/XbkFKAIpLO8) to get started. - -## 1. Testing in Gno +## 1. Writing the Gno test -Gno tests are written in the same manner and format as regular Go tests, just in -`_test.gno` files. +Gno tests are written in the same manner and format as regular Go tests, just in `_test.gno` files. -We can get started by adding a new file in the Playground, called `package_test.gno`: +We can place the Gno tests for the `Counter` Realm in the same directory as `counter.gno`: -![Test](../assets/how-to-guides/testing-gno/package_test.png) +```text +counter-app/ +├─ r/ +│ ├─ counter/ +│ │ ├─ counter.gno +│ │ ├─ counter_test.gno <--- the test source code +``` +```bash +cd counter +touch counter_test.gno +``` -What should be tested in this **Counter** Realm example? +What should be tested in this _Counter_ Realm example? Mainly, we want to verify that: -- `Increment()` increments the value. -- `Decrement()` decrements the value. -- `Render()` returns a valid formatted value. +- Increment increments the value. +- Decrement decrements the value. +- Render returns a valid formatted value. -Let's write the required unit tests in `package_test.gno`: +Let's write the required unit tests: [embedmd]:# (../assets/how-to-guides/testing-gno/counter-2.gno go) ```go +// counter-app/r/counter/counter_test.gno + package counter import "testing" @@ -115,23 +130,32 @@ func TestCounter_Render(t *testing.T) { :::warning Testing package-level variables -In practice, it is not advisable to test and validate package level variables -like this, as their value is mutated between test runs. For the sake of keeping -this guide simple, we went ahead and reset the variable value for each test, -however, you should employ more robust test strategies. +In practice, it is not advisable to test and validate package level variables like this, as their value is mutated +between test runs. For the sake of keeping this guide simple, we went ahead and reset the variable value for each test, +however, +you should employ more robust test strategies. ::: -You can view the code on [this Playground link](https://play.gno.land/p/A74fKPLQgQi). +## 2. Running the Gno test + +To run the prepared Gno tests, we can utilize the `gno test` CLI tool. + +Simply point it to the location containing our testing source code, and the tests will execute. +For example, we can run the following command from the `counter-app/r/counter` directory: + +```bash +gno test -v . +``` -## 2. Running the test +Let's look into the different parts of this command: -To run the prepared Gno tests, you can use the built-in testing functionality in -the Playground. +- `-v` enables the verbose output. +- `-root-dir` specifies the root directory to our cloned `gno` GitHub repository +- `.` specifies the location containing our test files. Since we are already located in that directory, we specify + a `.`. -By simply click "Test" in the top bar, the Playground will look for `_test.gno` -files and execute them. If all went well, you will receive the following output -in a terminal: +Running the test command should produce a successful output: ```bash === RUN TestCounter_Increment @@ -140,27 +164,24 @@ in a terminal: --- PASS: TestCounter_Decrement (0.00s) === RUN TestCounter_Render --- PASS: TestCounter_Render (0.00s) -ok /src 3.60s +ok ./. 1.00s ``` ## Additional test support -As we grow more familiar with Gno development, our Realm / Package logic can -become more complex. As such, we need more robust testing support in the form of -mocking values ahead of time that would normally be only available on a +As we grow more familiar with Gno development, our Realm / Package logic can become more complex. As such, we need +more robust testing support in the form of mocking values ahead of time that would normally be only available on a live (deployed) Realm / Package. -Luckily, the Gno standard library provides ample support for functionality such -as setting predefined values ahead of time, such as the caller address, block -height, etc. +Luckily, the Gno standard library provides ample support for functionality such as setting predefined values ahead of +time, such as the request caller address, or the calling package address. -You can learn more about these methods, which are importable using the `std` -import declaration, in the [standard library](../reference/standard-library/std/testing.md) -testing reference section. +You can learn more about these methods, that are importable using the `std` import declaration, +in the [Standard Library](../concepts/standard-library/overview.md) reference section. ## Conclusion That's it 🎉 -You have successfully written and tested Gno code. Additionally, you have -utilized the built-in Gno Playground testing functionality. +You have successfully written and tested Gno code. Additionally, you have utilized the `gno test` tool, and understood +how it can be configured to make the developer experience smooth. \ No newline at end of file From f872868a40de60994f8fa9ce18627cc0fa41df6b Mon Sep 17 00:00:00 2001 From: leohhhn Date: Fri, 5 Apr 2024 16:01:02 +0900 Subject: [PATCH 43/43] fix typo --- docs/how-to-guides/simple-library.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/how-to-guides/simple-library.md b/docs/how-to-guides/simple-library.md index 968a985dc38..2846117df45 100644 --- a/docs/how-to-guides/simple-library.md +++ b/docs/how-to-guides/simple-library.md @@ -83,7 +83,7 @@ to see how to start. When using the Gno Playground, writing, testing, deploying, and sharing Gno code is simple. This makes it perfect for getting started with Gno. -Vising the [Playground](https://play.gno.land) will greet you with a template file: +Visiting the [Playground](https://play.gno.land) will greet you with a template file: ![Default](../assets/how-to-guides/simple-library/playground_welcome.png)