Skip to content

Commit a4b220b

Browse files
authored
feat: add Web3 Kamp 2024 Workshop (#58)
1 parent c199cc8 commit a4b220b

File tree

19 files changed

+486
-0
lines changed

19 files changed

+486
-0
lines changed

Diff for: presentations/2024-08-05--web3kamp--leon/README.md

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Web3 Kamp 2024 - gno.land workshop
2+
3+
Welcome to the Web3 Kamp 2024 gno.land workshop!
4+
5+
This workshop is meant for developers that have little to no knowledge of Go/Gno,
6+
but have at least a beginner amount of knowledge in other blockchain systems
7+
(such as Ethereum).
8+
9+
In this workshop, you will learn the basics of programming in gno.land, and build a
10+
simple Twitter clone using the Gno language.
11+
12+
This workshop contains phases; each phase is meant to be a stepping stone in the
13+
development process to the next one. Check out the [phases](./phases) folder to start.
14+
15+
Check out the slides for the workshop [here](https://docs.google.com/presentation/d/1tnplCWxhg-RFatDS3w1iJnO0vSfBAuw2ZA0ommNJQOU/edit?usp=sharing).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module gno.land/r/petnica/twitter
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package twitter
2+
3+
import (
4+
"time"
5+
6+
"gno.land/p/demo/ufmt"
7+
)
8+
9+
func (p Post) String() string {
10+
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)
11+
}
12+
13+
func Render(_ string) string {
14+
if len(posts) == 0 {
15+
return "No posts currently."
16+
}
17+
18+
output := ""
19+
for _, post := range posts {
20+
output += ufmt.Sprintf("### Post #%d\n\n%s\n\n", post.id, post.String())
21+
}
22+
23+
return output
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package twitter
2+
3+
import (
4+
"std"
5+
"time"
6+
)
7+
8+
type Post struct {
9+
id uint
10+
11+
text string
12+
author std.Address
13+
createdAt time.Time
14+
upvotes uint
15+
downvotes uint
16+
17+
tipTotal int64
18+
}
19+
20+
var (
21+
posts []*Post
22+
idCounter uint
23+
)
24+
25+
func AddPost(text string) uint {
26+
if text == "" {
27+
panic("post text cannot be empty")
28+
}
29+
30+
p := &Post{
31+
text: text,
32+
id: idCounter,
33+
author: std.PrevRealm().Addr(),
34+
createdAt: time.Now(),
35+
upvotes: 0,
36+
downvotes: 0,
37+
tipTotal: 0,
38+
}
39+
40+
posts = append(posts, p)
41+
idCounter++
42+
43+
return p.id
44+
}
45+
46+
func RemovePost(id uint) {
47+
caller := std.PrevRealm().Addr()
48+
49+
idx, p := getPost(id)
50+
if p == nil {
51+
panic("could not find post with specified id")
52+
}
53+
54+
if p.author != caller {
55+
panic("you are not the author of this post!")
56+
}
57+
58+
posts = append(posts[:idx], posts[idx+1:]...)
59+
}
60+
61+
func Upvote(id uint) {
62+
_, p := getPost(id)
63+
if p == nil {
64+
panic("could not find post with specified id")
65+
}
66+
67+
p.upvotes++
68+
}
69+
70+
func Downvote(id uint) {
71+
_, p := getPost(id)
72+
if p == nil {
73+
panic("could not find post with specified id")
74+
}
75+
76+
p.downvotes++
77+
}
78+
79+
func TipPost(id uint) {
80+
tipAmt := std.GetOrigSend().AmountOf("ugnot")
81+
if tipAmt <= 0 {
82+
panic("cannot tip 0 or less!")
83+
}
84+
85+
_, p := getPost(id)
86+
if p == nil {
87+
panic("could not find post with specified id")
88+
}
89+
90+
banker := std.GetBanker(std.BankerTypeOrigSend)
91+
92+
coinTip := std.NewCoin("ugnot", tipAmt)
93+
banker.SendCoins(std.CurrentRealm().Addr(), p.author, std.NewCoins(coinTip))
94+
95+
p.tipTotal += tipAmt
96+
}
97+
98+
func getPost(id uint) (int, *Post) {
99+
for i, p := range posts {
100+
if p.id == id {
101+
return i, p
102+
}
103+
}
104+
105+
return -1, nil
106+
}
107+
108+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Web3 Kamp - P1 - Local Installation
2+
3+
Follow the [installation steps](https://docs.gno.land/getting-started/local-setup/installation)
4+
to install the tools necessary to complete the workshop. First verify the prerequisite requirements
5+
are installed before installing `gno`, `gnodev`, and `gnokey`.
6+
7+
## Syntax highlighting
8+
9+
This step is optional but convenient. We have a few [supported editor extensions](https://github.com/gnolang/gno/blob/master/CONTRIBUTING.md#environment)
10+
that enable gno syntax highlighting, including VSCode, ViM, emacs, and Sublime.
11+
12+
## gnokey - the CLI wallet
13+
14+
Let's generate a key pair. A key pair is what is used to sign transactions that are broadcast
15+
to the gno.land blockchain. In a real world context, they should not be shared.
16+
17+
### add
18+
19+
Add a new key by running `gnokey add <keyname>`. Choose whichever key name you'd like. Shorter is better
20+
since you'll have to type it at least a few times. A passphrase is optional and in our case unnecessary, so
21+
you can enter through this without typing anything.
22+
23+
Notice the mnemonic phrase that is generated. In a real world scenario, you would want to record this
24+
and store it offline, ideally on a piece of paper or other method of offline storage for security.
25+
26+
### list
27+
28+
Run `gnokey list`. You should see that a key has been added with the specified name.
29+
30+
## gnodev
31+
32+
`gnodev` is a tool to more easily facilitate development on gno.land. It's basic features include:
33+
34+
- spinning up an in-memory node
35+
- automatically deploying local packages to the chain
36+
- reloading packages when file changes are made
37+
- starting a web server using `gnoweb` to provide a UI
38+
39+
From this directory, try running `gnodev .`. If successful, the last line should be `` --- READY ┃ I for commands and help, press `h` ``.
40+
41+
## Setup complete!
42+
43+
You've just set up a local gno.land development environment 🎉
44+
45+
To see the Hello World example in action, visit [localhost:8888/r/petnica/hello](http://localhost:8888/r/petnica/hello).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module gno.land/r/petnica/hello
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package hello
2+
3+
var msg string
4+
5+
func init() {
6+
msg = "Hello World!"
7+
}
8+
9+
func UpdateMsg(newMsg string) {
10+
msg = newMsg
11+
}
12+
13+
func Render(_ string) string {
14+
return msg
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Web3 Kamp - P2 - Adding & RemovingPosts
2+
3+
1. Run `gnodev` in this folder with `gnodev .`.
4+
2. Check out the code comments in `twitter.gno` for TODO steps!
5+
3. View the app on [localhost:8888/r/petnica/twitter](http://localhost:8888/r/petnica/twitter).
6+
4. Check out the docs at [docs.gno.land](https://docs.gno.land)!
7+
5. When you finish, try posting and removing a post!
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module gno.land/r/petnica/twitter
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package twitter
2+
3+
import (
4+
"time"
5+
6+
"gno.land/p/demo/ufmt"
7+
)
8+
9+
func (p Post) String() string {
10+
return ufmt.Sprintf("Text: %s\n\nAuthor: %s\n\n Time: %s", p.text, p.author.String(), p.createdAt.Format(time.RFC822))
11+
}
12+
13+
func Render(_ string) string {
14+
if len(posts) == 0 {
15+
return "No posts currently."
16+
}
17+
18+
output := ""
19+
for _, post := range posts {
20+
output += ufmt.Sprintf("### Post #%d\n\n%s\n\n", post.id, post.String())
21+
}
22+
23+
return output
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package twitter
2+
3+
import (
4+
"std"
5+
"time"
6+
)
7+
8+
var (
9+
posts []*Post
10+
idCounter uint
11+
)
12+
13+
type Post struct {
14+
id uint
15+
16+
text string
17+
author std.Address
18+
createdAt time.Time
19+
}
20+
21+
// TODO: Create an exported function to add a new post
22+
// The function should add a pointer to a newly created post to the posts slice
23+
// Use the idCounter variable to assign an ID to a new post
24+
25+
// TODO: Create an exported function to remove a post by ID
26+
// Only the author of the post should be able to remove it
27+
// panic if the caller is not the author!
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Web3 Kamp - P3 -Upvotes & Downvotes
2+
3+
1. Run `gnodev` in this folder with `gnodev .`.
4+
2. Check out the code comments in all files for TODO steps.
5+
3. When you finish, try posting, upvoting, and downvoting a post!
6+
4. BONUS: How can we prevent double voting?
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module gno.land/r/petnica/twitter
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package twitter
2+
3+
import (
4+
"time"
5+
6+
"gno.land/p/demo/ufmt"
7+
)
8+
9+
// TODO: Update this function to show upvotes and downvotes
10+
func (p Post) String() string {
11+
return ufmt.Sprintf("Text: %s\n\nAuthor: %s\n\n Time: %s", p.text, p.author.String(), p.createdAt.Format(time.RFC822))
12+
}
13+
14+
func Render(_ string) string {
15+
if len(posts) == 0 {
16+
return "No posts currently."
17+
}
18+
19+
output := ""
20+
for _, post := range posts {
21+
output += ufmt.Sprintf("### Post #%d\n\n%s\n\n", post.id, post.String())
22+
}
23+
24+
return output
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package twitter
2+
3+
import (
4+
"std"
5+
"time"
6+
)
7+
8+
var (
9+
posts []*Post
10+
idCounter uint
11+
)
12+
13+
type Post struct {
14+
id uint
15+
16+
text string
17+
author std.Address
18+
createdAt time.Time
19+
// TODO: Add upvotes & downvotes
20+
}
21+
22+
func AddPost(text string) uint {
23+
if text == "" {
24+
panic("post text cannot be empty")
25+
}
26+
27+
p := &Post{
28+
text: text,
29+
id: idCounter,
30+
author: std.PrevRealm().Addr(),
31+
createdAt: time.Now(),
32+
}
33+
34+
posts = append(posts, p)
35+
idCounter++
36+
37+
return p.id
38+
}
39+
40+
func RemovePost(id uint) {
41+
caller := std.PrevRealm().Addr()
42+
43+
idx, p := getPost(id)
44+
if p == nil {
45+
panic("could not find post with specified id")
46+
}
47+
48+
if p.author != caller {
49+
panic("you are not the author of this post!")
50+
}
51+
52+
posts = append(posts[:idx], posts[idx+1:]...)
53+
}
54+
55+
// TODO: Add an exported Upvote function
56+
// TODO: Add an exported Downvote function
57+
58+
func getPost(id uint) (int, *Post) {
59+
for i, p := range posts {
60+
if p.id == id {
61+
return i, p
62+
}
63+
}
64+
65+
return -1, nil
66+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Web3 Kamp - P4 - Tipping a post with `ugnot`
2+
3+
1. Run `gnodev` in this folder with `gnodev .`.
4+
2. Check out the code comments in all files for TODO steps.
5+
3. Hint: check out the Banker, Coins, and `GetOrigSend` in the gno.land documentation.
6+
4. When you finish, try posting and tipping a post!
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module gno.land/r/petnica/twitter

0 commit comments

Comments
 (0)