Skip to content

Commit

Permalink
feat: add Web3 Kamp 2024 Workshop (#58)
Browse files Browse the repository at this point in the history
  • Loading branch information
leohhhn authored Aug 5, 2024
1 parent c199cc8 commit a4b220b
Show file tree
Hide file tree
Showing 19 changed files with 486 additions and 0 deletions.
15 changes: 15 additions & 0 deletions presentations/2024-08-05--web3kamp--leon/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Web3 Kamp 2024 - gno.land workshop

Welcome to the Web3 Kamp 2024 gno.land workshop!

This workshop is meant for developers that have little to no knowledge of Go/Gno,
but have at least a beginner amount of knowledge in other blockchain systems
(such as Ethereum).

In this workshop, you will learn the basics of programming in gno.land, and build a
simple Twitter clone using the Gno language.

This workshop contains phases; each phase is meant to be a stepping stone in the
development process to the next one. Check out the [phases](./phases) folder to start.

Check out the slides for the workshop [here](https://docs.google.com/presentation/d/1tnplCWxhg-RFatDS3w1iJnO0vSfBAuw2ZA0ommNJQOU/edit?usp=sharing).
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module gno.land/r/petnica/twitter
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package twitter

import (
"time"

"gno.land/p/demo/ufmt"
)

func (p Post) String() string {
return ufmt.Sprintf("Text: %s\n\nAuthor: %s\n\n Time: %s\n\nUp: %d, Down: %d\n\nTotal tips: %dugnot", p.text, p.author.String(), p.createdAt.Format(time.RFC822), p.upvotes, p.downvotes, p.tipTotal)
}

func Render(_ string) string {
if len(posts) == 0 {
return "No posts currently."
}

output := ""
for _, post := range posts {
output += ufmt.Sprintf("### Post #%d\n\n%s\n\n", post.id, post.String())
}

return output
}
108 changes: 108 additions & 0 deletions presentations/2024-08-05--web3kamp--leon/phases/finished/twitter.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package twitter

import (
"std"
"time"
)

type Post struct {
id uint

text string
author std.Address
createdAt time.Time
upvotes uint
downvotes uint

tipTotal int64
}

var (
posts []*Post
idCounter uint
)

func AddPost(text string) uint {
if text == "" {
panic("post text cannot be empty")
}

p := &Post{
text: text,
id: idCounter,
author: std.PrevRealm().Addr(),
createdAt: time.Now(),
upvotes: 0,
downvotes: 0,
tipTotal: 0,
}

posts = append(posts, p)
idCounter++

return p.id
}

func RemovePost(id uint) {
caller := std.PrevRealm().Addr()

idx, p := getPost(id)
if p == nil {
panic("could not find post with specified id")
}

if p.author != caller {
panic("you are not the author of this post!")
}

posts = append(posts[:idx], posts[idx+1:]...)
}

func Upvote(id uint) {
_, p := getPost(id)
if p == nil {
panic("could not find post with specified id")
}

p.upvotes++
}

func Downvote(id uint) {
_, p := getPost(id)
if p == nil {
panic("could not find post with specified id")
}

p.downvotes++
}

func TipPost(id uint) {
tipAmt := std.GetOrigSend().AmountOf("ugnot")
if tipAmt <= 0 {
panic("cannot tip 0 or less!")
}

_, p := getPost(id)
if p == nil {
panic("could not find post with specified id")
}

banker := std.GetBanker(std.BankerTypeOrigSend)

coinTip := std.NewCoin("ugnot", tipAmt)
banker.SendCoins(std.CurrentRealm().Addr(), p.author, std.NewCoins(coinTip))

p.tipTotal += tipAmt
}

func getPost(id uint) (int, *Post) {
for i, p := range posts {
if p.id == id {
return i, p
}
}

return -1, nil
}


45 changes: 45 additions & 0 deletions presentations/2024-08-05--web3kamp--leon/phases/p1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Web3 Kamp - P1 - Local Installation

Follow the [installation steps](https://docs.gno.land/getting-started/local-setup/installation)
to install the tools necessary to complete the workshop. First verify the prerequisite requirements
are installed before installing `gno`, `gnodev`, and `gnokey`.

## Syntax highlighting

This step is optional but convenient. We have a few [supported editor extensions](https://github.com/gnolang/gno/blob/master/CONTRIBUTING.md#environment)
that enable gno syntax highlighting, including VSCode, ViM, emacs, and Sublime.

## gnokey - the CLI wallet

Let's generate a key pair. A key pair is what is used to sign transactions that are broadcast
to the gno.land blockchain. In a real world context, they should not be shared.

### add

Add a new key by running `gnokey add <keyname>`. Choose whichever key name you'd like. Shorter is better
since you'll have to type it at least a few times. A passphrase is optional and in our case unnecessary, so
you can enter through this without typing anything.

Notice the mnemonic phrase that is generated. In a real world scenario, you would want to record this
and store it offline, ideally on a piece of paper or other method of offline storage for security.

### list

Run `gnokey list`. You should see that a key has been added with the specified name.

## gnodev

`gnodev` is a tool to more easily facilitate development on gno.land. It's basic features include:

- spinning up an in-memory node
- automatically deploying local packages to the chain
- reloading packages when file changes are made
- starting a web server using `gnoweb` to provide a UI

From this directory, try running `gnodev .`. If successful, the last line should be `` --- READY ┃ I for commands and help, press `h` ``.

## Setup complete!

You've just set up a local gno.land development environment 🎉

To see the Hello World example in action, visit [localhost:8888/r/petnica/hello](http://localhost:8888/r/petnica/hello).
1 change: 1 addition & 0 deletions presentations/2024-08-05--web3kamp--leon/phases/p1/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module gno.land/r/petnica/hello
15 changes: 15 additions & 0 deletions presentations/2024-08-05--web3kamp--leon/phases/p1/hello.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package hello

var msg string

func init() {
msg = "Hello World!"
}

func UpdateMsg(newMsg string) {
msg = newMsg
}

func Render(_ string) string {
return msg
}
7 changes: 7 additions & 0 deletions presentations/2024-08-05--web3kamp--leon/phases/p2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Web3 Kamp - P2 - Adding & RemovingPosts

1. Run `gnodev` in this folder with `gnodev .`.
2. Check out the code comments in `twitter.gno` for TODO steps!
3. View the app on [localhost:8888/r/petnica/twitter](http://localhost:8888/r/petnica/twitter).
4. Check out the docs at [docs.gno.land](https://docs.gno.land)!
5. When you finish, try posting and removing a post!
1 change: 1 addition & 0 deletions presentations/2024-08-05--web3kamp--leon/phases/p2/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module gno.land/r/petnica/twitter
24 changes: 24 additions & 0 deletions presentations/2024-08-05--web3kamp--leon/phases/p2/render.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package twitter

import (
"time"

"gno.land/p/demo/ufmt"
)

func (p Post) String() string {
return ufmt.Sprintf("Text: %s\n\nAuthor: %s\n\n Time: %s", p.text, p.author.String(), p.createdAt.Format(time.RFC822))
}

func Render(_ string) string {
if len(posts) == 0 {
return "No posts currently."
}

output := ""
for _, post := range posts {
output += ufmt.Sprintf("### Post #%d\n\n%s\n\n", post.id, post.String())
}

return output
}
27 changes: 27 additions & 0 deletions presentations/2024-08-05--web3kamp--leon/phases/p2/twitter.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package twitter

import (
"std"
"time"
)

var (
posts []*Post
idCounter uint
)

type Post struct {
id uint

text string
author std.Address
createdAt time.Time
}

// TODO: Create an exported function to add a new post
// The function should add a pointer to a newly created post to the posts slice
// Use the idCounter variable to assign an ID to a new post

// TODO: Create an exported function to remove a post by ID
// Only the author of the post should be able to remove it
// panic if the caller is not the author!
6 changes: 6 additions & 0 deletions presentations/2024-08-05--web3kamp--leon/phases/p3/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Web3 Kamp - P3 -Upvotes & Downvotes

1. Run `gnodev` in this folder with `gnodev .`.
2. Check out the code comments in all files for TODO steps.
3. When you finish, try posting, upvoting, and downvoting a post!
4. BONUS: How can we prevent double voting?
1 change: 1 addition & 0 deletions presentations/2024-08-05--web3kamp--leon/phases/p3/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module gno.land/r/petnica/twitter
25 changes: 25 additions & 0 deletions presentations/2024-08-05--web3kamp--leon/phases/p3/render.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package twitter

import (
"time"

"gno.land/p/demo/ufmt"
)

// TODO: Update this function to show upvotes and downvotes
func (p Post) String() string {
return ufmt.Sprintf("Text: %s\n\nAuthor: %s\n\n Time: %s", p.text, p.author.String(), p.createdAt.Format(time.RFC822))
}

func Render(_ string) string {
if len(posts) == 0 {
return "No posts currently."
}

output := ""
for _, post := range posts {
output += ufmt.Sprintf("### Post #%d\n\n%s\n\n", post.id, post.String())
}

return output
}
66 changes: 66 additions & 0 deletions presentations/2024-08-05--web3kamp--leon/phases/p3/twitter.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package twitter

import (
"std"
"time"
)

var (
posts []*Post
idCounter uint
)

type Post struct {
id uint

text string
author std.Address
createdAt time.Time
// TODO: Add upvotes & downvotes
}

func AddPost(text string) uint {
if text == "" {
panic("post text cannot be empty")
}

p := &Post{
text: text,
id: idCounter,
author: std.PrevRealm().Addr(),
createdAt: time.Now(),
}

posts = append(posts, p)
idCounter++

return p.id
}

func RemovePost(id uint) {
caller := std.PrevRealm().Addr()

idx, p := getPost(id)
if p == nil {
panic("could not find post with specified id")
}

if p.author != caller {
panic("you are not the author of this post!")
}

posts = append(posts[:idx], posts[idx+1:]...)
}

// TODO: Add an exported Upvote function
// TODO: Add an exported Downvote function

func getPost(id uint) (int, *Post) {
for i, p := range posts {
if p.id == id {
return i, p
}
}

return -1, nil
}
6 changes: 6 additions & 0 deletions presentations/2024-08-05--web3kamp--leon/phases/p4/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Web3 Kamp - P4 - Tipping a post with `ugnot`

1. Run `gnodev` in this folder with `gnodev .`.
2. Check out the code comments in all files for TODO steps.
3. Hint: check out the Banker, Coins, and `GetOrigSend` in the gno.land documentation.
4. When you finish, try posting and tipping a post!
1 change: 1 addition & 0 deletions presentations/2024-08-05--web3kamp--leon/phases/p4/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module gno.land/r/petnica/twitter
Loading

0 comments on commit a4b220b

Please sign in to comment.